Skip to main content

rucc_sema/
eval.rs

1//! Folding a constant expression, which is what an array bound and a `case` label are made of.
2//!
3//! Design: `spec/07-types-and-semantics.md` section 7.6.
4//!
5//! C has a dozen places where an expression has to have a value at translation time: the size of
6//! an array, a `case` label, an enumerator, a bit-field width, `static_assert`, `alignas`, the
7//! width of a `_BitInt`, and an initializer for an object with static storage duration. This is
8//! the one thing that answers all of them, because a compiler with two constant folders has two
9//! answers to `1 << 31` and only one of them is right.
10//!
11//! It folds the typed tree rather than the untyped one. That is not a detail: every conversion
12//! is already a node here, so folding never has to work out that an `int` met a `long`, and the
13//! width every operation happens in is on the node in front of it. The same walk over the
14//! untyped tree would have to redo the conversion rules, and that is the second implementation
15//! that ends up slightly wrong.
16//!
17//! # What it reports and what it hands back
18//!
19//! Two different things go wrong when a constant is wanted, and they belong to two different
20//! places. A division by zero is wrong wherever it is written, so it is reported here. Not being
21//! a constant at all is only wrong because of where the expression is, and gcc's messages say
22//! so: `case label does not reduce to an integer constant` and `enumerator value for 'x' is not
23//! an integer constant` are two sentences about one failure. So [`NotConstant`] is handed back
24//! with the node that stopped it and the caller writes the sentence.
25//!
26//! # Arithmetic
27//!
28//! Integers are held the way [`Const::Int`] holds them, as the low bits of the type extended
29//! into a hundred and twenty eight by its signedness, so every operation is done in [`i128`] and
30//! then wrapped by the [`IntegerInfo`] of the type it happened in. Signed overflow is warned
31//! about and wrapped, which is what gcc does and is the only useful thing to do: the standard
32//! says the program is undefined and a person who wrote `2147483647 + 1` wants to be told.
33//! Unsigned overflow is silent, because it is not overflow.
34//!
35//! Floating operations go to [`rucc_base::float`], which is correctly rounded and does not ask
36//! the host anything. Nothing here looks at the status those return. A constant that overflows
37//! to an infinity or loses a digit is still a constant and gcc says nothing about either, so the
38//! flags are dropped on purpose rather than by omission.
39//!
40//! # Addresses
41//!
42//! `&x`, `&s.field + 3` and a string literal are constants of a different kind: their value is
43//! an object and an offset rather than a number, because nothing knows where the object is
44//! until the linker puts it somewhere. [`Const::Address`] is that pair, and folding one is a
45//! walk down an lvalue adding up member offsets and scaled subscripts rather than a walk over
46//! values, which is why it is a second function and not another arm.
47//!
48//! Two of the rules are worth stating because they are not the obvious ones. A pointer with no
49//! object behind it is not an address at all: `(int *)4` folds to four, and so does `(int *)4 +
50//! 1` once the scaling is done, which is why an enumerator may be written that way and gcc
51//! accepts it. And an address cast to an integer stays an address only where every bit of it
52//! survives, which is what makes `long n = (long)&a;` a static initializer on a sixty four bit
53//! target and `int n = (int)&a;` not one, exactly as gcc has it.
54//!
55//! An address is not an integer constant expression, whatever type it is wearing. So an array
56//! bound, a `case` label and an enumerator each go through [`Eval::integer`], which asks for a
57//! number and gets [`NotConstant`] for any of these.
58//!
59//! # What is not here
60//!
61//! Folding happens where a constant is wanted, so an expression nothing asks about is not
62//! folded and the warnings below are not produced for it. `1/0;` as a statement is silent here
63//! and gcc warns, and that closes as more of the compiler asks this for values.
64
65use std::cmp::Ordering;
66
67use rucc_ast::{BinaryOp, UnaryOp};
68use rucc_base::Interner;
69use rucc_base::float::{Float, Format, Status};
70use rucc_diag::Diagnostic;
71use rucc_target::TargetInfo;
72use rucc_types::{IntegerInfo, TypeId, TypeKind, Types, float_format, integer_info, layout, spell};
73
74use crate::decl::{DeclId, StorageDuration};
75use crate::expr::{Classify, Conversion, ExprId, ExprKind, Sign};
76use crate::tast::{Address, Base, Const, Tast};
77
78/// Why an expression is not a constant.
79#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80pub struct NotConstant {
81    /// The node the folding stopped at, which is what a diagnostic should point at. It is the
82    /// subexpression and not the whole thing, so that `case 1 + f():` underlines the call.
83    pub at: ExprId,
84    /// Whether that node had already been diagnosed before the folding reached it.
85    ///
86    /// The poisoning rule of `spec/06-lexer-and-parser.md` section 6.8: a caller says nothing
87    /// about one of these, because something has already been said about the same source. It is
88    /// not the same as the folding having warned, which it does about a division by zero and
89    /// which gcc still follows with the caller's message.
90    pub poisoned: bool,
91}
92
93/// The constant folder, over one typed tree.
94#[derive(Debug)]
95pub struct Eval<'a> {
96    tast: &'a Tast,
97    types: &'a Types,
98    target: &'a TargetInfo,
99    names: &'a Interner,
100    diagnostics: Vec<Diagnostic>,
101}
102
103impl<'a> Eval<'a> {
104    /// A folder over a tree, the types it points into, and the target it is being compiled for.
105    #[must_use]
106    pub fn new(
107        tast: &'a Tast,
108        types: &'a Types,
109        target: &'a TargetInfo,
110        names: &'a Interner,
111    ) -> Eval<'a> {
112        Eval { tast, types, target, names, diagnostics: Vec::new() }
113    }
114
115    /// The value of an expression.
116    ///
117    /// # Errors
118    ///
119    /// [`NotConstant`] when the expression is not one, which is an ordinary answer rather than a
120    /// failure: whether it is a diagnostic depends on where the expression was.
121    pub fn constant(&mut self, expr: ExprId) -> Result<Const, NotConstant> {
122        self.eval(expr)
123    }
124
125    /// The value of an expression that has to be an integer constant expression, 6.6p6.
126    ///
127    /// The type has to be an integer type as well as the value being one, which is what rejects
128    /// `enum { a = nullptr };`: the value folds to zero and the expression is still not an
129    /// integer constant expression.
130    ///
131    /// # Errors
132    ///
133    /// [`NotConstant`] when the expression is not one, or is a constant of some other type.
134    pub fn integer(&mut self, expr: ExprId) -> Result<i128, NotConstant> {
135        let value = self.eval(expr)?;
136        let ty = self.tast[expr].ty;
137        match value {
138            Const::Int(value) if self.int_shape(ty).is_some() => Ok(value),
139            _ => Err(self.stop(expr)),
140        }
141    }
142
143    /// What the folding reported, in the order it was found.
144    #[must_use]
145    pub fn finish(self) -> Vec<Diagnostic> {
146        self.diagnostics
147    }
148
149    /// The value of one node.
150    fn eval(&mut self, expr: ExprId) -> Result<Const, NotConstant> {
151        match self.tast[expr].kind {
152            ExprKind::Error => Err(NotConstant { at: expr, poisoned: true }),
153            ExprKind::Const(value) => Ok(self.tast[value]),
154            // The address of an lvalue, which is the one operator whose operand is not folded
155            // to a value first, because an lvalue does not have one.
156            ExprKind::Unary { op: UnaryOp::AddrOf, operand } => {
157                Ok(Const::Address(self.place(operand)?))
158            }
159            ExprKind::Unary { op, operand } => self.unary(expr, op, operand),
160            ExprKind::Binary { op, lhs, rhs } => self.binary(expr, op, lhs, rhs),
161            ExprKind::Cond { cond, then, otherwise } => {
162                // Only the arm that is taken is folded. `1 ? 2 : f()` is a constant and so is
163                // `0 && f()` below, which is 6.6p3 saying the operands of an unevaluated
164                // subexpression do not have to be constants and is what both compilers do.
165                let cond = self.eval(cond)?;
166                let taken = if truth(cond) { then } else { otherwise };
167                self.eval(taken)
168            }
169            ExprKind::Classify { op, lhs, rhs } => self.classify(expr, op, lhs, rhs),
170            ExprKind::Sign { op, lhs, rhs } => self.sign(expr, op, lhs, rhs),
171            ExprKind::Cast(operand) => self.convert(expr, operand),
172            ExprKind::Convert {
173                kind: Conversion::Arithmetic | Conversion::Bool | Conversion::Pointer,
174                operand,
175            } => self.convert(expr, operand),
176            // An array or a function becoming a pointer is the address of the thing itself,
177            // which is why these two go to the lvalue walk rather than to a value.
178            ExprKind::Convert {
179                kind: Conversion::ArrayDecay | Conversion::FunctionDecay,
180                operand,
181            } => Ok(Const::Address(self.place(operand)?)),
182            // A null pointer constant keeps the value it had, since the whole point of the
183            // conversion is that the value was already zero.
184            ExprKind::Convert { kind: Conversion::NullPointer, operand } => self.eval(operand),
185            // Reading an object, which is not a constant however `const` the object is:
186            // `const int n = 1; int a[n];` is a variable length array in C, and it is this arm
187            // that makes it one. A named constant is the exception C23 added and the reason
188            // `constexpr` is a keyword rather than a promise.
189            ExprKind::Convert { kind: Conversion::Lvalue, operand } => {
190                match self.named_constant(operand) {
191                    Some(value) => Ok(value),
192                    None => Err(self.stop(expr)),
193                }
194            }
195            // What is left is a value being discarded, a call, an assignment, a comma, a
196            // statement expression, a label address, and an lvalue with no `&` in front of it.
197            // The comma is the interesting one: it is a constant nowhere, by 6.6p3, and
198            // `enum { a = (1, 2) };` is an error in gcc rather than a two.
199            _ => Err(self.stop(expr)),
200        }
201    }
202
203    /// One of the floating point classification builtins.
204    ///
205    /// Every one of them is a question about a value, so every one of them has an answer as soon
206    /// as the value is a constant, and gcc answers them in its front end too. What that buys is
207    /// `int flag = __builtin_isinf(1.0 / 0.0);` at file scope, which is an initializer for an
208    /// object with static storage duration and has to have a value here or the program is
209    /// refused rather than merely compiled slowly.
210    fn classify(
211        &mut self,
212        expr: ExprId,
213        op: Classify,
214        lhs: ExprId,
215        rhs: Option<ExprId>,
216    ) -> Result<Const, NotConstant> {
217        let Const::Float(left) = self.eval(lhs)? else {
218            return Err(self.stop(expr));
219        };
220        let answer = match op {
221            Classify::Nan => left.is_nan(),
222            Classify::Infinite => !left.is_finite() && !left.is_nan(),
223            Classify::Finite => left.is_finite(),
224            // The sign and not the value, so a negative zero answers yes where `x < 0` would
225            // answer no.
226            Classify::SignBit => left.is_negative(),
227            Classify::Unordered | Classify::LessGreater => {
228                let Some(rhs) = rhs else { return Err(self.stop(expr)) };
229                let Const::Float(right) = self.eval(rhs)? else {
230                    return Err(self.stop(expr));
231                };
232                let order = left.compare(right);
233                match op {
234                    Classify::Unordered => order.is_none(),
235                    _ => matches!(order, Some(Ordering::Less | Ordering::Greater)),
236                }
237            }
238        };
239        Ok(Const::Int(i128::from(answer)))
240    }
241
242    /// `fabs` or `copysign` of constants, which is the sign bit of the answer and nothing else.
243    ///
244    /// Neither of them rounds and neither has a case it cannot answer, so both fold wherever the
245    /// operands do. A static initializer written with one is the reason it matters: `static const
246    /// double lo = __builtin_copysign(0.0, -1.0);` has to have a value at translation time, and
247    /// the value is a negative zero, which is not something the negation of a literal gives.
248    fn sign(
249        &mut self,
250        expr: ExprId,
251        op: Sign,
252        lhs: ExprId,
253        rhs: Option<ExprId>,
254    ) -> Result<Const, NotConstant> {
255        let Const::Float(left) = self.eval(lhs)? else {
256            return Err(self.stop(expr));
257        };
258        let sign = match op {
259            Sign::Clear => false,
260            Sign::Of => {
261                let Some(rhs) = rhs else { return Err(self.stop(expr)) };
262                let Const::Float(right) = self.eval(rhs)? else {
263                    return Err(self.stop(expr));
264                };
265                right.is_negative()
266            }
267        };
268        Ok(Const::Float(left.with_sign(sign)))
269    }
270
271    /// A prefix operator applied to a folded operand.
272    fn unary(&mut self, expr: ExprId, op: UnaryOp, operand: ExprId) -> Result<Const, NotConstant> {
273        let value = self.eval(operand)?;
274        match (op, value) {
275            (UnaryOp::Plus, value) => Ok(value),
276            (UnaryOp::Not, value) => Ok(Const::Int(i128::from(!truth(value)))),
277            // `__real__` of a real operand is the operand, and `__imag__` of one is a zero of
278            // the same type. The complex cases cannot arrive: there is no complex constant for
279            // the operand to have folded to, so it fails above.
280            (UnaryOp::Real, value) => Ok(value),
281            (UnaryOp::Imag, _) => self.zero(expr),
282            (UnaryOp::Minus, Const::Float(value)) => Ok(Const::Float(value.negated())),
283            (UnaryOp::Minus | UnaryOp::BitNot, Const::Int(value)) => {
284                let Some(info) = self.int_shape(self.tast[operand].ty) else {
285                    return Err(self.stop(expr));
286                };
287                if matches!(op, UnaryOp::BitNot) {
288                    return Ok(Const::Int(info.wrap(!value)));
289                }
290                // The only negation that overflows is of the least value, whose negative is one
291                // past the greatest. gcc warns and wraps, and wrapping is what the hardware
292                // does with the same bits.
293                let negated = info.wrap(value.wrapping_neg());
294                if info.signed && value == least(info) {
295                    self.overflow(expr, negated);
296                }
297                Ok(Const::Int(negated))
298            }
299            // A dereference, an address, an increment or a decrement. None of them is a
300            // constant, and the last two are not even allowed to appear in one.
301            _ => Err(self.stop(expr)),
302        }
303    }
304
305    /// A binary operator applied to folded operands.
306    fn binary(
307        &mut self,
308        expr: ExprId,
309        op: BinaryOp,
310        lhs: ExprId,
311        rhs: ExprId,
312    ) -> Result<Const, NotConstant> {
313        match op {
314            BinaryOp::LogAnd | BinaryOp::LogOr => {
315                let wanted = matches!(op, BinaryOp::LogOr);
316                let left = self.eval(lhs)?;
317                if truth(left) == wanted {
318                    return Ok(Const::Int(i128::from(wanted)));
319                }
320                let right = self.eval(rhs)?;
321                Ok(Const::Int(i128::from(truth(right))))
322            }
323            BinaryOp::Shl | BinaryOp::Shr => self.shift(expr, op, lhs, rhs),
324            _ => {
325                let left = self.eval(lhs)?;
326                let right = self.eval(rhs)?;
327                if self.pointee_size(self.tast[lhs].ty).is_some()
328                    || self.pointee_size(self.tast[rhs].ty).is_some()
329                {
330                    return self.pointer_binary(expr, op, lhs, rhs, left, right);
331                }
332                match (left, right) {
333                    (Const::Int(left), Const::Int(right)) => {
334                        // The signedness and the width come from an operand and not from the
335                        // node, because a comparison has type `int` however wide the things it
336                        // compared were.
337                        let Some(info) = self.int_shape(self.tast[lhs].ty) else {
338                            return Err(self.stop(expr));
339                        };
340                        self.int_binary(expr, op, left, right, info)
341                    }
342                    (Const::Float(left), Const::Float(right)) => {
343                        self.float_binary(expr, op, left, right)
344                    }
345                    // The two operands of an arithmetic operator have one type by the time they
346                    // are here, so a mismatched pair is pointer arithmetic or a tree that did
347                    // not check. Neither has a value to give.
348                    _ => Err(self.stop(expr)),
349                }
350            }
351        }
352    }
353
354    /// A binary operator on two integers of the same type.
355    fn int_binary(
356        &mut self,
357        expr: ExprId,
358        op: BinaryOp,
359        left: i128,
360        right: i128,
361        info: IntegerInfo,
362    ) -> Result<Const, NotConstant> {
363        if let Some(ordering) = compare_int(op, left, right, info) {
364            return Ok(Const::Int(i128::from(ordering)));
365        }
366        let value = match op {
367            BinaryOp::BitAnd => left & right,
368            BinaryOp::BitOr => left | right,
369            BinaryOp::BitXor => left ^ right,
370            BinaryOp::Div | BinaryOp::Rem if right == 0 => {
371                // A warning and not an error, because that is what gcc calls it, and then no
372                // value, because there is not one. The caller adds what the context calls it.
373                self.warn(expr, "division by zero", "E0521");
374                return Err(NotConstant { at: expr, poisoned: false });
375            }
376            BinaryOp::Add | BinaryOp::Sub | BinaryOp::Mul | BinaryOp::Div | BinaryOp::Rem => {
377                return self.arithmetic(expr, op, left, right, info);
378            }
379            // The shifts and the logical operators went elsewhere, and the comparisons were
380            // answered above, so what is left is an operator with no meaning on two integers.
381            _ => return Err(self.stop(expr)),
382        };
383        Ok(Const::Int(info.wrap(value)))
384    }
385
386    /// The four operations that can leave the range of the type they happened in, and `%`.
387    fn arithmetic(
388        &mut self,
389        expr: ExprId,
390        op: BinaryOp,
391        left: i128,
392        right: i128,
393        info: IntegerInfo,
394    ) -> Result<Const, NotConstant> {
395        if !info.signed {
396            let (left, right) = (left as u128, right as u128);
397            let value = match op {
398                BinaryOp::Add => left.wrapping_add(right),
399                BinaryOp::Sub => left.wrapping_sub(right),
400                BinaryOp::Mul => left.wrapping_mul(right),
401                BinaryOp::Div => left / right,
402                _ => left % right,
403            };
404            return Ok(Const::Int(info.wrap(value as i128)));
405        }
406        let (exact, wrapped) = match op {
407            BinaryOp::Add => (left.checked_add(right), left.wrapping_add(right)),
408            BinaryOp::Sub => (left.checked_sub(right), left.wrapping_sub(right)),
409            BinaryOp::Mul => (left.checked_mul(right), left.wrapping_mul(right)),
410            BinaryOp::Div => (left.checked_div(right), left.wrapping_div(right)),
411            _ => (left.checked_rem(right), left.wrapping_rem(right)),
412        };
413        let value = info.wrap(wrapped);
414        // The least value divided by minus one is the one signed division that leaves the range,
415        // and gcc calls the remainder of the same pair an overflow too. It is right to: the
416        // remainder is zero and the instruction that computes it traps exactly as the quotient
417        // does, so a program that reaches either has the same problem.
418        let extreme =
419            matches!(op, BinaryOp::Div | BinaryOp::Rem) && right == -1 && left == least(info);
420        if extreme || exact.is_none_or(|exact| !info.holds(exact)) {
421            self.overflow(expr, value);
422        }
423        Ok(Const::Int(value))
424    }
425
426    /// A binary operator on two floating values of the same format.
427    fn float_binary(
428        &mut self,
429        expr: ExprId,
430        op: BinaryOp,
431        left: Float,
432        right: Float,
433    ) -> Result<Const, NotConstant> {
434        if let Some(ordering) = compare_float(op, left, right) {
435            return Ok(Const::Int(i128::from(ordering)));
436        }
437        // The status is dropped on purpose. Overflowing to an infinity and dropping a digit are
438        // both things a constant is allowed to do and neither compiler says a word about either.
439        let (value, _) = match op {
440            BinaryOp::Add => left.sum(right),
441            BinaryOp::Sub => left.difference(right),
442            BinaryOp::Mul => left.product(right),
443            BinaryOp::Div => left.quotient(right),
444            // `%` and the bitwise operators have no floating operands, so a tree with one here
445            // did not check.
446            _ => return Err(self.stop(expr)),
447        };
448        Ok(Const::Float(value))
449    }
450
451    /// `<<` or `>>`, whose operands have their own types and whose result has the left one's.
452    fn shift(
453        &mut self,
454        expr: ExprId,
455        op: BinaryOp,
456        lhs: ExprId,
457        rhs: ExprId,
458    ) -> Result<Const, NotConstant> {
459        let left = self.eval(lhs)?;
460        let right = self.eval(rhs)?;
461        let (Const::Int(value), Const::Int(count)) = (left, right) else {
462            return Err(self.stop(expr));
463        };
464        let (Some(info), Some(counts)) =
465            (self.int_shape(self.tast[lhs].ty), self.int_shape(self.tast[rhs].ty))
466        else {
467            return Err(self.stop(expr));
468        };
469        let side = if matches!(op, BinaryOp::Shl) { "left" } else { "right" };
470        if counts.signed && count < 0 {
471            self.warn(expr, format!("{side} shift count is negative"), "E0522");
472            return Err(NotConstant { at: expr, poisoned: false });
473        }
474        // Negative counts are gone, so the bits are the magnitude whichever type they came from,
475        // which is what makes a hundred and twenty eight bit unsigned count compare correctly.
476        let count = count as u128;
477        if count >= u128::from(info.width) {
478            self.warn(expr, format!("{side} shift count >= width of type"), "E0523");
479            // gcc still gives it a value, and the value is what shifting the whole width away
480            // leaves: nothing, or the sign repeated when the shift was an arithmetic right one.
481            let sign = matches!(op, BinaryOp::Shr) && info.signed && value < 0;
482            return Ok(Const::Int(if sign { -1 } else { 0 }));
483        }
484        let count = count as u32;
485        let value = match (op, info.signed) {
486            (BinaryOp::Shr, true) => value >> count,
487            (BinaryOp::Shr, false) => ((value as u128) >> count) as i128,
488            // A left shift out of the range of a signed type is undefined in C and gcc folds it
489            // without a word, which is the sensible answer: `1 << 31` is how a person writes the
490            // sign bit and warning about it would be noise in every real program.
491            _ => value.wrapping_shl(count),
492        };
493        Ok(Const::Int(info.wrap(value)))
494    }
495
496    /// The value read out of a named constant, and [`None`] when the object is not one.
497    ///
498    /// C23 6.6p8 lists what an integer constant expression may be built out of, and a named
499    /// constant is on it twice: one of an arithmetic type, and a member of one of a structure
500    /// or union type. A subscript is not on the list, so
501    /// `constexpr int a[3] = { 1, 2, 3 }; int n[a[1]];` is a variably modified type, which is
502    /// what gcc 16 makes of it as well, and the walk here goes through members only.
503    ///
504    /// The value comes out of the initializer rather than out of anything kept beside it, since
505    /// a named constant has one by definition and it has already been folded: C23 requires the
506    /// initializer of a `constexpr` object to be a constant expression, so whatever is at the
507    /// offset is a value and not an expression to evaluate a second time.
508    fn named_constant(&mut self, expr: ExprId) -> Option<Const> {
509        let (decl, offset) = self.designation(expr)?;
510        let node = &self.tast[decl];
511        if !node.constant {
512            return None;
513        }
514        let entries = self.tast[node.init?].to_vec();
515        let entry = entries.iter().find(|entry| entry.offset == offset && entry.bit_offset == 0)?;
516        // A member the initializer did not reach holds a zero, which is what the contract on
517        // an initializer list says: the object starts as zero and the entries are applied to it.
518        self.eval(entry.value).ok()
519    }
520
521    /// The object a designation names and the byte offset into it, through members only.
522    fn designation(&mut self, expr: ExprId) -> Option<(DeclId, u64)> {
523        match self.tast[expr].kind {
524            ExprKind::Decl(decl) => Some((decl, 0)),
525            ExprKind::Member { base, field } => {
526                let (decl, offset) = self.designation(base)?;
527                let TypeKind::Record(record) = bare(self.types, self.tast[base].ty) else {
528                    return None;
529                };
530                let field = self.types.record_info(record).fields.get(field as usize).copied()?;
531                Some((decl, offset.checked_add(field.offset)?))
532            }
533            _ => None,
534        }
535    }
536
537    /// The address of an lvalue, walked down rather than folded up.
538    ///
539    /// This is the half of the folding that does not have values to work with. A member adds its
540    /// own offset to whatever holds it and a subscript adds its index scaled by the element, so
541    /// what comes out is the object at the bottom and the distance travelled to reach it.
542    fn place(&mut self, expr: ExprId) -> Result<Address, NotConstant> {
543        match self.tast[expr].kind {
544            ExprKind::Error => Err(NotConstant { at: expr, poisoned: true }),
545            // An automatic object has no address until the frame holding it exists, so it is
546            // not a constant, and neither is a parameter for the same reason.
547            ExprKind::Decl(decl) | ExprKind::CompoundLiteral(decl)
548                if self.tast[decl].duration != StorageDuration::Automatic =>
549            {
550                Ok(Address { base: Base::Decl(decl), offset: 0 })
551            }
552            ExprKind::Str(id) => Ok(Address { base: Base::Str(id), offset: 0 }),
553            ExprKind::Member { base, field } => {
554                let mut address = self.place(base)?;
555                let TypeKind::Record(record) = bare(self.types, self.tast[base].ty) else {
556                    return Err(self.stop(expr));
557                };
558                let Some(field) =
559                    self.types.record_info(record).fields.get(field as usize).copied()
560                else {
561                    return Err(self.stop(expr));
562                };
563                address.offset += i128::from(field.offset);
564                Ok(address)
565            }
566            ExprKind::Subscript { base, index } => {
567                let base = self.eval(base)?;
568                let Const::Int(index) = self.eval(index)? else { return Err(self.stop(expr)) };
569                let size = i128::from(self.size_of(self.tast[expr].ty));
570                let Const::Address(mut address) = base else { return Err(self.stop(expr)) };
571                address.offset += index.wrapping_mul(size);
572                Ok(address)
573            }
574            // `&*p` is `p`, which is what makes `int *q = &*a;` a constant and is not a
575            // simplification: the dereference of an address constant is the object it names.
576            ExprKind::Unary { op: UnaryOp::Deref, operand } => match self.eval(operand)? {
577                Const::Address(address) => Ok(address),
578                _ => Err(self.stop(expr)),
579            },
580            _ => Err(self.stop(expr)),
581        }
582    }
583
584    /// An operator with a pointer on at least one side.
585    ///
586    /// Which side the pointer is on is read off the types rather than off the folded values,
587    /// because `(int *)4` folds to a number and is still a pointer, and the scaling that
588    /// `p + 1` does is decided by what `p` points at and not by what it happened to fold to.
589    fn pointer_binary(
590        &mut self,
591        expr: ExprId,
592        op: BinaryOp,
593        lhs: ExprId,
594        rhs: ExprId,
595        left: Const,
596        right: Const,
597    ) -> Result<Const, NotConstant> {
598        let (left_step, right_step) =
599            (self.pointee_size(self.tast[lhs].ty), self.pointee_size(self.tast[rhs].ty));
600        match (op, left_step, right_step) {
601            (BinaryOp::Add, Some(step), None) => self.offset_by(expr, left, right, step),
602            (BinaryOp::Add, None, Some(step)) => self.offset_by(expr, right, left, step),
603            (BinaryOp::Sub, Some(step), None) => self.offset_by(expr, left, negate(right), step),
604            // A difference of two pointers, which is a number and not an address however far
605            // from home the two are. It needs the same object under both, since the distance
606            // between two objects is not decided until they are placed.
607            (BinaryOp::Sub, Some(step), Some(_)) if step != 0 => {
608                let distance = match (left, right) {
609                    (Const::Address(left), Const::Address(right)) if left.base == right.base => {
610                        left.offset - right.offset
611                    }
612                    (Const::Int(left), Const::Int(right)) => left - right,
613                    _ => return Err(self.stop(expr)),
614                };
615                Ok(Const::Int(distance / i128::from(step)))
616            }
617            (_, Some(_), _) | (_, _, Some(_)) => self.pointer_compare(expr, op, left, right),
618            _ => Err(self.stop(expr)),
619        }
620    }
621
622    /// A pointer moved by a number of elements, whichever kind of pointer it folded to.
623    fn offset_by(
624        &mut self,
625        expr: ExprId,
626        pointer: Const,
627        count: Const,
628        step: u64,
629    ) -> Result<Const, NotConstant> {
630        let Const::Int(count) = count else { return Err(self.stop(expr)) };
631        let distance = count.wrapping_mul(i128::from(step));
632        match pointer {
633            Const::Address(address) => Ok(Const::Address(Address {
634                base: address.base,
635                offset: address.offset.wrapping_add(distance),
636            })),
637            Const::Int(value) => Ok(Const::Int(value.wrapping_add(distance))),
638            Const::Float(_) => Err(self.stop(expr)),
639        }
640    }
641
642    /// A comparison with a pointer on at least one side.
643    fn pointer_compare(
644        &mut self,
645        expr: ExprId,
646        op: BinaryOp,
647        left: Const,
648        right: Const,
649    ) -> Result<Const, NotConstant> {
650        let ordering = match (left, right) {
651            (Const::Address(left), Const::Address(right)) if left.base == right.base => {
652                left.offset.cmp(&right.offset)
653            }
654            // Two pointers that are both numbers, which compare as the unsigned values they are.
655            (Const::Int(left), Const::Int(right)) => (left as u128).cmp(&(right as u128)),
656            // An object has an address and a null pointer does not point at one, so the two are
657            // never the same. Which of them was written first does not matter to `==` or `!=`,
658            // and nothing else about the pair can be answered before the object is placed.
659            (Const::Address(_), Const::Int(0)) | (Const::Int(0), Const::Address(_)) => {
660                return match op {
661                    BinaryOp::Eq => Ok(Const::Int(0)),
662                    BinaryOp::Ne => Ok(Const::Int(1)),
663                    _ => Err(self.stop(expr)),
664                };
665            }
666            _ => return Err(self.stop(expr)),
667        };
668        match holds(op, ordering) {
669            Some(value) => Ok(Const::Int(i128::from(value))),
670            None => Err(self.stop(expr)),
671        }
672    }
673
674    /// How far apart two elements of a pointer's target type are, and [`None`] for a non-pointer.
675    ///
676    /// A pointer to `void` or to a function steps by one byte, which is what GNU C says and what
677    /// every program that does arithmetic on a `void *` is written against.
678    fn pointee_size(&self, ty: TypeId) -> Option<u64> {
679        match bare(self.types, ty) {
680            TypeKind::Pointer(target) => Some(match bare(self.types, target) {
681                TypeKind::Void | TypeKind::Function(_) => 1,
682                _ => self.size_of(target),
683            }),
684            _ => None,
685        }
686    }
687
688    /// The size of a type in bytes, and zero for one that has no size to give.
689    fn size_of(&self, ty: TypeId) -> u64 {
690        layout(self.types, ty, self.target).map_or(0, |layout| layout.size)
691    }
692
693    /// A folded value converted to the type of the node it is under.
694    fn convert(&mut self, expr: ExprId, operand: ExprId) -> Result<Const, NotConstant> {
695        let value = self.eval(operand)?;
696        let (from, to) = (self.tast[operand].ty, self.tast[expr].ty);
697        match self.converted(value, from, to) {
698            Some(value) => Ok(value),
699            None => Err(self.stop(expr)),
700        }
701    }
702
703    /// A value converted to `ty`, and [`None`] when `ty` is not one a number converts to.
704    ///
705    /// The conversion that changes the value is the caller's to warn about, not this one's:
706    /// `(char)300` is silent in gcc and `char c = 300;` is not, and both of them come through
707    /// here.
708    fn converted(&self, value: Const, from: TypeId, to: TypeId) -> Option<Const> {
709        match bare(self.types, to) {
710            // Not a truncation to one bit. `(bool)2` is one and `(bool)0.5` is one, which is
711            // why this is a comparison against zero and not the integer case below.
712            TypeKind::Bool => Some(Const::Int(i128::from(truth(value)))),
713            TypeKind::Int(_) | TypeKind::BitInt { .. } | TypeKind::Enum(_) => {
714                let info = self.int_shape(to)?;
715                match value {
716                    Const::Int(value) => Some(Const::Int(info.wrap(value))),
717                    // Out of range is undefined behaviour rather than a value, and what comes
718                    // back is the nearest end of the range with a flag on it. The flag is the
719                    // caller's to warn about and is why this drops it rather than reads it.
720                    Const::Float(value) => {
721                        Some(Const::Int(value.to_integer(info.width, info.signed).0))
722                    }
723                    // An address written as a number is still an address, and it survives only
724                    // where every bit of it does. That is the whole difference between gcc
725                    // taking `long n = (long)&a;` as a static initializer and refusing
726                    // `int n = (int)&a;`, and it is measured in bits and not in names.
727                    Const::Address(address) => (u64::from(info.width) == self.size_of(from) * 8)
728                        .then_some(Const::Address(address)),
729                }
730            }
731            TypeKind::Float(kind) => {
732                let format = float_format(kind, self.target);
733                let (value, _) = match value {
734                    Const::Float(value) => value.to_format(format),
735                    Const::Int(value) => match self.int_shape(from) {
736                        Some(info) if !info.signed => Float::from_unsigned(value as u128, format),
737                        _ => Float::from_signed(value, format),
738                    },
739                    // No cast turns an address into a floating value, so a tree with one here
740                    // did not check.
741                    Const::Address(_) => return None,
742                };
743                Some(Const::Float(value))
744            }
745            // A pointer keeps whatever it was, since a cast between pointer types moves nothing:
746            // an address stays the same address and a number stays the same number.
747            TypeKind::Pointer(_) => match value {
748                Const::Int(_) | Const::Address(_) => Some(value),
749                Const::Float(_) => None,
750            },
751            // `void`, a record, a complex type. None of them has a constant to be.
752            _ => None,
753        }
754    }
755
756    /// A zero of the type of a node, for the `__imag__` of something real.
757    fn zero(&mut self, expr: ExprId) -> Result<Const, NotConstant> {
758        let ty = self.tast[expr].ty;
759        if self.int_shape(ty).is_some() {
760            return Ok(Const::Int(0));
761        }
762        match self.float_shape(ty) {
763            Some(format) => Ok(Const::Float(Float::zero(format, false))),
764            None => Err(self.stop(expr)),
765        }
766    }
767
768    /// The shape of an integer type, over the tree's own types and target.
769    fn int_shape(&self, ty: TypeId) -> Option<IntegerInfo> {
770        int_shape(self.types, ty, self.target)
771    }
772
773    /// The format of a real floating type, and [`None`] for anything else.
774    fn float_shape(&self, ty: TypeId) -> Option<Format> {
775        match bare(self.types, ty) {
776            TypeKind::Float(kind) => Some(float_format(kind, self.target)),
777            _ => None,
778        }
779    }
780
781    /// The answer for a node that is not a constant and that nothing has been said about.
782    fn stop(&self, expr: ExprId) -> NotConstant {
783        NotConstant { at: expr, poisoned: false }
784    }
785
786    /// Warns that an operation left the range of the type it happened in.
787    fn overflow(&mut self, expr: ExprId, value: i128) {
788        let ty = spell(self.types, self.names, self.tast[expr].ty);
789        let message = format!("integer overflow in expression of type '{ty}' results in '{value}'");
790        self.warn(expr, message, "E0524");
791    }
792
793    /// Reports a warning about a node.
794    fn warn(&mut self, expr: ExprId, message: impl Into<String>, code: &'static str) {
795        let span = self.tast.expr_span(expr);
796        self.diagnostics.push(Diagnostic::warning(message.into(), span).with_code(code));
797    }
798}
799
800/// The shape of an integer type, and [`None`] for anything a folded constant cannot hold.
801///
802/// A `_BitInt` wider than a hundred and twenty eight bits is the one integer type in that second
803/// group. It is refused where it is written rather than folded to a wrong answer here.
804pub(crate) fn int_shape(types: &Types, ty: TypeId, target: &TargetInfo) -> Option<IntegerInfo> {
805    let info = integer_info(types, ty, target)?;
806    (info.width > 0 && info.width <= 128).then_some(info)
807}
808
809/// Whether a constant is true, which is a comparison against zero and not a look at the bits.
810///
811/// A nan is true, because it is not equal to zero, and so is a negative zero's negation of
812/// itself: the test is `!= 0` and `-0.0 == 0.0`.
813fn truth(value: Const) -> bool {
814    match value {
815        Const::Int(value) => value != 0,
816        Const::Float(value) => !value.is_zero(),
817        // An object has an address and no object is at zero, so an address is always true.
818        Const::Address(_) => true,
819    }
820}
821
822/// A folded integer negated, for the `p - n` that is written as an offset of minus `n`.
823fn negate(value: Const) -> Const {
824    match value {
825        Const::Int(value) => Const::Int(value.wrapping_neg()),
826        other => other,
827    }
828}
829
830/// The result of a comparison of two integers, and [`None`] when `op` is not a comparison.
831fn compare_int(op: BinaryOp, left: i128, right: i128, info: IntegerInfo) -> Option<bool> {
832    let ordering = if info.signed {
833        left.cmp(&right)
834    } else {
835        // The bits are the value for an unsigned type of any width, including the hundred and
836        // twenty eight bit one whose top bit is sitting in the sign of the `i128`.
837        (left as u128).cmp(&(right as u128))
838    };
839    holds(op, ordering)
840}
841
842/// The result of a comparison of two floating values, and [`None`] when `op` is not one.
843fn compare_float(op: BinaryOp, left: Float, right: Float) -> Option<bool> {
844    match left.compare(right) {
845        Some(ordering) => holds(op, ordering),
846        // Unordered, so one of them is a nan. Every comparison against one is false except the
847        // inequality, which is the whole of why `x != x` is the test for a nan. The ordering
848        // asked about first is only there to answer whether `op` is a comparison at all.
849        None if holds(op, Ordering::Equal).is_some() => Some(matches!(op, BinaryOp::Ne)),
850        None => None,
851    }
852}
853
854/// Whether an ordering satisfies a comparison operator, and [`None`] for anything else.
855fn holds(op: BinaryOp, ordering: Ordering) -> Option<bool> {
856    Some(match op {
857        BinaryOp::Lt => ordering.is_lt(),
858        BinaryOp::Gt => ordering.is_gt(),
859        BinaryOp::Le => ordering.is_le(),
860        BinaryOp::Ge => ordering.is_ge(),
861        BinaryOp::Eq => ordering.is_eq(),
862        BinaryOp::Ne => ordering.is_ne(),
863        _ => return None,
864    })
865}
866
867/// The least value a signed type of this shape holds, which is meaningless for an unsigned one.
868fn least(info: IntegerInfo) -> i128 {
869    info.wrap(1i128 << info.width.saturating_sub(1))
870}
871
872/// What a type is once the sugar, the qualifiers and `_Atomic` are off it.
873///
874/// The same peel `rucc_types` does behind each of its own predicates, spelled out here because
875/// this needs the kind itself rather than an answer about it.
876pub(crate) fn bare(types: &Types, ty: TypeId) -> TypeKind {
877    match types.kind(types.canonical(ty)) {
878        TypeKind::Atomic(inner) => types.kind(types.canonical(inner)),
879        other => other,
880    }
881}
882
883/// A folded integer as a diagnostic writes it, which needs the type to know whether the top bit
884/// is a sign or a digit.
885pub(crate) fn spell_int(value: i128, info: IntegerInfo) -> String {
886    if info.signed { format!("{value}") } else { format!("{}", value as u128) }
887}
888
889/// A folded value stored in an integer type of this shape, which is what the conversion leaves.
890pub(crate) fn narrowed(value: Const, info: IntegerInfo) -> i128 {
891    match value {
892        Const::Int(value) => info.wrap(value),
893        Const::Float(value) => value.to_integer(info.width, info.signed).0,
894        // Nothing narrows an address, since the caller asked for a number and got one of these
895        // instead. Zero is a value it will not use.
896        Const::Address(_) => 0,
897    }
898}
899
900/// A folded constant as a diagnostic writes it.
901///
902/// A floating value is written in hexadecimal, which is the one place the wording here is not
903/// gcc's. gcc prints `1.0e+40` and printing that needs a binary to decimal conversion that this
904/// compiler does not have yet, and `0x1.d6329f1c35ca5p+132` is at least the same number.
905pub(crate) fn spell_const(value: Const, info: Option<IntegerInfo>) -> String {
906    match value {
907        Const::Int(value) => match info {
908            Some(info) => spell_int(value, info),
909            None => format!("{value}"),
910        },
911        Const::Float(value) => value.to_hex(),
912        Const::Address(address) => {
913            let base = match address.base {
914                Base::Decl(decl) => decl.index(),
915                Base::Str(id) => id.index(),
916            };
917            format!("&#{base} + {}", address.offset)
918        }
919    }
920}
921
922/// Whether converting a folded value to a type of this shape changes it, gcc's `-Woverflow`.
923///
924/// The rule is not the obvious one and is worth stating. `signed char c = 200;` and `unsigned
925/// char u = -1;` both change the value and gcc warns about neither, because in each the bits are
926/// all there and it is only the sign that moved, which is a different option's business.
927/// `unsigned char u = 300;` is warned about, because three hundred does not fit in eight bits
928/// whichever way round they are read. So the question is whether the value fits in neither
929/// signedness of the target's width.
930pub(crate) fn overflows(value: Const, info: IntegerInfo) -> bool {
931    match value {
932        Const::Int(value) => {
933            !IntegerInfo::new(true, info.width).holds(value)
934                && !IntegerInfo::new(false, info.width).holds(value)
935        }
936        // A conversion that had to saturate, which is the flag the float arithmetic raises for
937        // a value out of range and for a nan. Dropping a fraction is not overflow and gcc does
938        // not warn about `char c = 3.5;` either.
939        Const::Float(value) => value.to_integer(info.width, info.signed).1.has(Status::INVALID),
940        // An address is as wide as a pointer or it would not have got this far, so nothing about
941        // it is lost.
942        Const::Address(_) => false,
943    }
944}
945
946#[cfg(test)]
947mod tests {
948    use rucc_ast as ast;
949    use rucc_ast::{
950        ArraySize, AttrList, Builtin, BuiltinSet, DeclSpecs, Declarator, Derived, Quals,
951        StorageClass, TypeSpec,
952    };
953    use rucc_base::Symbol;
954    use rucc_base::float::Format;
955    use rucc_diag::Span;
956    use rucc_lex::{
957        Encoding, FloatConstant, FloatConstantType, IntConstant, IntConstantType, Remarks,
958        StringLiteral,
959    };
960    use rucc_session::Std;
961    use rucc_target::{TargetInfo, Triple};
962    use rucc_types::IntKind;
963
964    use super::*;
965    use crate::check::{Checker, Context};
966
967    /// The untyped tree a test folds, built by hand.
968    ///
969    /// The same shape as the one the checking tests use and for the same reason: the checker
970    /// borrows the interner for as long as it lives, so everything a test needs to name is
971    /// named before the checker exists.
972    struct Fixture {
973        ast: ast::Ast,
974        names: Interner,
975        target: TargetInfo,
976    }
977
978    impl Fixture {
979        fn new() -> Fixture {
980            let target =
981                TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().expect("a triple"));
982            Fixture { ast: ast::Ast::new(), names: Interner::new(), target }
983        }
984
985        fn expr(&mut self, expr: ast::Expr) -> ast::ExprId {
986            self.ast.expr(expr, Span::DUMMY)
987        }
988
989        fn int(&mut self, value: u128, kind: IntKind) -> ast::ExprId {
990            let ty = IntConstantType::Standard(kind);
991            let id = self.ast.add_int(IntConstant { value, ty, remarks: Remarks::default() });
992            self.expr(ast::Expr::Int(id))
993        }
994
995        /// A constant of a bit precise type, which is the one integer type that does not promote.
996        fn bit_int(&mut self, value: u128, signed: bool, width: u32) -> ast::ExprId {
997            let ty = IntConstantType::BitInt { signed, width };
998            let id = self.ast.add_int(IntConstant { value, ty, remarks: Remarks::default() });
999            self.expr(ast::Expr::Int(id))
1000        }
1001
1002        fn double(&mut self, text: &str) -> ast::ExprId {
1003            let (value, _) = Float::parse(text, Format::Double).expect("a float");
1004            let constant = FloatConstant {
1005                value,
1006                ty: FloatConstantType::Double,
1007                imaginary: false,
1008                remarks: Remarks::default(),
1009            };
1010            let id = self.ast.add_float(constant);
1011            self.expr(ast::Expr::Float(id))
1012        }
1013
1014        fn binary(&mut self, op: BinaryOp, lhs: ast::ExprId, rhs: ast::ExprId) -> ast::ExprId {
1015            self.expr(ast::Expr::Binary { op, lhs, rhs })
1016        }
1017
1018        fn unary(&mut self, op: UnaryOp, operand: ast::ExprId) -> ast::ExprId {
1019            self.expr(ast::Expr::Unary { op, operand })
1020        }
1021
1022        fn name(&mut self, text: &str) -> Symbol {
1023            self.names.intern(text)
1024        }
1025
1026        fn use_name(&mut self, text: &str) -> ast::ExprId {
1027            let name = self.name(text);
1028            self.expr(ast::Expr::Name(name))
1029        }
1030
1031        fn string(&mut self, text: &str) -> ast::ExprId {
1032            let elements = text.chars().map(|c| c as u32).collect();
1033            let id = self.ast.add_string(StringLiteral {
1034                elements,
1035                encoding: Encoding::Plain,
1036                remarks: Remarks::default(),
1037            });
1038            self.expr(ast::Expr::Str(id))
1039        }
1040
1041        fn subscript(&mut self, base: ast::ExprId, index: ast::ExprId) -> ast::ExprId {
1042            self.expr(ast::Expr::Index { base, index })
1043        }
1044
1045        fn member(&mut self, base: ast::ExprId, field: &str) -> ast::ExprId {
1046            let name = self.name(field);
1047            self.expr(ast::Expr::Member { base, name, arrow: false })
1048        }
1049
1050        /// One member of a record.
1051        fn field(&mut self, specs: DeclSpecs, name: &str) -> ast::Member {
1052            let declarator = Some(self.declarator(Some(name), &[]));
1053            let specs = self.ast.add_specs(specs);
1054            ast::Member::Field(ast::Field {
1055                specs,
1056                declarator,
1057                bits: None,
1058                attrs: AttrList::EMPTY,
1059                span: Span::DUMMY,
1060            })
1061        }
1062
1063        /// `struct S { ... }`, as a specifier list.
1064        fn record(&mut self, tag: &str, members: &[ast::Member]) -> DeclSpecs {
1065            let tag = Some(self.name(tag));
1066            let fields = Some(self.ast.add_member_list(members));
1067            let mut specs = DeclSpecs::empty(Span::DUMMY);
1068            specs.ty = TypeSpec::Record {
1069                kind: ast::RecordKind::Struct,
1070                tag,
1071                fields,
1072                attrs: AttrList::EMPTY,
1073                pack: None,
1074            };
1075            specs
1076        }
1077
1078        fn cast(
1079            &mut self,
1080            specs: DeclSpecs,
1081            derived: &[Derived],
1082            operand: ast::ExprId,
1083        ) -> ast::ExprId {
1084            let ty = self.type_name(specs, derived);
1085            self.expr(ast::Expr::Cast { ty, operand })
1086        }
1087
1088        /// `int`, as a specifier list a test can add words to.
1089        fn int_specs(&self) -> DeclSpecs {
1090            self.builtin(BuiltinSet::INT)
1091        }
1092
1093        fn builtin(&self, keyword: BuiltinSet) -> DeclSpecs {
1094            let mut specs = DeclSpecs::empty(Span::DUMMY);
1095            let builtin = Builtin::NONE.add(keyword).expect("a keyword written once");
1096            specs.ty = TypeSpec::Builtin(builtin);
1097            specs
1098        }
1099
1100        fn type_name(&mut self, specs: DeclSpecs, derived: &[Derived]) -> ast::TypeNameId {
1101            let declarator = self.declarator(None, derived);
1102            let specs = self.ast.add_specs(specs);
1103            self.ast.add_type_name(ast::TypeName { specs, declarator, span: Span::DUMMY })
1104        }
1105
1106        fn declarator(&mut self, name: Option<&str>, derived: &[Derived]) -> ast::DeclaratorId {
1107            let name = name.map(|name| self.name(name));
1108            let derived = self.ast.add_derived_list(derived);
1109            self.ast.add_declarator(Declarator {
1110                name,
1111                name_span: Span::DUMMY,
1112                derived,
1113                span: Span::DUMMY,
1114            })
1115        }
1116
1117        /// A declaration of one name, which is what an address needs an object to be.
1118        fn var(&mut self, specs: DeclSpecs, name: &str, derived: &[Derived]) -> ast::DeclId {
1119            let declarator = self.declarator(Some(name), derived);
1120            let item = ast::InitDeclarator {
1121                declarator,
1122                init: None,
1123                asm_label: None,
1124                attrs: AttrList::EMPTY,
1125                span: Span::DUMMY,
1126            };
1127            let declarators = self.ast.add_init_declarator_list(&[item]);
1128            let specs = self.ast.add_specs(specs);
1129            self.ast.decl(ast::Decl::Var { specs, declarators }, Span::DUMMY)
1130        }
1131
1132        fn checker(&self) -> Checker<'_> {
1133            Checker::new(&self.ast, Context::new(&self.names, &self.target, Std::C23))
1134        }
1135    }
1136
1137    /// `[n]`, with a fixed bound.
1138    fn array(size: ast::ExprId) -> Derived {
1139        Derived::Array { size: ArraySize::Expr(size), quals: Quals::NONE, has_static: false }
1140    }
1141
1142    /// `*`.
1143    fn pointer() -> Derived {
1144        Derived::Pointer { quals: Quals::NONE, attrs: AttrList::EMPTY }
1145    }
1146
1147    /// What an expression folds to, whatever kind of constant that is.
1148    fn value(checker: &mut Checker<'_>, expr: ast::ExprId) -> Result<Const, NotConstant> {
1149        let id = checker.check_expr(expr);
1150        checker.eval_constant(id)
1151    }
1152
1153    /// The object an address constant is into, and how far.
1154    fn address(value: Result<Const, NotConstant>) -> Option<(usize, i128)> {
1155        match value {
1156            Ok(Const::Address(address)) => {
1157                let base = match address.base {
1158                    Base::Decl(decl) => decl.index(),
1159                    Base::Str(id) => id.index(),
1160                };
1161                Some((base, address.offset))
1162            }
1163            _ => None,
1164        }
1165    }
1166
1167    /// The integer one expression folds to, checking it first the way the compiler would.
1168    fn fold(checker: &mut Checker<'_>, expr: ast::ExprId) -> Result<i128, NotConstant> {
1169        let id = checker.check_expr(expr);
1170        checker.eval_integer(id)
1171    }
1172
1173    /// What was reported, as the messages alone.
1174    fn messages(checker: &Checker<'_>) -> Vec<String> {
1175        checker.errors.diagnostics().iter().map(|d| d.message.clone()).collect()
1176    }
1177
1178    #[test]
1179    fn the_address_of_a_static_object_is_that_object_and_no_distance() {
1180        let mut f = Fixture::new();
1181        let object = f.var(f.int_specs(), "a", &[]);
1182        let a = f.use_name("a");
1183        let taken = f.unary(UnaryOp::AddrOf, a);
1184
1185        let mut c = f.checker();
1186        c.check_decl(object);
1187        assert_eq!(address(value(&mut c, taken)), Some((0, 0)));
1188        assert!(messages(&c).is_empty());
1189    }
1190
1191    #[test]
1192    fn a_subscript_and_a_member_add_up_into_one_distance() {
1193        let mut f = Fixture::new();
1194        let x = f.int(4, IntKind::Int);
1195        let object = f.var(f.int_specs(), "a", &[array(x)]);
1196        let a = f.use_name("a");
1197        let two = f.int(2, IntKind::Int);
1198        let element = f.subscript(a, two);
1199        let taken = f.unary(UnaryOp::AddrOf, element);
1200
1201        let mut c = f.checker();
1202        c.check_decl(object);
1203        assert_eq!(
1204            address(value(&mut c, taken)),
1205            Some((0, 8)),
1206            "two elements of four bytes each into the object it started at"
1207        );
1208        assert!(messages(&c).is_empty());
1209    }
1210
1211    #[test]
1212    fn a_member_adds_its_own_offset_to_the_object_that_holds_it() {
1213        let mut f = Fixture::new();
1214        let x = f.field(f.int_specs(), "x");
1215        let y = f.field(f.int_specs(), "y");
1216        let specs = f.record("S", &[x, y]);
1217        let object = f.var(specs, "s", &[]);
1218        let s = f.use_name("s");
1219        let member = f.member(s, "y");
1220        let taken = f.unary(UnaryOp::AddrOf, member);
1221
1222        let mut c = f.checker();
1223        c.check_decl(object);
1224        assert_eq!(address(value(&mut c, taken)), Some((0, 4)));
1225        assert!(messages(&c).is_empty());
1226    }
1227
1228    #[test]
1229    fn a_pointer_moves_by_what_it_points_at_and_not_by_bytes() {
1230        let mut f = Fixture::new();
1231        let four = f.int(4, IntKind::Int);
1232        let object = f.var(f.int_specs(), "a", &[array(four)]);
1233        let a = f.use_name("a");
1234        let three = f.int(3, IntKind::Int);
1235        let moved = f.binary(BinaryOp::Add, a, three);
1236        let a = f.use_name("a");
1237        let one = f.int(1, IntKind::Int);
1238        let back = f.binary(BinaryOp::Sub, a, one);
1239
1240        let mut c = f.checker();
1241        c.check_decl(object);
1242        assert_eq!(address(value(&mut c, moved)), Some((0, 12)));
1243        assert_eq!(address(value(&mut c, back)), Some((0, -4)), "and it may go the other way");
1244        assert!(messages(&c).is_empty());
1245    }
1246
1247    #[test]
1248    fn two_pointers_into_one_object_subtract_to_the_elements_between_them() {
1249        let mut f = Fixture::new();
1250        let ten = f.int(10, IntKind::Int);
1251        let object = f.var(f.int_specs(), "a", &[array(ten)]);
1252        let a = f.use_name("a");
1253        let three = f.int(3, IntKind::Int);
1254        let high = f.subscript(a, three);
1255        let high = f.unary(UnaryOp::AddrOf, high);
1256        let a = f.use_name("a");
1257        let one = f.int(1, IntKind::Int);
1258        let low = f.subscript(a, one);
1259        let low = f.unary(UnaryOp::AddrOf, low);
1260        let distance = f.binary(BinaryOp::Sub, high, low);
1261
1262        let mut c = f.checker();
1263        c.check_decl(object);
1264        assert_eq!(
1265            value(&mut c, distance),
1266            Ok(Const::Int(2)),
1267            "a difference is a number, since the two cancel whatever the linker does with them"
1268        );
1269        assert!(messages(&c).is_empty());
1270    }
1271
1272    #[test]
1273    fn two_pointers_into_different_objects_have_no_distance_between_them() {
1274        let mut f = Fixture::new();
1275        let first = f.var(f.int_specs(), "a", &[]);
1276        let second = f.var(f.int_specs(), "b", &[]);
1277        let a = f.use_name("a");
1278        let a = f.unary(UnaryOp::AddrOf, a);
1279        let b = f.use_name("b");
1280        let b = f.unary(UnaryOp::AddrOf, b);
1281        let distance = f.binary(BinaryOp::Sub, a, b);
1282
1283        let mut c = f.checker();
1284        c.check_decl(first);
1285        c.check_decl(second);
1286        assert!(value(&mut c, distance).is_err(), "nothing decides that until the two are placed");
1287    }
1288
1289    #[test]
1290    fn the_address_of_an_automatic_object_is_not_a_constant() {
1291        let mut f = Fixture::new();
1292        let object = f.var(f.int_specs(), "a", &[]);
1293        let a = f.use_name("a");
1294        let taken = f.unary(UnaryOp::AddrOf, a);
1295
1296        let mut c = f.checker();
1297        c.scopes.push();
1298        c.check_decl(object);
1299        assert!(
1300            value(&mut c, taken).is_err(),
1301            "a local has no address until the frame holding it exists"
1302        );
1303    }
1304
1305    #[test]
1306    fn a_static_local_does_have_one_since_it_is_laid_out_once() {
1307        let mut f = Fixture::new();
1308        let mut specs = f.int_specs();
1309        specs.storage = Some(StorageClass::Static);
1310        let object = f.var(specs, "a", &[]);
1311        let a = f.use_name("a");
1312        let taken = f.unary(UnaryOp::AddrOf, a);
1313
1314        let mut c = f.checker();
1315        c.scopes.push();
1316        c.check_decl(object);
1317        assert_eq!(address(value(&mut c, taken)), Some((0, 0)));
1318    }
1319
1320    #[test]
1321    fn a_string_literal_is_an_object_and_its_decay_is_the_address_of_it() {
1322        let mut f = Fixture::new();
1323        let literal = f.string("hi");
1324        let one = f.int(1, IntKind::Int);
1325        let moved = f.binary(BinaryOp::Add, literal, one);
1326
1327        let mut c = f.checker();
1328        assert_eq!(address(value(&mut c, moved)), Some((0, 1)));
1329        assert!(messages(&c).is_empty());
1330    }
1331
1332    #[test]
1333    fn an_address_written_as_an_integer_survives_only_where_all_of_it_does() {
1334        let mut f = Fixture::new();
1335        let object = f.var(f.int_specs(), "a", &[]);
1336        let a = f.use_name("a");
1337        let taken = f.unary(UnaryOp::AddrOf, a);
1338        let wide = f.cast(f.builtin(BuiltinSet::LONG), &[], taken);
1339        let a = f.use_name("a");
1340        let taken = f.unary(UnaryOp::AddrOf, a);
1341        let narrow = f.cast(f.int_specs(), &[], taken);
1342
1343        let mut c = f.checker();
1344        c.check_decl(object);
1345        assert_eq!(
1346            address(value(&mut c, wide)),
1347            Some((0, 0)),
1348            "a `long` holds every bit of a pointer here, so the value is still the object"
1349        );
1350        assert!(
1351            value(&mut c, narrow).is_err(),
1352            "an `int` does not, and half an address is not an address"
1353        );
1354    }
1355
1356    #[test]
1357    fn a_pointer_with_no_object_behind_it_is_a_number_and_stays_one() {
1358        let mut f = Fixture::new();
1359        let four = f.int(4, IntKind::Int);
1360        let pointer = f.cast(f.int_specs(), &[pointer()], four);
1361        let one = f.int(1, IntKind::Int);
1362        let moved = f.binary(BinaryOp::Add, pointer, one);
1363        let back = f.cast(f.builtin(BuiltinSet::LONG), &[], moved);
1364
1365        let mut c = f.checker();
1366        assert_eq!(
1367            value(&mut c, back),
1368            Ok(Const::Int(8)),
1369            "the scaling happens and nothing has to be relocated, so it is an integer throughout"
1370        );
1371    }
1372
1373    #[test]
1374    fn an_address_is_never_null_and_says_so() {
1375        let mut f = Fixture::new();
1376        let object = f.var(f.int_specs(), "a", &[]);
1377        let a = f.use_name("a");
1378        let taken = f.unary(UnaryOp::AddrOf, a);
1379        let zero = f.int(0, IntKind::Int);
1380        let compared = f.binary(BinaryOp::Ne, taken, zero);
1381
1382        let mut c = f.checker();
1383        c.check_decl(object);
1384        assert_eq!(fold(&mut c, compared), Ok(1));
1385    }
1386
1387    #[test]
1388    fn an_address_is_not_an_integer_constant_expression_whatever_type_it_wears() {
1389        let mut f = Fixture::new();
1390        let object = f.var(f.int_specs(), "a", &[]);
1391        let a = f.use_name("a");
1392        let taken = f.unary(UnaryOp::AddrOf, a);
1393        let wide = f.cast(f.builtin(BuiltinSet::LONG), &[], taken);
1394
1395        let mut c = f.checker();
1396        c.check_decl(object);
1397        assert!(
1398            fold(&mut c, wide).is_err(),
1399            "an array bound and a case label want a number, and this is a relocation"
1400        );
1401    }
1402
1403    #[test]
1404    fn reading_an_object_is_not_a_constant_however_const_the_object_is() {
1405        let mut f = Fixture::new();
1406        let mut specs = f.int_specs();
1407        specs.quals = Quals::CONST;
1408        let object = f.var(specs, "n", &[]);
1409        let n = f.use_name("n");
1410
1411        let mut c = f.checker();
1412        c.check_decl(object);
1413        assert!(
1414            value(&mut c, n).is_err(),
1415            "which is the whole reason `const int n = 1; int a[n];` is a variable length array"
1416        );
1417    }
1418
1419    #[test]
1420    fn arithmetic_folds_to_the_value_the_program_wrote() {
1421        let mut f = Fixture::new();
1422        let (one, two, three) =
1423            (f.int(1, IntKind::Int), f.int(2, IntKind::Int), f.int(3, IntKind::Int));
1424        let sum = f.binary(BinaryOp::Add, one, two);
1425        let product = f.binary(BinaryOp::Mul, sum, three);
1426
1427        let mut c = f.checker();
1428        assert_eq!(fold(&mut c, product), Ok(9));
1429        assert!(messages(&c).is_empty());
1430    }
1431
1432    #[test]
1433    fn signed_overflow_is_warned_about_and_wrapped() {
1434        let mut f = Fixture::new();
1435        let (big, one) = (f.int(2_147_483_647, IntKind::Int), f.int(1, IntKind::Int));
1436        let sum = f.binary(BinaryOp::Add, big, one);
1437
1438        let mut c = f.checker();
1439        assert_eq!(fold(&mut c, sum), Ok(-2_147_483_648));
1440        assert_eq!(
1441            messages(&c),
1442            ["integer overflow in expression of type 'int' results in '-2147483648'"]
1443        );
1444    }
1445
1446    #[test]
1447    fn unsigned_arithmetic_wraps_without_a_word_because_it_is_not_overflow() {
1448        let mut f = Fixture::new();
1449        let (big, one) = (f.int(4_294_967_295, IntKind::UInt), f.int(1, IntKind::UInt));
1450        let sum = f.binary(BinaryOp::Add, big, one);
1451
1452        let mut c = f.checker();
1453        assert_eq!(fold(&mut c, sum), Ok(0));
1454        assert!(messages(&c).is_empty());
1455    }
1456
1457    #[test]
1458    fn a_bit_precise_type_overflows_in_its_own_width_and_not_in_an_int() {
1459        let mut f = Fixture::new();
1460        let (a, b) = (f.bit_int(100, true, 8), f.bit_int(100, true, 8));
1461        let sum = f.binary(BinaryOp::Add, a, b);
1462
1463        let mut c = f.checker();
1464        // Two hundred is an ordinary `int` and is not a `_BitInt(8)`, and the whole point of the
1465        // type is that it does what it says rather than promoting out of the question.
1466        assert_eq!(fold(&mut c, sum), Ok(-56));
1467        assert_eq!(messages(&c).len(), 1, "{:?}", messages(&c));
1468    }
1469
1470    #[test]
1471    fn division_by_zero_is_warned_about_and_has_no_value() {
1472        let mut f = Fixture::new();
1473        let (one, zero) = (f.int(1, IntKind::Int), f.int(0, IntKind::Int));
1474        let quotient = f.binary(BinaryOp::Div, one, zero);
1475
1476        let mut c = f.checker();
1477        let folded = fold(&mut c, quotient);
1478        assert!(folded.is_err());
1479        assert!(!folded.expect_err("no value").poisoned, "the caller still names the context");
1480        assert_eq!(messages(&c), ["division by zero"]);
1481    }
1482
1483    #[test]
1484    fn the_least_value_over_minus_one_overflows_and_so_does_its_remainder() {
1485        for op in [BinaryOp::Div, BinaryOp::Rem] {
1486            let mut f = Fixture::new();
1487            let (big, one) = (f.int(2_147_483_647, IntKind::Int), f.int(1, IntKind::Int));
1488            let negated = f.unary(UnaryOp::Minus, big);
1489            let least = f.binary(BinaryOp::Sub, negated, one);
1490            let minus_one = f.unary(UnaryOp::Minus, one);
1491            let divided = f.binary(op, least, minus_one);
1492
1493            let mut c = f.checker();
1494            let expected = if matches!(op, BinaryOp::Div) { -2_147_483_648 } else { 0 };
1495            assert_eq!(fold(&mut c, divided), Ok(expected));
1496            assert_eq!(messages(&c).len(), 1, "{:?}", messages(&c));
1497        }
1498    }
1499
1500    #[test]
1501    fn negating_the_least_value_overflows_onto_itself() {
1502        let mut f = Fixture::new();
1503        let (big, one) = (f.int(2_147_483_647, IntKind::Int), f.int(1, IntKind::Int));
1504        let flipped = f.unary(UnaryOp::Minus, big);
1505        let least = f.binary(BinaryOp::Sub, flipped, one);
1506        let negated = f.unary(UnaryOp::Minus, least);
1507
1508        let mut c = f.checker();
1509        assert_eq!(fold(&mut c, negated), Ok(-2_147_483_648));
1510        assert_eq!(
1511            messages(&c),
1512            ["integer overflow in expression of type 'int' results in '-2147483648'"]
1513        );
1514    }
1515
1516    #[test]
1517    fn a_shift_past_the_width_is_warned_about_and_folded_the_way_gcc_folds_it() {
1518        let mut f = Fixture::new();
1519        let (one, thirty_two) = (f.int(1, IntKind::Int), f.int(32, IntKind::Int));
1520        let shifted = f.binary(BinaryOp::Shl, one, thirty_two);
1521
1522        let mut c = f.checker();
1523        assert_eq!(fold(&mut c, shifted), Ok(0));
1524        assert_eq!(messages(&c), ["left shift count >= width of type"]);
1525    }
1526
1527    #[test]
1528    fn an_arithmetic_right_shift_past_the_width_keeps_the_sign() {
1529        let mut f = Fixture::new();
1530        let (one, forty) = (f.int(1, IntKind::Int), f.int(40, IntKind::Int));
1531        let minus_one = f.unary(UnaryOp::Minus, one);
1532        let shifted = f.binary(BinaryOp::Shr, minus_one, forty);
1533
1534        let mut c = f.checker();
1535        // Measured: gcc 13.3 folds `-1 >> 40` to minus one and `1 >> 40` to zero, which is the
1536        // shift having gone as far as it can rather than the count having wrapped.
1537        assert_eq!(fold(&mut c, shifted), Ok(-1));
1538        assert_eq!(messages(&c), ["right shift count >= width of type"]);
1539    }
1540
1541    #[test]
1542    fn a_negative_shift_count_is_warned_about_and_has_no_value() {
1543        let mut f = Fixture::new();
1544        let (one, two) = (f.int(1, IntKind::Int), f.int(2, IntKind::Int));
1545        let count = f.unary(UnaryOp::Minus, two);
1546        let shifted = f.binary(BinaryOp::Shl, one, count);
1547
1548        let mut c = f.checker();
1549        assert!(fold(&mut c, shifted).is_err());
1550        assert_eq!(messages(&c), ["left shift count is negative"]);
1551    }
1552
1553    #[test]
1554    fn a_shift_folds_in_the_width_of_its_left_operand_alone() {
1555        let mut f = Fixture::new();
1556        let (one, forty) = (f.int(1, IntKind::LongLong), f.int(40, IntKind::Int));
1557        let shifted = f.binary(BinaryOp::Shl, one, forty);
1558
1559        let mut c = f.checker();
1560        // The usual arithmetic conversions do not apply to a shift, so this is a sixty four bit
1561        // one shifted forty places and not an `int` shifted out of existence.
1562        assert_eq!(fold(&mut c, shifted), Ok(1 << 40));
1563        assert!(messages(&c).is_empty());
1564    }
1565
1566    #[test]
1567    fn an_unsigned_comparison_reads_the_top_bit_as_a_digit() {
1568        let mut f = Fixture::new();
1569        let one = f.int(1, IntKind::UInt);
1570        let big = f.unary(UnaryOp::Minus, one);
1571        let other = f.int(1, IntKind::UInt);
1572        let greater = f.binary(BinaryOp::Gt, big, other);
1573
1574        let mut c = f.checker();
1575        // `-1u` is four billion and something. Compared as a signed value it would be less than
1576        // one, and a compiler that folds it that way gets every unsigned bound check wrong.
1577        assert_eq!(fold(&mut c, greater), Ok(1));
1578        assert!(messages(&c).is_empty());
1579    }
1580
1581    #[test]
1582    fn short_circuiting_does_not_fold_what_the_language_did_not_evaluate() {
1583        let mut f = Fixture::new();
1584        let zero = f.int(0, IntKind::Int);
1585        let name = f.names.intern("x");
1586        let x = f.expr(ast::Expr::Name(name));
1587        let and = f.binary(BinaryOp::LogAnd, zero, x);
1588
1589        let mut c = f.checker();
1590        let int = c.types.int(IntKind::Int);
1591        c.declare_object(name, int, Span::DUMMY);
1592        assert_eq!(fold(&mut c, and), Ok(0));
1593        assert!(messages(&c).is_empty(), "{:?}", messages(&c));
1594    }
1595
1596    #[test]
1597    fn only_the_arm_the_condition_takes_is_folded() {
1598        let mut f = Fixture::new();
1599        let (one, two) = (f.int(1, IntKind::Int), f.int(2, IntKind::Int));
1600        let name = f.names.intern("x");
1601        let x = f.expr(ast::Expr::Name(name));
1602        let conditional = f.expr(ast::Expr::Cond { cond: one, then: Some(two), otherwise: x });
1603
1604        let mut c = f.checker();
1605        let int = c.types.int(IntKind::Int);
1606        c.declare_object(name, int, Span::DUMMY);
1607        assert_eq!(fold(&mut c, conditional), Ok(2));
1608        assert!(messages(&c).is_empty(), "{:?}", messages(&c));
1609    }
1610
1611    #[test]
1612    fn reading_an_object_is_not_a_constant_however_const_it_is() {
1613        let mut f = Fixture::new();
1614        let name = f.names.intern("n");
1615        let x = f.expr(ast::Expr::Name(name));
1616
1617        let mut c = f.checker();
1618        let int = c.types.int(IntKind::Int);
1619        let constant = c.types.qualified(int, rucc_types::Qualifiers::CONST);
1620        c.declare_object(name, constant, Span::DUMMY);
1621        // C says `const int n = 1; int a[n];` is a variable length array and C++ says it is not.
1622        // This is the arm that decides which language is being compiled.
1623        assert!(fold(&mut c, x).is_err());
1624        assert!(messages(&c).is_empty());
1625    }
1626
1627    #[test]
1628    fn a_comma_is_a_constant_nowhere() {
1629        let mut f = Fixture::new();
1630        let (one, two) = (f.int(1, IntKind::Int), f.int(2, IntKind::Int));
1631        let comma = f.expr(ast::Expr::Comma { lhs: one, rhs: two });
1632
1633        let mut c = f.checker();
1634        // 6.6p3 lists the comma operator among the things a constant expression shall not
1635        // contain, and gcc refuses `enum { a = (1, 2) };` accordingly.
1636        assert!(fold(&mut c, comma).is_err());
1637        assert!(messages(&c).is_empty());
1638    }
1639
1640    #[test]
1641    fn nothing_is_said_about_an_expression_that_was_already_diagnosed() {
1642        let mut f = Fixture::new();
1643        let name = f.names.intern("undeclared");
1644        let x = f.expr(ast::Expr::Name(name));
1645        let one = f.int(1, IntKind::Int);
1646        let sum = f.binary(BinaryOp::Add, x, one);
1647
1648        let mut c = f.checker();
1649        let folded = fold(&mut c, sum);
1650        assert!(folded.expect_err("no value").poisoned);
1651        assert_eq!(messages(&c).len(), 1, "the undeclared name, and nothing about the addition");
1652    }
1653
1654    #[test]
1655    fn a_floating_constant_is_not_an_integer_constant_expression() {
1656        let mut f = Fixture::new();
1657        let three = f.double("3.0");
1658
1659        let mut c = f.checker();
1660        // Exactly three and still not an integer constant expression, which is 6.6p6 being
1661        // about the type and not about the value. gcc refuses `enum { a = 3.0 };` too.
1662        let id = c.check_expr(three);
1663        assert!(c.eval_integer(id).is_err());
1664        let (three, _) = Float::parse("3.0", Format::Double).expect("a float");
1665        assert_eq!(c.eval_constant(id), Ok(Const::Float(three)));
1666        assert!(messages(&c).is_empty());
1667    }
1668
1669    #[test]
1670    fn floating_arithmetic_is_folded_in_the_target_format() {
1671        let mut f = Fixture::new();
1672        let (one, three) = (f.double("1.0"), f.double("3.0"));
1673        let third = f.binary(BinaryOp::Div, one, three);
1674
1675        let mut c = f.checker();
1676        let id = c.check_expr(third);
1677        let Ok(Const::Float(value)) = c.eval_constant(id) else { panic!("a folded float") };
1678        assert_eq!(value.to_bits(), 0x3fd5_5555_5555_5555, "the correctly rounded double third");
1679        assert!(messages(&c).is_empty());
1680    }
1681
1682    #[test]
1683    fn a_comparison_against_a_nan_is_false_except_for_the_inequality() {
1684        for (op, expected) in [(BinaryOp::Eq, 0), (BinaryOp::Ne, 1), (BinaryOp::Lt, 0)] {
1685            let mut f = Fixture::new();
1686            let (a, b) = (f.double("0.0"), f.double("0.0"));
1687            let nan = f.binary(BinaryOp::Div, a, b);
1688            let (c1, c2) = (f.double("0.0"), f.double("0.0"));
1689            let other = f.binary(BinaryOp::Div, c1, c2);
1690            let compared = f.binary(op, nan, other);
1691
1692            let mut c = f.checker();
1693            assert_eq!(fold(&mut c, compared), Ok(expected));
1694            // A floating division by zero is a nan and not a diagnostic, which is what makes
1695            // `0.0/0.0` a way to write one and what both compilers accept in a constant.
1696            assert!(messages(&c).is_empty());
1697        }
1698    }
1699
1700    #[test]
1701    fn a_conversion_between_arithmetic_types_folds_through_the_node_the_checking_wrote() {
1702        let mut f = Fixture::new();
1703        let (half, one) = (f.double("0.5"), f.int(1, IntKind::Int));
1704        let sum = f.binary(BinaryOp::Add, half, one);
1705
1706        let mut c = f.checker();
1707        let id = c.check_expr(sum);
1708        let Ok(Const::Float(value)) = c.eval_constant(id) else { panic!("a folded float") };
1709        // The `1` became a `1.0` in a conversion node, which is the whole reason the folding
1710        // reads the typed tree: nothing here had to work out that an int met a double.
1711        assert_eq!(value.to_bits(), 0x3ff8_0000_0000_0000, "one and a half, in a double");
1712        assert!(messages(&c).is_empty());
1713    }
1714}