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::classify::{element, is_vector, lanes};
20use crate::kind::{FloatKind, IntKind, TypeKind};
21use crate::layout::{float_format, float_width, int_width, integer_info, layout};
22use crate::types::{TypeId, Types};
23
24/// The integer promotions, 6.3.1.1.
25///
26/// Anything narrower than `int` becomes `int`, or `unsigned int` when `int` cannot hold every
27/// value it had. Everything else, floating types and pointers included, is its own answer, so
28/// this can be called on any operand without asking what it is first.
29///
30/// The qualifiers and `_Atomic` come off, because by the time a value is being promoted the
31/// lvalue conversion of 6.3.2.1 has already happened and neither of them is part of the value.
32///
33/// `_BitInt` is deliberately not promoted. That is C23 6.3.1.1p2, and it is what both compilers
34/// do: `+x` on a `_BitInt(8)` is still a `_BitInt(8)`.
35pub fn promote(types: &mut Types, id: TypeId, target: &TargetInfo) -> TypeId {
36    let id = value_type(types, id);
37    match types.kind(id) {
38        TypeKind::Bool => types.int(IntKind::Int),
39        TypeKind::Int(kind) => promoted_int(types, kind, int_width(kind, target), target),
40        TypeKind::Enum(id) => {
41            // An enumeration promotes through whatever it is represented in. Until that has
42            // been decided the declaration is incomplete, which is a diagnostic somewhere with
43            // a span; `int` keeps the rest of the expression checkable in the meantime.
44            let underlying = types.enum_info(id).underlying;
45            match underlying {
46                Some(underlying) => promote(types, underlying, target),
47                None => types.int(IntKind::Int),
48            }
49        }
50        _ => id,
51    }
52}
53
54/// The integer promotions applied to a bit-field of the given width.
55///
56/// A bit-field is narrower than the type it was declared with, and it is the width rather than
57/// the type that decides. `unsigned b:3` promotes to `int`, because every three bit value fits
58/// in one, and `unsigned b:32` promotes to `unsigned int`, because they no longer do.
59///
60/// A bit-field wider than an `int` is an integer of exactly that many bits, which is the type
61/// `_BitInt` already is. The C17 wording says `unsigned int` there, which would turn a forty bit
62/// field into a thirty two bit value, and C23 says the declared type instead. Neither is what
63/// either compiler does: gcc gives such a field a type whose precision is the width, so a forty
64/// bit field shifted left by thirty two is zero rather than a value with bits above the fortieth,
65/// and clang agrees. That is a `_BitInt(40)` here, and saying it that way is what makes the usual
66/// arithmetic conversions below give the right answer for a pair of them without knowing they
67/// came from bit-fields.
68///
69/// A field as wide as the type it was declared with is that type, since there is no precision to
70/// lose and `unsigned long long b:64` reading as a `_BitInt(64)` would be a different type for no
71/// reason.
72pub fn promote_bit_field(types: &mut Types, id: TypeId, width: u32, target: &TargetInfo) -> TypeId {
73    let id = value_type(types, id);
74    let (signed, declared) = match types.kind(id) {
75        TypeKind::Bool => (false, 1),
76        TypeKind::Int(kind) => (kind.is_signed(target.char_is_signed), int_width(kind, target)),
77        TypeKind::BitInt { signed, width } => (signed, width),
78        // An enumeration bit-field promotes through what it is represented in, and anything
79        // else is not something a bit-field may be declared with.
80        _ => return promote(types, id, target),
81    };
82    let int = int_width(IntKind::Int, target);
83    if width < int || (signed && width == int) {
84        return types.int(IntKind::Int);
85    }
86    if !signed && width == int {
87        return types.int(IntKind::UInt);
88    }
89    if width >= declared {
90        return promote(types, id, target);
91    }
92    types.bit_int(signed, width)
93}
94
95/// The usual arithmetic conversions, 6.3.1.8: the one type both operands are converted to.
96///
97/// [`None`] when either operand is not an arithmetic type, which is not a failure of this
98/// function but the shape of a diagnostic its caller is about to write.
99///
100/// The floating rules come first and the integer rules only run when neither side is floating,
101/// which is why `unsigned long long + float` is `float` and loses precision rather than being
102/// the other way round.
103///
104/// The complexity comes off both operands first and goes back on at the end, which is 6.3.1.8p1
105/// read literally: the rule is written over the corresponding real types and says the result is
106/// complex when either operand was. Doing it that way is also what gives gcc's answer for the
107/// complex integer types, since `_Complex int + long` is the integer rule on `int` and `long`
108/// with the complexity carried across, and none of the integer rules had to learn the word.
109///
110/// A half of a complex operand is not promoted, which is measured from gcc 16.2.0 rather than
111/// read anywhere: `_Complex char + _Complex char` is a `_Complex char` where `char + char` is an
112/// `int`. The promotions are written over the integer types and a complex one is not one of
113/// them, so the operand that is not complex is promoted and the half is taken as it stands.
114/// `_Complex char + char` is therefore a `_Complex int`, since the `char` beside it promotes and
115/// the rule then picks the wider of the two.
116pub fn usual_arithmetic(
117    types: &mut Types,
118    left: TypeId,
119    right: TypeId,
120    target: &TargetInfo,
121) -> Option<TypeId> {
122    let left = value_type(types, left);
123    let right = value_type(types, right);
124    let (left, left_complex) = real_of(types, left, target);
125    let (right, right_complex) = real_of(types, right, target);
126    let common = real_usual_arithmetic(types, left, right, target)?;
127    Some(if left_complex || right_complex { types.complex(common) } else { common })
128}
129
130/// The operand as the real type the rule works on, and whether it was complex.
131///
132/// The promotion is here because it is the operand that is not complex that gets one.
133fn real_of(types: &mut Types, id: TypeId, target: &TargetInfo) -> (TypeId, bool) {
134    match types.kind(id) {
135        TypeKind::Complex(part) => (part, true),
136        _ => (promote(types, id, target), false),
137    }
138}
139
140/// The usual arithmetic conversions on two operands neither of which is complex.
141fn real_usual_arithmetic(
142    types: &mut Types,
143    left: TypeId,
144    right: TypeId,
145    target: &TargetInfo,
146) -> Option<TypeId> {
147    if let Some(common) = floating(types, left, right, target) {
148        return Some(common);
149    }
150    if left == right {
151        // Not only a shortcut: it is also the answer for the types this function does not model
152        // as integers, which is every one of them once the two sides agree.
153        return integer_shape(types, left, target).map(|_| left);
154    }
155    let left = integer_shape(types, left, target)?;
156    let right = integer_shape(types, right, target)?;
157    Some(common_integer(types, left, right, target))
158}
159
160/// Whether one vector may be assigned to another that is not compatible with it.
161///
162/// GNU C is deliberately loose here, and it has to be, because the vector extension gives no way
163/// to spell a conversion: `(V)x` on two vectors reinterprets the bytes and there is no cast that
164/// means anything else, so a rule as strict as the one for records would leave a program with no
165/// way to write what it meant. gcc's rule, in `vector_types_convertible_p`, is that two vectors
166/// of the same total size convert when their lanes are both integers, or are both floating and
167/// the same width. The lane count may differ, so a sixteen lane vector of `short` may be assigned
168/// to an eight lane vector of `int`.
169///
170/// The place a program hits this without asking for it is a comparison. The mask a comparison
171/// answers with has signed lanes whatever the operands had, so `V x = (v > 0);` where `V` has
172/// unsigned lanes is this rule and nothing more exotic, and three of the torture cases are
173/// exactly that line.
174///
175/// gcc notes the first one it converts this way and suggests `-flax-vector-conversions`. Nothing
176/// is said here, because the note is about a flag that turns off a stricter check we do not make.
177#[must_use]
178pub fn vectors_convertible(types: &Types, a: TypeId, b: TypeId, target: &TargetInfo) -> bool {
179    if !is_vector(types, a) || !is_vector(types, b) {
180        return false;
181    }
182    let (Some(x), Some(y)) = (element(types, a), element(types, b)) else {
183        return false;
184    };
185    let sizes = |left, right| match (layout(types, left, target), layout(types, right, target)) {
186        (Ok(left), Ok(right)) => Some((left.size, right.size)),
187        _ => None,
188    };
189    let Some((whole, other)) = sizes(a, b) else {
190        return false;
191    };
192    if whole != other {
193        return false;
194    }
195    match (integer_info(types, x, target), integer_info(types, y, target)) {
196        (Some(_), Some(_)) => true,
197        (None, None) => sizes(x, y).is_some_and(|(left, right)| left == right),
198        _ => false,
199    }
200}
201
202/// The type a comparison of two vectors answers with, and [`None`] where `id` is not a vector.
203///
204/// GNU C says `a < b` over vectors is a vector of signed integers with the lane count the
205/// operands had and a lane as wide as theirs, holding all ones where the comparison held and
206/// zero where it did not. The width has to match rather than merely be an `int`, because the
207/// mask is written to be used: `(a < b) & c` reaches every bit of `c` only when the two line up
208/// lane for lane, and that is what the result of a vector comparison is for.
209///
210/// A vector of signed integers is its own mask type, which is what gcc answers and is worth
211/// keeping, since `v4si` comparing to `v4si` reading back as some other spelling of the same
212/// thing is a diagnostic nobody can act on. Anything else, an unsigned lane or a floating one,
213/// gets the signed integer type of that width.
214///
215/// [`None`] also where the target has no standard integer as wide as the lane, which is the
216/// eighty bit `long double` and nothing else.
217pub fn mask_of(types: &mut Types, id: TypeId, target: &TargetInfo) -> Option<TypeId> {
218    let elem = element(types, id)?;
219    let len = lanes(types, id)?;
220    let bits = match integer_info(types, elem, target) {
221        Some(info) if info.signed => {
222            let lane = types.unqualified(elem);
223            return Some(types.vector(lane, len));
224        }
225        Some(info) => info.width,
226        None => match types.kind(types.canonical(elem)) {
227            TypeKind::Float(kind) => float_width(kind, target),
228            _ => return None,
229        },
230    };
231    let standard = [
232        IntKind::SChar,
233        IntKind::Short,
234        IntKind::Int,
235        IntKind::Long,
236        IntKind::LongLong,
237        IntKind::Int128,
238    ];
239    let kind = standard.into_iter().find(|&kind| int_width(kind, target) == bits)?;
240    let lane = types.int(kind);
241    Some(types.vector(lane, len))
242}
243
244/// The type of a value of type `id`, which is `id` without the parts an lvalue conversion
245/// removes.
246fn value_type(types: &mut Types, id: TypeId) -> TypeId {
247    let id = types.canonical(id);
248    let id = match types.kind(id) {
249        TypeKind::Atomic(inner) => types.canonical(inner),
250        _ => id,
251    };
252    types.unqualified(id)
253}
254
255/// The promotion of a standard integer type of the given width.
256fn promoted_int(types: &mut Types, kind: IntKind, width: u32, target: &TargetInfo) -> TypeId {
257    if kind.rank() >= IntKind::Int.rank() {
258        return types.int(kind);
259    }
260    let int = int_width(IntKind::Int, target);
261    let signed = kind.is_signed(target.char_is_signed);
262    if width < int || (signed && width == int) {
263        return types.int(IntKind::Int);
264    }
265    types.int(IntKind::UInt)
266}
267
268/// The common type when either side is a real floating type, and [`None`] when neither is.
269///
270/// The answer is the one with the higher rank, or the floating one when the other side is an
271/// integer. The complexity was taken off both operands before this and is put back on by
272/// [`usual_arithmetic`], which is why `_Complex float + double` is `_Complex double`.
273fn floating(types: &mut Types, left: TypeId, right: TypeId, target: &TargetInfo) -> Option<TypeId> {
274    let left = float_part(types, left);
275    let right = float_part(types, right);
276    let kind = match (left, right) {
277        (None, None) => return None,
278        (Some(kind), None) | (None, Some(kind)) => kind,
279        (Some(a), Some(b)) => {
280            if float_rank(a, target) >= float_rank(b, target) {
281                a
282            } else {
283                b
284            }
285        }
286    };
287    Some(types.float(kind))
288}
289
290/// The conversion rank of a real floating type, as something two of which can be compared.
291///
292/// There is no ordering on the kinds themselves to use here, because since C23 the answer
293/// depends on the target: `long double` outranks `_Float64x` on x86-64, where both of them are
294/// the x87 format, and loses to it on Apple, where `long double` is a `double`. So the question
295/// is asked of the format instead. Precision first and then range, which never disagree among
296/// the binary formats, and [`FloatKind::tie_break`] settles two types that are the same format,
297/// which is what makes `double + _Float64` a `_Float64`.
298fn float_rank(kind: FloatKind, target: &TargetInfo) -> (u32, i32, u8) {
299    let format = float_format(kind, target);
300    (format.precision(), format.max_exponent(), kind.tie_break())
301}
302
303/// The real floating type `id` is, and [`None`] for everything else.
304fn float_part(types: &Types, id: TypeId) -> Option<FloatKind> {
305    match types.kind(id) {
306        TypeKind::Float(kind) => Some(kind),
307        _ => None,
308    }
309}
310
311/// What an integer type is, once it no longer matters how it was spelled.
312#[derive(Clone, Copy)]
313struct IntShape {
314    signed: bool,
315    width: u32,
316    /// The standard type it is, and [`None`] for a `_BitInt`.
317    standard: Option<IntKind>,
318}
319
320impl IntShape {
321    /// The integer conversion rank, as something two of which can be compared.
322    ///
323    /// Width first, which is what makes a `_BitInt(40)` outrank an `int` and lose to a `long`.
324    /// A standard type outranks a `_BitInt` of the same width, which is C23 6.3.1.1p1 and is
325    /// why `_BitInt(32) + int` is `int`. The standard rank breaks the last tie, which is the
326    /// one that matters on every 64-bit target: `long` and `long long` are both sixty four bits
327    /// and `long long` outranks `long`.
328    fn rank(self) -> (u32, u8, u8) {
329        match self.standard {
330            Some(kind) => (self.width, 1, kind.rank()),
331            None => (self.width, 0, 0),
332        }
333    }
334
335    /// Whether every value of `other` is a value of this type.
336    fn covers(self, other: IntShape) -> bool {
337        if self.signed == other.signed {
338            return self.width >= other.width;
339        }
340        // A signed type loses a bit to the sign, so it takes a strictly wider one to hold every
341        // value of an unsigned type. That is what makes `unsigned int + long` a `long` on
342        // Linux and an `unsigned long` on Windows, where `long` is only thirty two bits.
343        self.signed && self.width > other.width
344    }
345}
346
347/// The shape of an integer type, and [`None`] when `id` is not one.
348fn integer_shape(types: &Types, id: TypeId, target: &TargetInfo) -> Option<IntShape> {
349    match types.kind(id) {
350        TypeKind::Int(kind) => Some(IntShape {
351            signed: kind.is_signed(target.char_is_signed),
352            width: int_width(kind, target),
353            standard: Some(kind),
354        }),
355        TypeKind::BitInt { signed, width } => Some(IntShape { signed, width, standard: None }),
356        _ => None,
357    }
358}
359
360/// The common type of two promoted integer types.
361fn common_integer(
362    types: &mut Types,
363    left: IntShape,
364    right: IntShape,
365    target: &TargetInfo,
366) -> TypeId {
367    let (higher, lower) = if left.rank() >= right.rank() { (left, right) } else { (right, left) };
368    if higher.signed == lower.signed || !higher.signed || higher.covers(lower) {
369        // Same signedness, or the higher ranked one is the unsigned one, or it is the signed
370        // one and wide enough to hold every value the other side had.
371        return build(types, higher, target);
372    }
373    // The signed type wins on rank and loses on range, so neither operand's type will do and
374    // the answer is the unsigned type of the same width. This is the arm that turns
375    // `long + unsigned long` into `unsigned long`, and it is the one a program is surprised by.
376    build(types, IntShape { signed: false, ..higher }, target)
377}
378
379/// The type an [`IntShape`] describes.
380fn build(types: &mut Types, shape: IntShape, target: &TargetInfo) -> TypeId {
381    match shape.standard {
382        Some(kind) if kind.is_signed(target.char_is_signed) == shape.signed => types.int(kind),
383        Some(kind) => types.int(kind.flip_sign()),
384        None => types.bit_int(shape.signed, shape.width),
385    }
386}