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