Skip to main content

rucc_types/
convert.rs

1//! The integer promotions and the usual arithmetic conversions.
2//!
3//! Design: `spec/07-types-and-semantics.md` section 7.2.
4//!
5//! These are 6.3.1.1 and 6.3.1.8, the two rules that decide what type an arithmetic expression
6//! has, and they are where a compiler quietly goes wrong in ways that only show up as an
7//! overflow or a sign extension in generated code. So the answers here were read out of gcc
8//! 13.3 and clang 18 rather than out of the standard, with `_Generic` naming the type of every
9//! interesting pair, and the standard was used to explain what was measured.
10//!
11//! C23 changed three things and they are all here. `bool` is a real type and promotes to `int`.
12//! An enumeration may have a fixed underlying type, and then it promotes through that rather
13//! than through a type the implementation picked. And `_BitInt` does not promote at all, so
14//! `_BitInt(8) + _BitInt(8)` is `_BitInt(8)` where `char + char` is `int`, which is the point
15//! of the type: it is the one integer type in C that does what it says.
16
17use rucc_target::TargetInfo;
18
19use crate::kind::{FloatKind, IntKind, TypeKind};
20use crate::layout::{float_format, int_width};
21use crate::types::{TypeId, Types};
22
23/// The integer promotions, 6.3.1.1.
24///
25/// Anything narrower than `int` becomes `int`, or `unsigned int` when `int` cannot hold every
26/// value it had. Everything else, floating types and pointers included, is its own answer, so
27/// this can be called on any operand without asking what it is first.
28///
29/// The qualifiers and `_Atomic` come off, because by the time a value is being promoted the
30/// lvalue conversion of 6.3.2.1 has already happened and neither of them is part of the value.
31///
32/// `_BitInt` is deliberately not promoted. That is C23 6.3.1.1p2, and it is what both compilers
33/// do: `+x` on a `_BitInt(8)` is still a `_BitInt(8)`.
34pub fn promote(types: &mut Types, id: TypeId, target: &TargetInfo) -> TypeId {
35    let id = value_type(types, id);
36    match types.kind(id) {
37        TypeKind::Bool => types.int(IntKind::Int),
38        TypeKind::Int(kind) => promoted_int(types, kind, int_width(kind, target), target),
39        TypeKind::Enum(id) => {
40            // An enumeration promotes through whatever it is represented in. Until that has
41            // been decided the declaration is incomplete, which is a diagnostic somewhere with
42            // a span; `int` keeps the rest of the expression checkable in the meantime.
43            let underlying = types.enum_info(id).underlying;
44            match underlying {
45                Some(underlying) => promote(types, underlying, target),
46                None => types.int(IntKind::Int),
47            }
48        }
49        _ => id,
50    }
51}
52
53/// The integer promotions applied to a bit-field of the given width.
54///
55/// A bit-field is narrower than the type it was declared with, and it is the width rather than
56/// the type that decides. `unsigned b:3` promotes to `int`, because every three bit value fits
57/// in one, and `unsigned b:32` promotes to `unsigned int`, because they no longer do.
58///
59/// A bit-field wider than an `int` keeps the type it was declared with. The C17 wording says
60/// `unsigned int` there, which would turn a forty bit field into a thirty two bit value; both
61/// compilers answer `unsigned long long` instead, and C23 says so.
62pub fn promote_bit_field(types: &mut Types, id: TypeId, width: u32, target: &TargetInfo) -> TypeId {
63    let id = value_type(types, id);
64    let signed = match types.kind(id) {
65        TypeKind::Bool => false,
66        TypeKind::Int(kind) => kind.is_signed(target.char_is_signed),
67        TypeKind::BitInt { signed, .. } => signed,
68        // An enumeration bit-field promotes through what it is represented in, and anything
69        // else is not something a bit-field may be declared with.
70        _ => return promote(types, id, target),
71    };
72    let int = int_width(IntKind::Int, target);
73    if width < int || (signed && width == int) {
74        return types.int(IntKind::Int);
75    }
76    if !signed && width == int {
77        return types.int(IntKind::UInt);
78    }
79    promote(types, id, target)
80}
81
82/// The usual arithmetic conversions, 6.3.1.8: the one type both operands are converted to.
83///
84/// [`None`] when either operand is not an arithmetic type, which is not a failure of this
85/// function but the shape of a diagnostic its caller is about to write.
86///
87/// The floating rules come first and the integer rules only run when neither side is floating,
88/// which is why `unsigned long long + float` is `float` and loses precision rather than being
89/// the other way round.
90pub fn usual_arithmetic(
91    types: &mut Types,
92    left: TypeId,
93    right: TypeId,
94    target: &TargetInfo,
95) -> Option<TypeId> {
96    let left = value_type(types, left);
97    let right = value_type(types, right);
98    if let Some(common) = floating(types, left, right, target) {
99        return Some(common);
100    }
101    let left = promote(types, left, target);
102    let right = promote(types, right, target);
103    if left == right {
104        // Not only a shortcut: it is also the answer for the types this function does not model
105        // as integers, which is every one of them once the two sides agree.
106        return integer_shape(types, left, target).map(|_| left);
107    }
108    let left = integer_shape(types, left, target)?;
109    let right = integer_shape(types, right, target)?;
110    Some(common_integer(types, left, right, target))
111}
112
113/// The type of a value of type `id`, which is `id` without the parts an lvalue conversion
114/// removes.
115fn value_type(types: &mut Types, id: TypeId) -> TypeId {
116    let id = types.canonical(id);
117    let id = match types.kind(id) {
118        TypeKind::Atomic(inner) => types.canonical(inner),
119        _ => id,
120    };
121    types.unqualified(id)
122}
123
124/// The promotion of a standard integer type of the given width.
125fn promoted_int(types: &mut Types, kind: IntKind, width: u32, target: &TargetInfo) -> TypeId {
126    if kind.rank() >= IntKind::Int.rank() {
127        return types.int(kind);
128    }
129    let int = int_width(IntKind::Int, target);
130    let signed = kind.is_signed(target.char_is_signed);
131    if width < int || (signed && width == int) {
132        return types.int(IntKind::Int);
133    }
134    types.int(IntKind::UInt)
135}
136
137/// The common type when either side is a floating type, and [`None`] when neither is.
138///
139/// The real type is the one with the higher rank, or the floating one when the other side is an
140/// integer, and the result is complex when either operand was. That last part is why
141/// `_Complex float + double` is `_Complex double`: the real types combine first and the
142/// complexity is carried across afterwards.
143fn floating(types: &mut Types, left: TypeId, right: TypeId, target: &TargetInfo) -> Option<TypeId> {
144    let left = float_part(types, left);
145    let right = float_part(types, right);
146    let (kind, complex) = match (left, right) {
147        (None, None) => return None,
148        (Some((kind, complex)), None) | (None, Some((kind, complex))) => (kind, complex),
149        (Some((a, a_complex)), Some((b, b_complex))) => {
150            let kind = if float_rank(a, target) >= float_rank(b, target) { a } else { b };
151            (kind, a_complex || b_complex)
152        }
153    };
154    Some(if complex { types.complex(kind) } else { types.float(kind) })
155}
156
157/// The conversion rank of a real floating type, as something two of which can be compared.
158///
159/// There is no ordering on the kinds themselves to use here, because since C23 the answer
160/// depends on the target: `long double` outranks `_Float64x` on x86-64, where both of them are
161/// the x87 format, and loses to it on Apple, where `long double` is a `double`. So the question
162/// is asked of the format instead. Precision first and then range, which never disagree among
163/// the binary formats, and [`FloatKind::tie_break`] settles two types that are the same format,
164/// which is what makes `double + _Float64` a `_Float64`.
165fn float_rank(kind: FloatKind, target: &TargetInfo) -> (u32, i32, u8) {
166    let format = float_format(kind, target);
167    (format.precision(), format.max_exponent(), kind.tie_break())
168}
169
170/// The real floating type inside `id`, and whether it was complex.
171fn float_part(types: &Types, id: TypeId) -> Option<(FloatKind, bool)> {
172    match types.kind(id) {
173        TypeKind::Float(kind) => Some((kind, false)),
174        TypeKind::Complex(kind) => Some((kind, true)),
175        _ => None,
176    }
177}
178
179/// What an integer type is, once it no longer matters how it was spelled.
180#[derive(Clone, Copy)]
181struct IntShape {
182    signed: bool,
183    width: u32,
184    /// The standard type it is, and [`None`] for a `_BitInt`.
185    standard: Option<IntKind>,
186}
187
188impl IntShape {
189    /// The integer conversion rank, as something two of which can be compared.
190    ///
191    /// Width first, which is what makes a `_BitInt(40)` outrank an `int` and lose to a `long`.
192    /// A standard type outranks a `_BitInt` of the same width, which is C23 6.3.1.1p1 and is
193    /// why `_BitInt(32) + int` is `int`. The standard rank breaks the last tie, which is the
194    /// one that matters on every 64-bit target: `long` and `long long` are both sixty four bits
195    /// and `long long` outranks `long`.
196    fn rank(self) -> (u32, u8, u8) {
197        match self.standard {
198            Some(kind) => (self.width, 1, kind.rank()),
199            None => (self.width, 0, 0),
200        }
201    }
202
203    /// Whether every value of `other` is a value of this type.
204    fn covers(self, other: IntShape) -> bool {
205        if self.signed == other.signed {
206            return self.width >= other.width;
207        }
208        // A signed type loses a bit to the sign, so it takes a strictly wider one to hold every
209        // value of an unsigned type. That is what makes `unsigned int + long` a `long` on
210        // Linux and an `unsigned long` on Windows, where `long` is only thirty two bits.
211        self.signed && self.width > other.width
212    }
213}
214
215/// The shape of an integer type, and [`None`] when `id` is not one.
216fn integer_shape(types: &Types, id: TypeId, target: &TargetInfo) -> Option<IntShape> {
217    match types.kind(id) {
218        TypeKind::Int(kind) => Some(IntShape {
219            signed: kind.is_signed(target.char_is_signed),
220            width: int_width(kind, target),
221            standard: Some(kind),
222        }),
223        TypeKind::BitInt { signed, width } => Some(IntShape { signed, width, standard: None }),
224        _ => None,
225    }
226}
227
228/// The common type of two promoted integer types.
229fn common_integer(
230    types: &mut Types,
231    left: IntShape,
232    right: IntShape,
233    target: &TargetInfo,
234) -> TypeId {
235    let (higher, lower) = if left.rank() >= right.rank() { (left, right) } else { (right, left) };
236    if higher.signed == lower.signed || !higher.signed || higher.covers(lower) {
237        // Same signedness, or the higher ranked one is the unsigned one, or it is the signed
238        // one and wide enough to hold every value the other side had.
239        return build(types, higher, target);
240    }
241    // The signed type wins on rank and loses on range, so neither operand's type will do and
242    // the answer is the unsigned type of the same width. This is the arm that turns
243    // `long + unsigned long` into `unsigned long`, and it is the one a program is surprised by.
244    build(types, IntShape { signed: false, ..higher }, target)
245}
246
247/// The type an [`IntShape`] describes.
248fn build(types: &mut Types, shape: IntShape, target: &TargetInfo) -> TypeId {
249    match shape.standard {
250        Some(kind) if kind.is_signed(target.char_is_signed) == shape.signed => types.int(kind),
251        Some(kind) => types.int(kind.flip_sign()),
252        None => types.bit_int(shape.signed, shape.width),
253    }
254}