Skip to main content

rucc_sema/
convert.rs

1//! The conversions the language performs without being asked, as nodes in the tree.
2//!
3//! Design: `spec/07-types-and-semantics.md` section 7.2.
4//!
5//! Every one of these writes a [`Conversion`] node. Nothing downstream is allowed to work out
6//! for itself that an `int` met a `long` somewhere, because a second place that knows the
7//! conversion rules is a second place that is slightly wrong about them, and that is where the
8//! sign extension bugs live.
9//!
10//! # The order the standard puts them in
11//!
12//! An expression used for its value goes through at most three steps, in this order, and the
13//! order is not a convenience:
14//!
15//! First the lvalue conversion of 6.3.2.1, which reads the object and drops the qualifiers and
16//! the atomicity, since neither is part of a value. An array and a function do not take part in
17//! it at all: they decay instead, which is why `sizeof a` on an array is the array's size and
18//! not a pointer's, and why the decay has to be a separate step rather than a special case of
19//! reading.
20//!
21//! Then the integer promotions of 6.3.1.1, which are about one operand.
22//!
23//! Then the usual arithmetic conversions of 6.3.1.8, which are about two.
24//!
25//! [`Conv::value`] is the first step and is what almost every caller wants, because an operand
26//! that is still an lvalue is an operand somebody forgot to read.
27//!
28//! # Bit-fields
29//!
30//! A bit-field's lvalue conversion gives the type it was declared with and its promotion is
31//! decided by its width rather than by that type, which is why [`Conv::promote_bits`] exists
32//! next to [`Conv::promote`]. `unsigned b:3` promotes to `int` because every three bit value
33//! fits in one, and `unsigned b:32` promotes to `unsigned int` because they no longer do.
34//!
35//! No caller has to know that, because [`Conv::promote`] and [`Conv::usual_arithmetic`] look for
36//! the width themselves. A caller that had to remember would be a caller that forgot, and the
37//! symptom is a whole expression coming out unsigned on the strength of one member's declared
38//! type.
39
40use rucc_target::TargetInfo;
41use rucc_types::{TypeId, TypeKind, Types, is_arithmetic, is_pointer, is_void};
42
43use crate::expr::{Category, Conversion, Expr, ExprId, ExprKind};
44use crate::tast::{Const, Tast};
45
46/// Everything a conversion needs: the tree to write the node into and the table to ask.
47///
48/// Three references rather than a pass-wide context, because the conversions are the part of
49/// semantic analysis with no state of its own and nothing else here should be able to reach
50/// the scopes or the diagnostics through them.
51#[derive(Debug)]
52pub struct Conv<'a> {
53    /// The tree the nodes are written into.
54    pub tast: &'a mut Tast,
55    /// The types, which conversions extend.
56    pub types: &'a mut Types,
57    /// What the target's integers are, which is what the promotions are decided by.
58    pub target: &'a TargetInfo,
59}
60
61impl Conv<'_> {
62    /// The value of an expression: 6.3.2.1, with the decays that replace it.
63    ///
64    /// An array becomes a pointer to its first element, a function becomes a pointer to itself,
65    /// and everything else that is an lvalue is read. An expression that is already a value is
66    /// its own answer, so this can be called on any operand without asking what it is first.
67    pub fn value(&mut self, expr: ExprId) -> ExprId {
68        let ty = self.tast[expr].ty;
69        match self.types.kind(self.types.canonical(ty)) {
70            TypeKind::Array { elem, .. } => {
71                let ty = self.types.pointer(elem);
72                self.write(Conversion::ArrayDecay, expr, ty)
73            }
74            TypeKind::Function(_) => {
75                let ty = self.types.pointer(ty);
76                self.write(Conversion::FunctionDecay, expr, ty)
77            }
78            _ if self.tast[expr].category == Category::Rvalue => expr,
79            _ => {
80                let ty = self.read_as(ty);
81                self.write(Conversion::Lvalue, expr, ty)
82            }
83        }
84    }
85
86    /// The value of an expression with the integer promotions applied, 6.3.1.1.
87    ///
88    /// Anything narrower than `int` becomes `int`, or `unsigned int` where `int` cannot hold
89    /// every value it had. A floating type, a pointer and a `_BitInt` are each their own
90    /// answer, the last because C23 6.3.1.1p2 says so and because that is the point of the
91    /// type: it is the one integer type in C that does what it says.
92    pub fn promote(&mut self, expr: ExprId) -> ExprId {
93        let expr = self.value_promoting_bits(expr);
94        let ty = self.tast[expr].ty;
95        let promoted = rucc_types::promote(self.types, ty, self.target);
96        self.arithmetic(expr, promoted)
97    }
98
99    /// The value of a bit-field with the integer promotions applied to its width.
100    ///
101    /// A bit-field is narrower than the type it was declared with, and it is the width that
102    /// decides. The caller passes the width because the tree holds the field index and the
103    /// width is a fact about the record, not about the expression.
104    pub fn promote_bits(&mut self, expr: ExprId, width: u32) -> ExprId {
105        let expr = self.value(expr);
106        let ty = self.tast[expr].ty;
107        let promoted = rucc_types::promote_bit_field(self.types, ty, width, self.target);
108        self.arithmetic(expr, promoted)
109    }
110
111    /// The value of an expression, promoted by its width first where it names a bit-field.
112    ///
113    /// This is what every operand that is about to be promoted goes through, because the type a
114    /// bit-field was declared with is not the type it brings to an operator: `unsigned b:1` is
115    /// an `int` in `b + 1` and not an `unsigned int`, and a whole expression comes out signed or
116    /// unsigned on the strength of that one width.
117    fn value_promoting_bits(&mut self, expr: ExprId) -> ExprId {
118        match self.bit_field_width(expr) {
119            Some(width) => self.promote_bits(expr, width),
120            None => self.value(expr),
121        }
122    }
123
124    /// The width of the bit-field an expression names, or [`None`] where it names none.
125    ///
126    /// The width lives on the record rather than on the expression, so this asks the type table
127    /// rather than reading it off the node. Only a member access can be one: a bit-field has no
128    /// address, so there is no other expression that can arrive still being one.
129    ///
130    /// The lvalue conversion is looked through, because most callers read the object before they
131    /// know they are about to promote it and the value they are left holding is still as wide as
132    /// the field was.
133    fn bit_field_width(&self, expr: ExprId) -> Option<u32> {
134        let expr = match self.tast[expr].kind {
135            ExprKind::Convert { kind: Conversion::Lvalue, operand } => operand,
136            _ => expr,
137        };
138        let ExprKind::Member { base, field } = self.tast[expr].kind else { return None };
139        let base = self.types.canonical(self.tast[base].ty);
140        let TypeKind::Record(record) = self.types.kind(base) else { return None };
141        self.types.record_info(record).fields.get(field as usize)?.bits
142    }
143
144    /// The usual arithmetic conversions, 6.3.1.8: both operands converted to one type.
145    ///
146    /// [`None`] where either operand is not arithmetic, which is not a failure of this rule but
147    /// a question it does not answer, since `p + 1` is pointer arithmetic and never reaches it.
148    pub fn usual_arithmetic(&mut self, lhs: ExprId, rhs: ExprId) -> Option<(ExprId, ExprId)> {
149        let (lhs, rhs) = (self.value_promoting_bits(lhs), self.value_promoting_bits(rhs));
150        let common = rucc_types::usual_arithmetic(
151            self.types,
152            self.tast[lhs].ty,
153            self.tast[rhs].ty,
154            self.target,
155        )?;
156        Some((self.arithmetic(lhs, common), self.arithmetic(rhs, common)))
157    }
158
159    /// A scalar as a condition, which is a comparison against zero and not a truncation.
160    ///
161    /// That is why it is [`Conversion::Bool`] rather than [`Conversion::Arithmetic`]: `(bool)
162    /// 256` is true and `(char) 256` is zero, and a compiler that treats the two the same is
163    /// wrong about one of them.
164    pub fn to_bool(&mut self, expr: ExprId) -> ExprId {
165        let expr = self.value(expr);
166        let boolean = self.types.boolean();
167        if self.tast[expr].ty == boolean {
168            return expr;
169        }
170        self.write(Conversion::Bool, expr, boolean)
171    }
172
173    /// A value discarded, which is what a cast to `void` does.
174    ///
175    /// An expression statement does not write one of these, even though it discards a value too.
176    /// The statement is what does the discarding, and a statement expression's value is the last
177    /// statement's, so an expression statement that had thrown its type away would have nothing
178    /// left to give.
179    pub fn to_void(&mut self, expr: ExprId) -> ExprId {
180        if is_void(self.types, self.tast[expr].ty) {
181            return expr;
182        }
183        let void = self.types.void();
184        self.write(Conversion::Void, expr, void)
185    }
186
187    /// A value converted to a given type, with the kind of conversion worked out from the two.
188    ///
189    /// This is what an assignment, an argument, a `return` and an initializer all do. It writes
190    /// the conversion the pair calls for and does not judge whether the pair is allowed: the
191    /// caller has the span and the wording, and a conversion that should have been diagnosed is
192    /// a diagnostic the caller owes rather than a node this refuses to write.
193    pub fn to_type(&mut self, expr: ExprId, ty: TypeId) -> ExprId {
194        let expr = self.value(expr);
195        let from = self.tast[expr].ty;
196        let target = self.read_as(ty);
197        if from == target {
198            return expr;
199        }
200        if is_void(self.types, target) {
201            return self.to_void(expr);
202        }
203        let boolean = self.types.boolean();
204        if target == boolean {
205            return self.to_bool(expr);
206        }
207        let kind = if is_pointer(self.types, target) {
208            // A null pointer constant is not the integer zero converted. The constant may have
209            // any integer type and `(void *)0` is one of them, so what makes it a null pointer
210            // is what it says rather than what it weighs.
211            if self.is_null_pointer_constant(expr) {
212                Conversion::NullPointer
213            } else {
214                Conversion::Pointer
215            }
216        } else if is_arithmetic(self.types, target) && is_arithmetic(self.types, from) {
217            Conversion::Arithmetic
218        } else if rucc_types::is_vector(self.types, target) && is_arithmetic(self.types, from) {
219            Conversion::Broadcast
220        } else {
221            // A record to a record of the same type, or anything else the caller has already
222            // decided about. There is nothing to compute, so the node records that a value of
223            // one type is being used as another and the verifier can see it happened.
224            Conversion::Pointer
225        };
226        self.write(kind, expr, target)
227    }
228
229    /// Whether an expression is a null pointer constant, 6.3.2.3p3.
230    ///
231    /// An integer constant expression with the value zero, or such an expression cast to `void
232    /// *`. The casts and the conversions are looked through because `(void *)0` is one and so
233    /// is `(long)0`, and stopping at the first node would see a cast rather than a zero.
234    #[must_use]
235    pub fn is_null_pointer_constant(&self, expr: ExprId) -> bool {
236        match self.tast[expr].kind {
237            ExprKind::Const(value) => self.tast[value] == Const::Int(0),
238            ExprKind::Cast(inner) | ExprKind::Convert { operand: inner, .. } => {
239                self.is_null_pointer_constant(inner)
240            }
241            _ => false,
242        }
243    }
244
245    /// Writes an arithmetic conversion, or nothing where the type is already the one wanted.
246    fn arithmetic(&mut self, expr: ExprId, ty: TypeId) -> ExprId {
247        if self.tast[expr].ty == ty {
248            return expr;
249        }
250        self.write(Conversion::Arithmetic, expr, ty)
251    }
252
253    /// The type a value has once it has been read out of an object.
254    ///
255    /// The qualifiers and the atomicity come off, because neither is part of a value: `const
256    /// int x; x + 1` has an `int` on the left of the `+` and not a `const int`.
257    pub(crate) fn read_as(&mut self, ty: TypeId) -> TypeId {
258        let stripped = match self.types.kind(self.types.canonical(ty)) {
259            TypeKind::Atomic(inner) => inner,
260            _ => ty,
261        };
262        self.types.unqualified(stripped)
263    }
264
265    /// Writes one conversion node over an operand.
266    fn write(&mut self, kind: Conversion, operand: ExprId, ty: TypeId) -> ExprId {
267        let span = self.tast.expr_span(operand);
268        let node = Expr::new(ExprKind::Convert { kind, operand }, ty, Category::Rvalue);
269        self.tast.expr(node, span)
270    }
271}
272
273#[cfg(test)]
274mod tests {
275    use rucc_base::Interner;
276    use rucc_diag::Span;
277    use rucc_target::{TargetInfo, Triple};
278    use rucc_types::{ArrayLen, FunctionType, IntKind, Qualifiers};
279
280    use super::*;
281    use crate::decl::{Decl, DeclKind, DeclList, Definition, Emission, Linkage, StorageDuration};
282    use crate::print::Printer;
283
284    struct Fixture {
285        tast: Tast,
286        types: Types,
287        names: Interner,
288        target: TargetInfo,
289    }
290
291    impl Fixture {
292        fn new() -> Fixture {
293            let target =
294                TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().expect("a triple"));
295            Fixture { tast: Tast::new(), types: Types::new(), names: Interner::new(), target }
296        }
297
298        fn conv(&mut self) -> Conv<'_> {
299            Conv { tast: &mut self.tast, types: &mut self.types, target: &self.target }
300        }
301
302        /// An lvalue of the given type, which is what a use of an object is.
303        fn object(&mut self, ty: TypeId) -> ExprId {
304            let decl = self.tast.decl(
305                Decl {
306                    name: None,
307                    ty,
308                    kind: DeclKind::Object,
309                    linkage: Linkage::None,
310                    duration: StorageDuration::Automatic,
311                    state: Definition::Defined,
312                    alignment: None,
313                    constant: false,
314                    retained: false,
315                    asm_label: None,
316                    alias: None,
317                    inline: Emission::Silent,
318                    gnu_inline: false,
319                    noreturn: false,
320                    visibility: None,
321                    init: None,
322                    params: DeclList::EMPTY,
323                    body: None,
324                },
325                Span::DUMMY,
326            );
327            self.tast.expr(Expr::new(ExprKind::Decl(decl), ty, Category::Lvalue), Span::DUMMY)
328        }
329
330        /// A use of the one member of a `struct` that has one, which is a bit-field of `bits`.
331        fn bit_field(&mut self, ty: TypeId, bits: u32) -> ExprId {
332            let fields = [rucc_types::FieldDecl::bit_field(None, ty, bits)];
333            let id = self.types.declare_record(rucc_types::RecordKind::Struct, None);
334            let laid_out = rucc_types::layout_record(
335                &self.types,
336                rucc_types::RecordKind::Struct,
337                &fields,
338                &rucc_types::RecordOptions::default(),
339                &self.target,
340            )
341            .expect("a layout");
342            self.types.complete_record(id, laid_out);
343            let record = self.types.record(id);
344            let base = self.object(record);
345            self.tast.expr(
346                Expr::new(ExprKind::Member { base, field: 0 }, ty, Category::Bitfield),
347                Span::DUMMY,
348            )
349        }
350
351        fn zero(&mut self, ty: TypeId) -> ExprId {
352            let value = self.tast.add_const(Const::Int(0));
353            self.tast.expr(Expr::new(ExprKind::Const(value), ty, Category::Rvalue), Span::DUMMY)
354        }
355
356        fn text(&self, expr: ExprId) -> String {
357            let mut printer = Printer::new(&self.tast, &self.types, &self.names);
358            printer.expr(expr);
359            printer.finish()
360        }
361    }
362
363    #[test]
364    fn reading_an_object_drops_the_qualifiers_because_they_are_not_part_of_a_value() {
365        let mut f = Fixture::new();
366        let int = f.types.int(IntKind::Int);
367        let constant = f.types.qualified(int, Qualifiers::CONST);
368        let object = f.object(constant);
369        let read = f.conv().value(object);
370
371        assert_eq!(f.tast[read].ty, int);
372        assert_eq!(f.tast[read].category, Category::Rvalue);
373        assert_eq!(f.text(read), "convert lvalue : int\n  decl #0 : const int lvalue\n");
374    }
375
376    #[test]
377    fn an_atomic_object_reads_as_the_type_it_wraps() {
378        let mut f = Fixture::new();
379        let int = f.types.int(IntKind::Int);
380        let atomic = f.types.atomic(int);
381        let object = f.object(atomic);
382        let read = f.conv().value(object);
383
384        assert_eq!(f.tast[read].ty, int);
385    }
386
387    #[test]
388    fn an_array_decays_and_is_not_read() {
389        let mut f = Fixture::new();
390        let int = f.types.int(IntKind::Int);
391        let array = f.types.array(int, ArrayLen::Fixed(3));
392        let object = f.object(array);
393        let decayed = f.conv().value(object);
394
395        // Not an lvalue conversion, which is why `sizeof a` is the array's size: the decay is a
396        // step of its own and `sizeof` is the operator that does not take it.
397        assert_eq!(f.text(decayed), "convert array-decay : int *\n  decl #0 : int[3] lvalue\n");
398    }
399
400    #[test]
401    fn a_function_decays_to_a_pointer_to_itself() {
402        let mut f = Fixture::new();
403        let void = f.types.void();
404        let signature =
405            FunctionType { ret: void, params: Vec::new(), variadic: false, prototyped: true };
406        let function = f.types.function(signature);
407        let designator =
408            f.tast.expr(Expr::new(ExprKind::Error, function, Category::Function), Span::DUMMY);
409        let decayed = f.conv().value(designator);
410
411        assert_eq!(
412            f.text(decayed),
413            "convert function-decay : void (*)(void)\n  error : void(void) function\n"
414        );
415    }
416
417    #[test]
418    fn a_narrow_integer_promotes_and_an_int_does_not_move() {
419        let mut f = Fixture::new();
420        let char_type = f.types.int(IntKind::Char);
421        let int = f.types.int(IntKind::Int);
422        let narrow = f.object(char_type);
423        let wide = f.object(int);
424
425        let promoted = f.conv().promote(narrow);
426        assert_eq!(f.tast[promoted].ty, int);
427        assert_eq!(
428            f.text(promoted),
429            "convert arithmetic : int\n  convert lvalue : char\n    decl #0 : char lvalue\n"
430        );
431
432        // Nothing is written where nothing happens, so a dump has no noise in it.
433        let already = f.conv().promote(wide);
434        assert_eq!(f.text(already), "convert lvalue : int\n  decl #1 : int lvalue\n");
435    }
436
437    #[test]
438    fn a_bit_field_promotes_by_its_width_and_not_by_its_type() {
439        let mut f = Fixture::new();
440        let unsigned = f.types.int(IntKind::UInt);
441        let int = f.types.int(IntKind::Int);
442        let three = f.object(unsigned);
443        let full = f.object(unsigned);
444
445        // Every three bit value fits in an `int`, so the promotion changes the signedness.
446        let narrow = f.conv().promote_bits(three, 3);
447        assert_eq!(f.tast[narrow].ty, int);
448        // Thirty two bit values no longer do.
449        let wide = f.conv().promote_bits(full, 32);
450        assert_eq!(f.tast[wide].ty, unsigned);
451    }
452
453    #[test]
454    fn a_bit_field_operand_promotes_by_its_width_without_being_asked() {
455        // The width is not on the operand, so this is the one promotion that has to be found
456        // rather than read off the node, and forgetting it makes `b.flag + 1` come out unsigned
457        // for a one bit field. gcc says `int`, and so does 6.3.1.1p2.
458        let mut f = Fixture::new();
459        let unsigned = f.types.int(IntKind::UInt);
460        let int = f.types.int(IntKind::Int);
461        let one = f.zero(int);
462        let again = f.zero(int);
463
464        let narrow = f.bit_field(unsigned, 1);
465        let (lhs, rhs) = f.conv().usual_arithmetic(narrow, one).expect("both are arithmetic");
466        assert_eq!(f.tast[lhs].ty, int);
467        assert_eq!(f.tast[rhs].ty, int);
468
469        // A field as wide as the type it was declared with keeps that type, which is the case
470        // that says the width is what decides and not the fact of being a bit-field.
471        let full = f.bit_field(unsigned, 32);
472        let (lhs, rhs) = f.conv().usual_arithmetic(full, again).expect("both are arithmetic");
473        assert_eq!(f.tast[lhs].ty, unsigned);
474        assert_eq!(f.tast[rhs].ty, unsigned);
475    }
476
477    #[test]
478    fn a_bit_field_that_has_already_been_read_still_promotes_by_its_width() {
479        // Almost every caller reads the object before it knows it is about to promote, so the
480        // member is under an lvalue conversion by the time the promotion looks for it.
481        let mut f = Fixture::new();
482        let unsigned = f.types.int(IntKind::UInt);
483        let int = f.types.int(IntKind::Int);
484        let narrow = f.bit_field(unsigned, 1);
485        let read = f.conv().value(narrow);
486
487        let promoted = f.conv().promote(read);
488        assert_eq!(f.tast[promoted].ty, int);
489    }
490
491    #[test]
492    fn the_usual_arithmetic_conversions_move_both_sides_to_one_type() {
493        let mut f = Fixture::new();
494        let int = f.types.int(IntKind::Int);
495        let long = f.types.int(IntKind::Long);
496        let narrow = f.object(int);
497        let wide = f.object(long);
498
499        let (lhs, rhs) = f.conv().usual_arithmetic(narrow, wide).expect("both are arithmetic");
500        assert_eq!(f.tast[lhs].ty, long);
501        assert_eq!(f.tast[rhs].ty, long);
502    }
503
504    #[test]
505    fn a_pointer_pair_has_no_usual_arithmetic_conversion() {
506        let mut f = Fixture::new();
507        let int = f.types.int(IntKind::Int);
508        let pointer = f.types.pointer(int);
509        let left = f.object(pointer);
510        let right = f.object(int);
511
512        assert!(f.conv().usual_arithmetic(left, right).is_none());
513    }
514
515    #[test]
516    fn a_condition_is_a_comparison_against_zero_and_not_a_truncation() {
517        let mut f = Fixture::new();
518        let int = f.types.int(IntKind::Int);
519        let object = f.object(int);
520        let condition = f.conv().to_bool(object);
521
522        // `(bool) 256` is true and `(char) 256` is zero, which is why this is its own kind.
523        assert_eq!(
524            f.text(condition),
525            "convert bool : _Bool\n  convert lvalue : int\n    decl #0 : int lvalue\n"
526        );
527    }
528
529    #[test]
530    fn a_zero_of_any_integer_type_is_a_null_pointer_constant() {
531        let mut f = Fixture::new();
532        let long = f.types.int(IntKind::Long);
533        let int = f.types.int(IntKind::Int);
534        let pointer = f.types.pointer(int);
535        let zero = f.zero(long);
536        let null = f.conv().to_type(zero, pointer);
537
538        assert_eq!(f.text(null), "convert null-pointer : int *\n  const 0 : long\n");
539    }
540
541    #[test]
542    fn a_pointer_that_is_not_a_constant_zero_is_an_ordinary_pointer_conversion() {
543        let mut f = Fixture::new();
544        let int = f.types.int(IntKind::Int);
545        let void = f.types.void();
546        let from = f.types.pointer(void);
547        let to = f.types.pointer(int);
548        let object = f.object(from);
549        let converted = f.conv().to_type(object, to);
550
551        assert_eq!(
552            f.text(converted),
553            "convert pointer : int *\n  convert lvalue : void *\n    decl #0 : void * lvalue\n"
554        );
555    }
556
557    #[test]
558    fn converting_to_the_type_it_already_has_writes_nothing() {
559        let mut f = Fixture::new();
560        let int = f.types.int(IntKind::Int);
561        let object = f.object(int);
562        let read = f.conv().value(object);
563        let again = f.conv().to_type(read, int);
564
565        assert_eq!(read, again);
566    }
567
568    #[test]
569    fn a_value_is_discarded_by_a_node_rather_than_by_being_ignored() {
570        let mut f = Fixture::new();
571        let int = f.types.int(IntKind::Int);
572        let object = f.object(int);
573        let dropped = f.conv().to_void(object);
574
575        assert_eq!(f.text(dropped), "convert void : void\n  decl #0 : int lvalue\n");
576    }
577}