1use 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::StorageDuration;
75use crate::expr::{Conversion, ExprId, ExprKind};
76use crate::tast::{Address, Base, Const, Tast};
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq)]
80pub struct NotConstant {
81 pub at: ExprId,
84 pub poisoned: bool,
91}
92
93#[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 #[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 pub fn constant(&mut self, expr: ExprId) -> Result<Const, NotConstant> {
122 self.eval(expr)
123 }
124
125 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 #[must_use]
145 pub fn finish(self) -> Vec<Diagnostic> {
146 self.diagnostics
147 }
148
149 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 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 let cond = self.eval(cond)?;
166 let taken = if truth(cond) { then } else { otherwise };
167 self.eval(taken)
168 }
169 ExprKind::Cast(operand) => self.convert(expr, operand),
170 ExprKind::Convert {
171 kind: Conversion::Arithmetic | Conversion::Bool | Conversion::Pointer,
172 operand,
173 } => self.convert(expr, operand),
174 ExprKind::Convert {
177 kind: Conversion::ArrayDecay | Conversion::FunctionDecay,
178 operand,
179 } => Ok(Const::Address(self.place(operand)?)),
180 ExprKind::Convert { kind: Conversion::NullPointer, operand } => self.eval(operand),
183 _ => Err(self.stop(expr)),
190 }
191 }
192
193 fn unary(&mut self, expr: ExprId, op: UnaryOp, operand: ExprId) -> Result<Const, NotConstant> {
195 let value = self.eval(operand)?;
196 match (op, value) {
197 (UnaryOp::Plus, value) => Ok(value),
198 (UnaryOp::Not, value) => Ok(Const::Int(i128::from(!truth(value)))),
199 (UnaryOp::Real, value) => Ok(value),
203 (UnaryOp::Imag, _) => self.zero(expr),
204 (UnaryOp::Minus, Const::Float(value)) => Ok(Const::Float(value.negated())),
205 (UnaryOp::Minus | UnaryOp::BitNot, Const::Int(value)) => {
206 let Some(info) = self.int_shape(self.tast[operand].ty) else {
207 return Err(self.stop(expr));
208 };
209 if matches!(op, UnaryOp::BitNot) {
210 return Ok(Const::Int(info.wrap(!value)));
211 }
212 let negated = info.wrap(value.wrapping_neg());
216 if info.signed && value == least(info) {
217 self.overflow(expr, negated);
218 }
219 Ok(Const::Int(negated))
220 }
221 _ => Err(self.stop(expr)),
224 }
225 }
226
227 fn binary(
229 &mut self,
230 expr: ExprId,
231 op: BinaryOp,
232 lhs: ExprId,
233 rhs: ExprId,
234 ) -> Result<Const, NotConstant> {
235 match op {
236 BinaryOp::LogAnd | BinaryOp::LogOr => {
237 let wanted = matches!(op, BinaryOp::LogOr);
238 let left = self.eval(lhs)?;
239 if truth(left) == wanted {
240 return Ok(Const::Int(i128::from(wanted)));
241 }
242 let right = self.eval(rhs)?;
243 Ok(Const::Int(i128::from(truth(right))))
244 }
245 BinaryOp::Shl | BinaryOp::Shr => self.shift(expr, op, lhs, rhs),
246 _ => {
247 let left = self.eval(lhs)?;
248 let right = self.eval(rhs)?;
249 if self.pointee_size(self.tast[lhs].ty).is_some()
250 || self.pointee_size(self.tast[rhs].ty).is_some()
251 {
252 return self.pointer_binary(expr, op, lhs, rhs, left, right);
253 }
254 match (left, right) {
255 (Const::Int(left), Const::Int(right)) => {
256 let Some(info) = self.int_shape(self.tast[lhs].ty) else {
260 return Err(self.stop(expr));
261 };
262 self.int_binary(expr, op, left, right, info)
263 }
264 (Const::Float(left), Const::Float(right)) => {
265 self.float_binary(expr, op, left, right)
266 }
267 _ => Err(self.stop(expr)),
271 }
272 }
273 }
274 }
275
276 fn int_binary(
278 &mut self,
279 expr: ExprId,
280 op: BinaryOp,
281 left: i128,
282 right: i128,
283 info: IntegerInfo,
284 ) -> Result<Const, NotConstant> {
285 if let Some(ordering) = compare_int(op, left, right, info) {
286 return Ok(Const::Int(i128::from(ordering)));
287 }
288 let value = match op {
289 BinaryOp::BitAnd => left & right,
290 BinaryOp::BitOr => left | right,
291 BinaryOp::BitXor => left ^ right,
292 BinaryOp::Div | BinaryOp::Rem if right == 0 => {
293 self.warn(expr, "division by zero", "E0521");
296 return Err(NotConstant { at: expr, poisoned: false });
297 }
298 BinaryOp::Add | BinaryOp::Sub | BinaryOp::Mul | BinaryOp::Div | BinaryOp::Rem => {
299 return self.arithmetic(expr, op, left, right, info);
300 }
301 _ => return Err(self.stop(expr)),
304 };
305 Ok(Const::Int(info.wrap(value)))
306 }
307
308 fn arithmetic(
310 &mut self,
311 expr: ExprId,
312 op: BinaryOp,
313 left: i128,
314 right: i128,
315 info: IntegerInfo,
316 ) -> Result<Const, NotConstant> {
317 if !info.signed {
318 let (left, right) = (left as u128, right as u128);
319 let value = match op {
320 BinaryOp::Add => left.wrapping_add(right),
321 BinaryOp::Sub => left.wrapping_sub(right),
322 BinaryOp::Mul => left.wrapping_mul(right),
323 BinaryOp::Div => left / right,
324 _ => left % right,
325 };
326 return Ok(Const::Int(info.wrap(value as i128)));
327 }
328 let (exact, wrapped) = match op {
329 BinaryOp::Add => (left.checked_add(right), left.wrapping_add(right)),
330 BinaryOp::Sub => (left.checked_sub(right), left.wrapping_sub(right)),
331 BinaryOp::Mul => (left.checked_mul(right), left.wrapping_mul(right)),
332 BinaryOp::Div => (left.checked_div(right), left.wrapping_div(right)),
333 _ => (left.checked_rem(right), left.wrapping_rem(right)),
334 };
335 let value = info.wrap(wrapped);
336 let extreme =
341 matches!(op, BinaryOp::Div | BinaryOp::Rem) && right == -1 && left == least(info);
342 if extreme || exact.is_none_or(|exact| !info.holds(exact)) {
343 self.overflow(expr, value);
344 }
345 Ok(Const::Int(value))
346 }
347
348 fn float_binary(
350 &mut self,
351 expr: ExprId,
352 op: BinaryOp,
353 left: Float,
354 right: Float,
355 ) -> Result<Const, NotConstant> {
356 if let Some(ordering) = compare_float(op, left, right) {
357 return Ok(Const::Int(i128::from(ordering)));
358 }
359 let (value, _) = match op {
362 BinaryOp::Add => left.sum(right),
363 BinaryOp::Sub => left.difference(right),
364 BinaryOp::Mul => left.product(right),
365 BinaryOp::Div => left.quotient(right),
366 _ => return Err(self.stop(expr)),
369 };
370 Ok(Const::Float(value))
371 }
372
373 fn shift(
375 &mut self,
376 expr: ExprId,
377 op: BinaryOp,
378 lhs: ExprId,
379 rhs: ExprId,
380 ) -> Result<Const, NotConstant> {
381 let left = self.eval(lhs)?;
382 let right = self.eval(rhs)?;
383 let (Const::Int(value), Const::Int(count)) = (left, right) else {
384 return Err(self.stop(expr));
385 };
386 let (Some(info), Some(counts)) =
387 (self.int_shape(self.tast[lhs].ty), self.int_shape(self.tast[rhs].ty))
388 else {
389 return Err(self.stop(expr));
390 };
391 let side = if matches!(op, BinaryOp::Shl) { "left" } else { "right" };
392 if counts.signed && count < 0 {
393 self.warn(expr, format!("{side} shift count is negative"), "E0522");
394 return Err(NotConstant { at: expr, poisoned: false });
395 }
396 let count = count as u128;
399 if count >= u128::from(info.width) {
400 self.warn(expr, format!("{side} shift count >= width of type"), "E0523");
401 let sign = matches!(op, BinaryOp::Shr) && info.signed && value < 0;
404 return Ok(Const::Int(if sign { -1 } else { 0 }));
405 }
406 let count = count as u32;
407 let value = match (op, info.signed) {
408 (BinaryOp::Shr, true) => value >> count,
409 (BinaryOp::Shr, false) => ((value as u128) >> count) as i128,
410 _ => value.wrapping_shl(count),
414 };
415 Ok(Const::Int(info.wrap(value)))
416 }
417
418 fn place(&mut self, expr: ExprId) -> Result<Address, NotConstant> {
424 match self.tast[expr].kind {
425 ExprKind::Error => Err(NotConstant { at: expr, poisoned: true }),
426 ExprKind::Decl(decl) | ExprKind::CompoundLiteral(decl)
429 if self.tast[decl].duration != StorageDuration::Automatic =>
430 {
431 Ok(Address { base: Base::Decl(decl), offset: 0 })
432 }
433 ExprKind::Str(id) => Ok(Address { base: Base::Str(id), offset: 0 }),
434 ExprKind::Member { base, field } => {
435 let mut address = self.place(base)?;
436 let TypeKind::Record(record) = bare(self.types, self.tast[base].ty) else {
437 return Err(self.stop(expr));
438 };
439 let Some(field) =
440 self.types.record_info(record).fields.get(field as usize).copied()
441 else {
442 return Err(self.stop(expr));
443 };
444 address.offset += i128::from(field.byte_offset());
445 Ok(address)
446 }
447 ExprKind::Subscript { base, index } => {
448 let base = self.eval(base)?;
449 let Const::Int(index) = self.eval(index)? else { return Err(self.stop(expr)) };
450 let size = i128::from(self.size_of(self.tast[expr].ty));
451 let Const::Address(mut address) = base else { return Err(self.stop(expr)) };
452 address.offset += index.wrapping_mul(size);
453 Ok(address)
454 }
455 ExprKind::Unary { op: UnaryOp::Deref, operand } => match self.eval(operand)? {
458 Const::Address(address) => Ok(address),
459 _ => Err(self.stop(expr)),
460 },
461 _ => Err(self.stop(expr)),
462 }
463 }
464
465 fn pointer_binary(
471 &mut self,
472 expr: ExprId,
473 op: BinaryOp,
474 lhs: ExprId,
475 rhs: ExprId,
476 left: Const,
477 right: Const,
478 ) -> Result<Const, NotConstant> {
479 let (left_step, right_step) =
480 (self.pointee_size(self.tast[lhs].ty), self.pointee_size(self.tast[rhs].ty));
481 match (op, left_step, right_step) {
482 (BinaryOp::Add, Some(step), None) => self.offset_by(expr, left, right, step),
483 (BinaryOp::Add, None, Some(step)) => self.offset_by(expr, right, left, step),
484 (BinaryOp::Sub, Some(step), None) => self.offset_by(expr, left, negate(right), step),
485 (BinaryOp::Sub, Some(step), Some(_)) if step != 0 => {
489 let distance = match (left, right) {
490 (Const::Address(left), Const::Address(right)) if left.base == right.base => {
491 left.offset - right.offset
492 }
493 (Const::Int(left), Const::Int(right)) => left - right,
494 _ => return Err(self.stop(expr)),
495 };
496 Ok(Const::Int(distance / i128::from(step)))
497 }
498 (_, Some(_), _) | (_, _, Some(_)) => self.pointer_compare(expr, op, left, right),
499 _ => Err(self.stop(expr)),
500 }
501 }
502
503 fn offset_by(
505 &mut self,
506 expr: ExprId,
507 pointer: Const,
508 count: Const,
509 step: u64,
510 ) -> Result<Const, NotConstant> {
511 let Const::Int(count) = count else { return Err(self.stop(expr)) };
512 let distance = count.wrapping_mul(i128::from(step));
513 match pointer {
514 Const::Address(address) => Ok(Const::Address(Address {
515 base: address.base,
516 offset: address.offset.wrapping_add(distance),
517 })),
518 Const::Int(value) => Ok(Const::Int(value.wrapping_add(distance))),
519 Const::Float(_) => Err(self.stop(expr)),
520 }
521 }
522
523 fn pointer_compare(
525 &mut self,
526 expr: ExprId,
527 op: BinaryOp,
528 left: Const,
529 right: Const,
530 ) -> Result<Const, NotConstant> {
531 let ordering = match (left, right) {
532 (Const::Address(left), Const::Address(right)) if left.base == right.base => {
533 left.offset.cmp(&right.offset)
534 }
535 (Const::Int(left), Const::Int(right)) => (left as u128).cmp(&(right as u128)),
537 (Const::Address(_), Const::Int(0)) | (Const::Int(0), Const::Address(_)) => {
541 return match op {
542 BinaryOp::Eq => Ok(Const::Int(0)),
543 BinaryOp::Ne => Ok(Const::Int(1)),
544 _ => Err(self.stop(expr)),
545 };
546 }
547 _ => return Err(self.stop(expr)),
548 };
549 match holds(op, ordering) {
550 Some(value) => Ok(Const::Int(i128::from(value))),
551 None => Err(self.stop(expr)),
552 }
553 }
554
555 fn pointee_size(&self, ty: TypeId) -> Option<u64> {
560 match bare(self.types, ty) {
561 TypeKind::Pointer(target) => Some(match bare(self.types, target) {
562 TypeKind::Void | TypeKind::Function(_) => 1,
563 _ => self.size_of(target),
564 }),
565 _ => None,
566 }
567 }
568
569 fn size_of(&self, ty: TypeId) -> u64 {
571 layout(self.types, ty, self.target).map_or(0, |layout| layout.size)
572 }
573
574 fn convert(&mut self, expr: ExprId, operand: ExprId) -> Result<Const, NotConstant> {
576 let value = self.eval(operand)?;
577 let (from, to) = (self.tast[operand].ty, self.tast[expr].ty);
578 match self.converted(value, from, to) {
579 Some(value) => Ok(value),
580 None => Err(self.stop(expr)),
581 }
582 }
583
584 fn converted(&self, value: Const, from: TypeId, to: TypeId) -> Option<Const> {
590 match bare(self.types, to) {
591 TypeKind::Bool => Some(Const::Int(i128::from(truth(value)))),
594 TypeKind::Int(_) | TypeKind::BitInt { .. } | TypeKind::Enum(_) => {
595 let info = self.int_shape(to)?;
596 match value {
597 Const::Int(value) => Some(Const::Int(info.wrap(value))),
598 Const::Float(value) => {
602 Some(Const::Int(value.to_integer(info.width, info.signed).0))
603 }
604 Const::Address(address) => (u64::from(info.width) == self.size_of(from) * 8)
609 .then_some(Const::Address(address)),
610 }
611 }
612 TypeKind::Float(kind) => {
613 let format = float_format(kind, self.target);
614 let (value, _) = match value {
615 Const::Float(value) => value.to_format(format),
616 Const::Int(value) => match self.int_shape(from) {
617 Some(info) if !info.signed => Float::from_unsigned(value as u128, format),
618 _ => Float::from_signed(value, format),
619 },
620 Const::Address(_) => return None,
623 };
624 Some(Const::Float(value))
625 }
626 TypeKind::Pointer(_) => match value {
629 Const::Int(_) | Const::Address(_) => Some(value),
630 Const::Float(_) => None,
631 },
632 _ => None,
634 }
635 }
636
637 fn zero(&mut self, expr: ExprId) -> Result<Const, NotConstant> {
639 let ty = self.tast[expr].ty;
640 if self.int_shape(ty).is_some() {
641 return Ok(Const::Int(0));
642 }
643 match self.float_shape(ty) {
644 Some(format) => Ok(Const::Float(Float::zero(format, false))),
645 None => Err(self.stop(expr)),
646 }
647 }
648
649 fn int_shape(&self, ty: TypeId) -> Option<IntegerInfo> {
651 int_shape(self.types, ty, self.target)
652 }
653
654 fn float_shape(&self, ty: TypeId) -> Option<Format> {
656 match bare(self.types, ty) {
657 TypeKind::Float(kind) => Some(float_format(kind, self.target)),
658 _ => None,
659 }
660 }
661
662 fn stop(&self, expr: ExprId) -> NotConstant {
664 NotConstant { at: expr, poisoned: false }
665 }
666
667 fn overflow(&mut self, expr: ExprId, value: i128) {
669 let ty = spell(self.types, self.names, self.tast[expr].ty);
670 let message = format!("integer overflow in expression of type '{ty}' results in '{value}'");
671 self.warn(expr, message, "E0524");
672 }
673
674 fn warn(&mut self, expr: ExprId, message: impl Into<String>, code: &'static str) {
676 let span = self.tast.expr_span(expr);
677 self.diagnostics.push(Diagnostic::warning(message.into(), span).with_code(code));
678 }
679}
680
681pub(crate) fn int_shape(types: &Types, ty: TypeId, target: &TargetInfo) -> Option<IntegerInfo> {
686 let info = integer_info(types, ty, target)?;
687 (info.width > 0 && info.width <= 128).then_some(info)
688}
689
690fn truth(value: Const) -> bool {
695 match value {
696 Const::Int(value) => value != 0,
697 Const::Float(value) => !value.is_zero(),
698 Const::Address(_) => true,
700 }
701}
702
703fn negate(value: Const) -> Const {
705 match value {
706 Const::Int(value) => Const::Int(value.wrapping_neg()),
707 other => other,
708 }
709}
710
711fn compare_int(op: BinaryOp, left: i128, right: i128, info: IntegerInfo) -> Option<bool> {
713 let ordering = if info.signed {
714 left.cmp(&right)
715 } else {
716 (left as u128).cmp(&(right as u128))
719 };
720 holds(op, ordering)
721}
722
723fn compare_float(op: BinaryOp, left: Float, right: Float) -> Option<bool> {
725 match left.compare(right) {
726 Some(ordering) => holds(op, ordering),
727 None if holds(op, Ordering::Equal).is_some() => Some(matches!(op, BinaryOp::Ne)),
731 None => None,
732 }
733}
734
735fn holds(op: BinaryOp, ordering: Ordering) -> Option<bool> {
737 Some(match op {
738 BinaryOp::Lt => ordering.is_lt(),
739 BinaryOp::Gt => ordering.is_gt(),
740 BinaryOp::Le => ordering.is_le(),
741 BinaryOp::Ge => ordering.is_ge(),
742 BinaryOp::Eq => ordering.is_eq(),
743 BinaryOp::Ne => ordering.is_ne(),
744 _ => return None,
745 })
746}
747
748fn least(info: IntegerInfo) -> i128 {
750 info.wrap(1i128 << info.width.saturating_sub(1))
751}
752
753pub(crate) fn bare(types: &Types, ty: TypeId) -> TypeKind {
758 match types.kind(types.canonical(ty)) {
759 TypeKind::Atomic(inner) => types.kind(types.canonical(inner)),
760 other => other,
761 }
762}
763
764pub(crate) fn spell_int(value: i128, info: IntegerInfo) -> String {
767 if info.signed { format!("{value}") } else { format!("{}", value as u128) }
768}
769
770pub(crate) fn narrowed(value: Const, info: IntegerInfo) -> i128 {
772 match value {
773 Const::Int(value) => info.wrap(value),
774 Const::Float(value) => value.to_integer(info.width, info.signed).0,
775 Const::Address(_) => 0,
778 }
779}
780
781pub(crate) fn spell_const(value: Const, info: Option<IntegerInfo>) -> String {
787 match value {
788 Const::Int(value) => match info {
789 Some(info) => spell_int(value, info),
790 None => format!("{value}"),
791 },
792 Const::Float(value) => value.to_hex(),
793 Const::Address(address) => {
794 let base = match address.base {
795 Base::Decl(decl) => decl.index(),
796 Base::Str(id) => id.index(),
797 };
798 format!("&#{base} + {}", address.offset)
799 }
800 }
801}
802
803pub(crate) fn overflows(value: Const, info: IntegerInfo) -> bool {
812 match value {
813 Const::Int(value) => {
814 !IntegerInfo::new(true, info.width).holds(value)
815 && !IntegerInfo::new(false, info.width).holds(value)
816 }
817 Const::Float(value) => value.to_integer(info.width, info.signed).1.has(Status::INVALID),
821 Const::Address(_) => false,
824 }
825}
826
827#[cfg(test)]
828mod tests {
829 use rucc_ast as ast;
830 use rucc_ast::{
831 ArraySize, AttrList, Builtin, BuiltinSet, DeclSpecs, Declarator, Derived, Quals,
832 StorageClass, TypeSpec,
833 };
834 use rucc_base::Symbol;
835 use rucc_base::float::Format;
836 use rucc_diag::Span;
837 use rucc_lex::{
838 Encoding, FloatConstant, FloatConstantType, IntConstant, IntConstantType, Remarks,
839 StringLiteral,
840 };
841 use rucc_session::Std;
842 use rucc_target::{TargetInfo, Triple};
843 use rucc_types::IntKind;
844
845 use super::*;
846 use crate::check::{Checker, Context};
847
848 struct Fixture {
854 ast: ast::Ast,
855 names: Interner,
856 target: TargetInfo,
857 }
858
859 impl Fixture {
860 fn new() -> Fixture {
861 let target =
862 TargetInfo::new("x86_64-unknown-linux-gnu".parse::<Triple>().expect("a triple"));
863 Fixture { ast: ast::Ast::new(), names: Interner::new(), target }
864 }
865
866 fn expr(&mut self, expr: ast::Expr) -> ast::ExprId {
867 self.ast.expr(expr, Span::DUMMY)
868 }
869
870 fn int(&mut self, value: u128, kind: IntKind) -> ast::ExprId {
871 let ty = IntConstantType::Standard(kind);
872 let id = self.ast.add_int(IntConstant { value, ty, remarks: Remarks::default() });
873 self.expr(ast::Expr::Int(id))
874 }
875
876 fn bit_int(&mut self, value: u128, signed: bool, width: u32) -> ast::ExprId {
878 let ty = IntConstantType::BitInt { signed, width };
879 let id = self.ast.add_int(IntConstant { value, ty, remarks: Remarks::default() });
880 self.expr(ast::Expr::Int(id))
881 }
882
883 fn double(&mut self, text: &str) -> ast::ExprId {
884 let (value, _) = Float::parse(text, Format::Double).expect("a float");
885 let constant = FloatConstant {
886 value,
887 ty: FloatConstantType::Double,
888 imaginary: false,
889 remarks: Remarks::default(),
890 };
891 let id = self.ast.add_float(constant);
892 self.expr(ast::Expr::Float(id))
893 }
894
895 fn binary(&mut self, op: BinaryOp, lhs: ast::ExprId, rhs: ast::ExprId) -> ast::ExprId {
896 self.expr(ast::Expr::Binary { op, lhs, rhs })
897 }
898
899 fn unary(&mut self, op: UnaryOp, operand: ast::ExprId) -> ast::ExprId {
900 self.expr(ast::Expr::Unary { op, operand })
901 }
902
903 fn name(&mut self, text: &str) -> Symbol {
904 self.names.intern(text)
905 }
906
907 fn use_name(&mut self, text: &str) -> ast::ExprId {
908 let name = self.name(text);
909 self.expr(ast::Expr::Name(name))
910 }
911
912 fn string(&mut self, text: &str) -> ast::ExprId {
913 let elements = text.chars().map(|c| c as u32).collect();
914 let id = self.ast.add_string(StringLiteral {
915 elements,
916 encoding: Encoding::Plain,
917 remarks: Remarks::default(),
918 });
919 self.expr(ast::Expr::Str(id))
920 }
921
922 fn subscript(&mut self, base: ast::ExprId, index: ast::ExprId) -> ast::ExprId {
923 self.expr(ast::Expr::Index { base, index })
924 }
925
926 fn member(&mut self, base: ast::ExprId, field: &str) -> ast::ExprId {
927 let name = self.name(field);
928 self.expr(ast::Expr::Member { base, name, arrow: false })
929 }
930
931 fn field(&mut self, specs: DeclSpecs, name: &str) -> ast::Member {
933 let declarator = Some(self.declarator(Some(name), &[]));
934 let specs = self.ast.add_specs(specs);
935 ast::Member::Field(ast::Field {
936 specs,
937 declarator,
938 bits: None,
939 attrs: AttrList::EMPTY,
940 span: Span::DUMMY,
941 })
942 }
943
944 fn record(&mut self, tag: &str, members: &[ast::Member]) -> DeclSpecs {
946 let tag = Some(self.name(tag));
947 let fields = Some(self.ast.add_member_list(members));
948 let mut specs = DeclSpecs::empty(Span::DUMMY);
949 specs.ty = TypeSpec::Record {
950 kind: ast::RecordKind::Struct,
951 tag,
952 fields,
953 attrs: AttrList::EMPTY,
954 };
955 specs
956 }
957
958 fn cast(
959 &mut self,
960 specs: DeclSpecs,
961 derived: &[Derived],
962 operand: ast::ExprId,
963 ) -> ast::ExprId {
964 let ty = self.type_name(specs, derived);
965 self.expr(ast::Expr::Cast { ty, operand })
966 }
967
968 fn int_specs(&self) -> DeclSpecs {
970 self.builtin(BuiltinSet::INT)
971 }
972
973 fn builtin(&self, keyword: BuiltinSet) -> DeclSpecs {
974 let mut specs = DeclSpecs::empty(Span::DUMMY);
975 let builtin = Builtin::NONE.add(keyword).expect("a keyword written once");
976 specs.ty = TypeSpec::Builtin(builtin);
977 specs
978 }
979
980 fn type_name(&mut self, specs: DeclSpecs, derived: &[Derived]) -> ast::TypeNameId {
981 let declarator = self.declarator(None, derived);
982 let specs = self.ast.add_specs(specs);
983 self.ast.add_type_name(ast::TypeName { specs, declarator, span: Span::DUMMY })
984 }
985
986 fn declarator(&mut self, name: Option<&str>, derived: &[Derived]) -> ast::DeclaratorId {
987 let name = name.map(|name| self.name(name));
988 let derived = self.ast.add_derived_list(derived);
989 self.ast.add_declarator(Declarator {
990 name,
991 name_span: Span::DUMMY,
992 derived,
993 span: Span::DUMMY,
994 })
995 }
996
997 fn var(&mut self, specs: DeclSpecs, name: &str, derived: &[Derived]) -> ast::DeclId {
999 let declarator = self.declarator(Some(name), derived);
1000 let item = ast::InitDeclarator {
1001 declarator,
1002 init: None,
1003 asm_label: None,
1004 attrs: AttrList::EMPTY,
1005 span: Span::DUMMY,
1006 };
1007 let declarators = self.ast.add_init_declarator_list(&[item]);
1008 let specs = self.ast.add_specs(specs);
1009 self.ast.decl(ast::Decl::Var { specs, declarators }, Span::DUMMY)
1010 }
1011
1012 fn checker(&self) -> Checker<'_> {
1013 Checker::new(&self.ast, Context::new(&self.names, &self.target, Std::C23))
1014 }
1015 }
1016
1017 fn array(size: ast::ExprId) -> Derived {
1019 Derived::Array { size: ArraySize::Expr(size), quals: Quals::NONE, has_static: false }
1020 }
1021
1022 fn pointer() -> Derived {
1024 Derived::Pointer { quals: Quals::NONE, attrs: AttrList::EMPTY }
1025 }
1026
1027 fn value(checker: &mut Checker<'_>, expr: ast::ExprId) -> Result<Const, NotConstant> {
1029 let id = checker.check_expr(expr);
1030 checker.eval_constant(id)
1031 }
1032
1033 fn address(value: Result<Const, NotConstant>) -> Option<(usize, i128)> {
1035 match value {
1036 Ok(Const::Address(address)) => {
1037 let base = match address.base {
1038 Base::Decl(decl) => decl.index(),
1039 Base::Str(id) => id.index(),
1040 };
1041 Some((base, address.offset))
1042 }
1043 _ => None,
1044 }
1045 }
1046
1047 fn fold(checker: &mut Checker<'_>, expr: ast::ExprId) -> Result<i128, NotConstant> {
1049 let id = checker.check_expr(expr);
1050 checker.eval_integer(id)
1051 }
1052
1053 fn messages(checker: &Checker<'_>) -> Vec<String> {
1055 checker.errors.diagnostics().iter().map(|d| d.message.clone()).collect()
1056 }
1057
1058 #[test]
1059 fn the_address_of_a_static_object_is_that_object_and_no_distance() {
1060 let mut f = Fixture::new();
1061 let object = f.var(f.int_specs(), "a", &[]);
1062 let a = f.use_name("a");
1063 let taken = f.unary(UnaryOp::AddrOf, a);
1064
1065 let mut c = f.checker();
1066 c.check_decl(object);
1067 assert_eq!(address(value(&mut c, taken)), Some((0, 0)));
1068 assert!(messages(&c).is_empty());
1069 }
1070
1071 #[test]
1072 fn a_subscript_and_a_member_add_up_into_one_distance() {
1073 let mut f = Fixture::new();
1074 let x = f.int(4, IntKind::Int);
1075 let object = f.var(f.int_specs(), "a", &[array(x)]);
1076 let a = f.use_name("a");
1077 let two = f.int(2, IntKind::Int);
1078 let element = f.subscript(a, two);
1079 let taken = f.unary(UnaryOp::AddrOf, element);
1080
1081 let mut c = f.checker();
1082 c.check_decl(object);
1083 assert_eq!(
1084 address(value(&mut c, taken)),
1085 Some((0, 8)),
1086 "two elements of four bytes each into the object it started at"
1087 );
1088 assert!(messages(&c).is_empty());
1089 }
1090
1091 #[test]
1092 fn a_member_adds_its_own_offset_to_the_object_that_holds_it() {
1093 let mut f = Fixture::new();
1094 let x = f.field(f.int_specs(), "x");
1095 let y = f.field(f.int_specs(), "y");
1096 let specs = f.record("S", &[x, y]);
1097 let object = f.var(specs, "s", &[]);
1098 let s = f.use_name("s");
1099 let member = f.member(s, "y");
1100 let taken = f.unary(UnaryOp::AddrOf, member);
1101
1102 let mut c = f.checker();
1103 c.check_decl(object);
1104 assert_eq!(address(value(&mut c, taken)), Some((0, 4)));
1105 assert!(messages(&c).is_empty());
1106 }
1107
1108 #[test]
1109 fn a_pointer_moves_by_what_it_points_at_and_not_by_bytes() {
1110 let mut f = Fixture::new();
1111 let four = f.int(4, IntKind::Int);
1112 let object = f.var(f.int_specs(), "a", &[array(four)]);
1113 let a = f.use_name("a");
1114 let three = f.int(3, IntKind::Int);
1115 let moved = f.binary(BinaryOp::Add, a, three);
1116 let a = f.use_name("a");
1117 let one = f.int(1, IntKind::Int);
1118 let back = f.binary(BinaryOp::Sub, a, one);
1119
1120 let mut c = f.checker();
1121 c.check_decl(object);
1122 assert_eq!(address(value(&mut c, moved)), Some((0, 12)));
1123 assert_eq!(address(value(&mut c, back)), Some((0, -4)), "and it may go the other way");
1124 assert!(messages(&c).is_empty());
1125 }
1126
1127 #[test]
1128 fn two_pointers_into_one_object_subtract_to_the_elements_between_them() {
1129 let mut f = Fixture::new();
1130 let ten = f.int(10, IntKind::Int);
1131 let object = f.var(f.int_specs(), "a", &[array(ten)]);
1132 let a = f.use_name("a");
1133 let three = f.int(3, IntKind::Int);
1134 let high = f.subscript(a, three);
1135 let high = f.unary(UnaryOp::AddrOf, high);
1136 let a = f.use_name("a");
1137 let one = f.int(1, IntKind::Int);
1138 let low = f.subscript(a, one);
1139 let low = f.unary(UnaryOp::AddrOf, low);
1140 let distance = f.binary(BinaryOp::Sub, high, low);
1141
1142 let mut c = f.checker();
1143 c.check_decl(object);
1144 assert_eq!(
1145 value(&mut c, distance),
1146 Ok(Const::Int(2)),
1147 "a difference is a number, since the two cancel whatever the linker does with them"
1148 );
1149 assert!(messages(&c).is_empty());
1150 }
1151
1152 #[test]
1153 fn two_pointers_into_different_objects_have_no_distance_between_them() {
1154 let mut f = Fixture::new();
1155 let first = f.var(f.int_specs(), "a", &[]);
1156 let second = f.var(f.int_specs(), "b", &[]);
1157 let a = f.use_name("a");
1158 let a = f.unary(UnaryOp::AddrOf, a);
1159 let b = f.use_name("b");
1160 let b = f.unary(UnaryOp::AddrOf, b);
1161 let distance = f.binary(BinaryOp::Sub, a, b);
1162
1163 let mut c = f.checker();
1164 c.check_decl(first);
1165 c.check_decl(second);
1166 assert!(value(&mut c, distance).is_err(), "nothing decides that until the two are placed");
1167 }
1168
1169 #[test]
1170 fn the_address_of_an_automatic_object_is_not_a_constant() {
1171 let mut f = Fixture::new();
1172 let object = f.var(f.int_specs(), "a", &[]);
1173 let a = f.use_name("a");
1174 let taken = f.unary(UnaryOp::AddrOf, a);
1175
1176 let mut c = f.checker();
1177 c.scopes.push();
1178 c.check_decl(object);
1179 assert!(
1180 value(&mut c, taken).is_err(),
1181 "a local has no address until the frame holding it exists"
1182 );
1183 }
1184
1185 #[test]
1186 fn a_static_local_does_have_one_since_it_is_laid_out_once() {
1187 let mut f = Fixture::new();
1188 let mut specs = f.int_specs();
1189 specs.storage = Some(StorageClass::Static);
1190 let object = f.var(specs, "a", &[]);
1191 let a = f.use_name("a");
1192 let taken = f.unary(UnaryOp::AddrOf, a);
1193
1194 let mut c = f.checker();
1195 c.scopes.push();
1196 c.check_decl(object);
1197 assert_eq!(address(value(&mut c, taken)), Some((0, 0)));
1198 }
1199
1200 #[test]
1201 fn a_string_literal_is_an_object_and_its_decay_is_the_address_of_it() {
1202 let mut f = Fixture::new();
1203 let literal = f.string("hi");
1204 let one = f.int(1, IntKind::Int);
1205 let moved = f.binary(BinaryOp::Add, literal, one);
1206
1207 let mut c = f.checker();
1208 assert_eq!(address(value(&mut c, moved)), Some((0, 1)));
1209 assert!(messages(&c).is_empty());
1210 }
1211
1212 #[test]
1213 fn an_address_written_as_an_integer_survives_only_where_all_of_it_does() {
1214 let mut f = Fixture::new();
1215 let object = f.var(f.int_specs(), "a", &[]);
1216 let a = f.use_name("a");
1217 let taken = f.unary(UnaryOp::AddrOf, a);
1218 let wide = f.cast(f.builtin(BuiltinSet::LONG), &[], taken);
1219 let a = f.use_name("a");
1220 let taken = f.unary(UnaryOp::AddrOf, a);
1221 let narrow = f.cast(f.int_specs(), &[], taken);
1222
1223 let mut c = f.checker();
1224 c.check_decl(object);
1225 assert_eq!(
1226 address(value(&mut c, wide)),
1227 Some((0, 0)),
1228 "a `long` holds every bit of a pointer here, so the value is still the object"
1229 );
1230 assert!(
1231 value(&mut c, narrow).is_err(),
1232 "an `int` does not, and half an address is not an address"
1233 );
1234 }
1235
1236 #[test]
1237 fn a_pointer_with_no_object_behind_it_is_a_number_and_stays_one() {
1238 let mut f = Fixture::new();
1239 let four = f.int(4, IntKind::Int);
1240 let pointer = f.cast(f.int_specs(), &[pointer()], four);
1241 let one = f.int(1, IntKind::Int);
1242 let moved = f.binary(BinaryOp::Add, pointer, one);
1243 let back = f.cast(f.builtin(BuiltinSet::LONG), &[], moved);
1244
1245 let mut c = f.checker();
1246 assert_eq!(
1247 value(&mut c, back),
1248 Ok(Const::Int(8)),
1249 "the scaling happens and nothing has to be relocated, so it is an integer throughout"
1250 );
1251 }
1252
1253 #[test]
1254 fn an_address_is_never_null_and_says_so() {
1255 let mut f = Fixture::new();
1256 let object = f.var(f.int_specs(), "a", &[]);
1257 let a = f.use_name("a");
1258 let taken = f.unary(UnaryOp::AddrOf, a);
1259 let zero = f.int(0, IntKind::Int);
1260 let compared = f.binary(BinaryOp::Ne, taken, zero);
1261
1262 let mut c = f.checker();
1263 c.check_decl(object);
1264 assert_eq!(fold(&mut c, compared), Ok(1));
1265 }
1266
1267 #[test]
1268 fn an_address_is_not_an_integer_constant_expression_whatever_type_it_wears() {
1269 let mut f = Fixture::new();
1270 let object = f.var(f.int_specs(), "a", &[]);
1271 let a = f.use_name("a");
1272 let taken = f.unary(UnaryOp::AddrOf, a);
1273 let wide = f.cast(f.builtin(BuiltinSet::LONG), &[], taken);
1274
1275 let mut c = f.checker();
1276 c.check_decl(object);
1277 assert!(
1278 fold(&mut c, wide).is_err(),
1279 "an array bound and a case label want a number, and this is a relocation"
1280 );
1281 }
1282
1283 #[test]
1284 fn reading_an_object_is_not_a_constant_however_const_the_object_is() {
1285 let mut f = Fixture::new();
1286 let mut specs = f.int_specs();
1287 specs.quals = Quals::CONST;
1288 let object = f.var(specs, "n", &[]);
1289 let n = f.use_name("n");
1290
1291 let mut c = f.checker();
1292 c.check_decl(object);
1293 assert!(
1294 value(&mut c, n).is_err(),
1295 "which is the whole reason `const int n = 1; int a[n];` is a variable length array"
1296 );
1297 }
1298
1299 #[test]
1300 fn arithmetic_folds_to_the_value_the_program_wrote() {
1301 let mut f = Fixture::new();
1302 let (one, two, three) =
1303 (f.int(1, IntKind::Int), f.int(2, IntKind::Int), f.int(3, IntKind::Int));
1304 let sum = f.binary(BinaryOp::Add, one, two);
1305 let product = f.binary(BinaryOp::Mul, sum, three);
1306
1307 let mut c = f.checker();
1308 assert_eq!(fold(&mut c, product), Ok(9));
1309 assert!(messages(&c).is_empty());
1310 }
1311
1312 #[test]
1313 fn signed_overflow_is_warned_about_and_wrapped() {
1314 let mut f = Fixture::new();
1315 let (big, one) = (f.int(2_147_483_647, IntKind::Int), f.int(1, IntKind::Int));
1316 let sum = f.binary(BinaryOp::Add, big, one);
1317
1318 let mut c = f.checker();
1319 assert_eq!(fold(&mut c, sum), Ok(-2_147_483_648));
1320 assert_eq!(
1321 messages(&c),
1322 ["integer overflow in expression of type 'int' results in '-2147483648'"]
1323 );
1324 }
1325
1326 #[test]
1327 fn unsigned_arithmetic_wraps_without_a_word_because_it_is_not_overflow() {
1328 let mut f = Fixture::new();
1329 let (big, one) = (f.int(4_294_967_295, IntKind::UInt), f.int(1, IntKind::UInt));
1330 let sum = f.binary(BinaryOp::Add, big, one);
1331
1332 let mut c = f.checker();
1333 assert_eq!(fold(&mut c, sum), Ok(0));
1334 assert!(messages(&c).is_empty());
1335 }
1336
1337 #[test]
1338 fn a_bit_precise_type_overflows_in_its_own_width_and_not_in_an_int() {
1339 let mut f = Fixture::new();
1340 let (a, b) = (f.bit_int(100, true, 8), f.bit_int(100, true, 8));
1341 let sum = f.binary(BinaryOp::Add, a, b);
1342
1343 let mut c = f.checker();
1344 assert_eq!(fold(&mut c, sum), Ok(-56));
1347 assert_eq!(messages(&c).len(), 1, "{:?}", messages(&c));
1348 }
1349
1350 #[test]
1351 fn division_by_zero_is_warned_about_and_has_no_value() {
1352 let mut f = Fixture::new();
1353 let (one, zero) = (f.int(1, IntKind::Int), f.int(0, IntKind::Int));
1354 let quotient = f.binary(BinaryOp::Div, one, zero);
1355
1356 let mut c = f.checker();
1357 let folded = fold(&mut c, quotient);
1358 assert!(folded.is_err());
1359 assert!(!folded.expect_err("no value").poisoned, "the caller still names the context");
1360 assert_eq!(messages(&c), ["division by zero"]);
1361 }
1362
1363 #[test]
1364 fn the_least_value_over_minus_one_overflows_and_so_does_its_remainder() {
1365 for op in [BinaryOp::Div, BinaryOp::Rem] {
1366 let mut f = Fixture::new();
1367 let (big, one) = (f.int(2_147_483_647, IntKind::Int), f.int(1, IntKind::Int));
1368 let negated = f.unary(UnaryOp::Minus, big);
1369 let least = f.binary(BinaryOp::Sub, negated, one);
1370 let minus_one = f.unary(UnaryOp::Minus, one);
1371 let divided = f.binary(op, least, minus_one);
1372
1373 let mut c = f.checker();
1374 let expected = if matches!(op, BinaryOp::Div) { -2_147_483_648 } else { 0 };
1375 assert_eq!(fold(&mut c, divided), Ok(expected));
1376 assert_eq!(messages(&c).len(), 1, "{:?}", messages(&c));
1377 }
1378 }
1379
1380 #[test]
1381 fn negating_the_least_value_overflows_onto_itself() {
1382 let mut f = Fixture::new();
1383 let (big, one) = (f.int(2_147_483_647, IntKind::Int), f.int(1, IntKind::Int));
1384 let flipped = f.unary(UnaryOp::Minus, big);
1385 let least = f.binary(BinaryOp::Sub, flipped, one);
1386 let negated = f.unary(UnaryOp::Minus, least);
1387
1388 let mut c = f.checker();
1389 assert_eq!(fold(&mut c, negated), Ok(-2_147_483_648));
1390 assert_eq!(
1391 messages(&c),
1392 ["integer overflow in expression of type 'int' results in '-2147483648'"]
1393 );
1394 }
1395
1396 #[test]
1397 fn a_shift_past_the_width_is_warned_about_and_folded_the_way_gcc_folds_it() {
1398 let mut f = Fixture::new();
1399 let (one, thirty_two) = (f.int(1, IntKind::Int), f.int(32, IntKind::Int));
1400 let shifted = f.binary(BinaryOp::Shl, one, thirty_two);
1401
1402 let mut c = f.checker();
1403 assert_eq!(fold(&mut c, shifted), Ok(0));
1404 assert_eq!(messages(&c), ["left shift count >= width of type"]);
1405 }
1406
1407 #[test]
1408 fn an_arithmetic_right_shift_past_the_width_keeps_the_sign() {
1409 let mut f = Fixture::new();
1410 let (one, forty) = (f.int(1, IntKind::Int), f.int(40, IntKind::Int));
1411 let minus_one = f.unary(UnaryOp::Minus, one);
1412 let shifted = f.binary(BinaryOp::Shr, minus_one, forty);
1413
1414 let mut c = f.checker();
1415 assert_eq!(fold(&mut c, shifted), Ok(-1));
1418 assert_eq!(messages(&c), ["right shift count >= width of type"]);
1419 }
1420
1421 #[test]
1422 fn a_negative_shift_count_is_warned_about_and_has_no_value() {
1423 let mut f = Fixture::new();
1424 let (one, two) = (f.int(1, IntKind::Int), f.int(2, IntKind::Int));
1425 let count = f.unary(UnaryOp::Minus, two);
1426 let shifted = f.binary(BinaryOp::Shl, one, count);
1427
1428 let mut c = f.checker();
1429 assert!(fold(&mut c, shifted).is_err());
1430 assert_eq!(messages(&c), ["left shift count is negative"]);
1431 }
1432
1433 #[test]
1434 fn a_shift_folds_in_the_width_of_its_left_operand_alone() {
1435 let mut f = Fixture::new();
1436 let (one, forty) = (f.int(1, IntKind::LongLong), f.int(40, IntKind::Int));
1437 let shifted = f.binary(BinaryOp::Shl, one, forty);
1438
1439 let mut c = f.checker();
1440 assert_eq!(fold(&mut c, shifted), Ok(1 << 40));
1443 assert!(messages(&c).is_empty());
1444 }
1445
1446 #[test]
1447 fn an_unsigned_comparison_reads_the_top_bit_as_a_digit() {
1448 let mut f = Fixture::new();
1449 let one = f.int(1, IntKind::UInt);
1450 let big = f.unary(UnaryOp::Minus, one);
1451 let other = f.int(1, IntKind::UInt);
1452 let greater = f.binary(BinaryOp::Gt, big, other);
1453
1454 let mut c = f.checker();
1455 assert_eq!(fold(&mut c, greater), Ok(1));
1458 assert!(messages(&c).is_empty());
1459 }
1460
1461 #[test]
1462 fn short_circuiting_does_not_fold_what_the_language_did_not_evaluate() {
1463 let mut f = Fixture::new();
1464 let zero = f.int(0, IntKind::Int);
1465 let name = f.names.intern("x");
1466 let x = f.expr(ast::Expr::Name(name));
1467 let and = f.binary(BinaryOp::LogAnd, zero, x);
1468
1469 let mut c = f.checker();
1470 let int = c.types.int(IntKind::Int);
1471 c.declare_object(name, int, Span::DUMMY);
1472 assert_eq!(fold(&mut c, and), Ok(0));
1473 assert!(messages(&c).is_empty(), "{:?}", messages(&c));
1474 }
1475
1476 #[test]
1477 fn only_the_arm_the_condition_takes_is_folded() {
1478 let mut f = Fixture::new();
1479 let (one, two) = (f.int(1, IntKind::Int), f.int(2, IntKind::Int));
1480 let name = f.names.intern("x");
1481 let x = f.expr(ast::Expr::Name(name));
1482 let conditional = f.expr(ast::Expr::Cond { cond: one, then: Some(two), otherwise: x });
1483
1484 let mut c = f.checker();
1485 let int = c.types.int(IntKind::Int);
1486 c.declare_object(name, int, Span::DUMMY);
1487 assert_eq!(fold(&mut c, conditional), Ok(2));
1488 assert!(messages(&c).is_empty(), "{:?}", messages(&c));
1489 }
1490
1491 #[test]
1492 fn reading_an_object_is_not_a_constant_however_const_it_is() {
1493 let mut f = Fixture::new();
1494 let name = f.names.intern("n");
1495 let x = f.expr(ast::Expr::Name(name));
1496
1497 let mut c = f.checker();
1498 let int = c.types.int(IntKind::Int);
1499 let constant = c.types.qualified(int, rucc_types::Qualifiers::CONST);
1500 c.declare_object(name, constant, Span::DUMMY);
1501 assert!(fold(&mut c, x).is_err());
1504 assert!(messages(&c).is_empty());
1505 }
1506
1507 #[test]
1508 fn a_comma_is_a_constant_nowhere() {
1509 let mut f = Fixture::new();
1510 let (one, two) = (f.int(1, IntKind::Int), f.int(2, IntKind::Int));
1511 let comma = f.expr(ast::Expr::Comma { lhs: one, rhs: two });
1512
1513 let mut c = f.checker();
1514 assert!(fold(&mut c, comma).is_err());
1517 assert!(messages(&c).is_empty());
1518 }
1519
1520 #[test]
1521 fn nothing_is_said_about_an_expression_that_was_already_diagnosed() {
1522 let mut f = Fixture::new();
1523 let name = f.names.intern("undeclared");
1524 let x = f.expr(ast::Expr::Name(name));
1525 let one = f.int(1, IntKind::Int);
1526 let sum = f.binary(BinaryOp::Add, x, one);
1527
1528 let mut c = f.checker();
1529 let folded = fold(&mut c, sum);
1530 assert!(folded.expect_err("no value").poisoned);
1531 assert_eq!(messages(&c).len(), 1, "the undeclared name, and nothing about the addition");
1532 }
1533
1534 #[test]
1535 fn a_floating_constant_is_not_an_integer_constant_expression() {
1536 let mut f = Fixture::new();
1537 let three = f.double("3.0");
1538
1539 let mut c = f.checker();
1540 let id = c.check_expr(three);
1543 assert!(c.eval_integer(id).is_err());
1544 let (three, _) = Float::parse("3.0", Format::Double).expect("a float");
1545 assert_eq!(c.eval_constant(id), Ok(Const::Float(three)));
1546 assert!(messages(&c).is_empty());
1547 }
1548
1549 #[test]
1550 fn floating_arithmetic_is_folded_in_the_target_format() {
1551 let mut f = Fixture::new();
1552 let (one, three) = (f.double("1.0"), f.double("3.0"));
1553 let third = f.binary(BinaryOp::Div, one, three);
1554
1555 let mut c = f.checker();
1556 let id = c.check_expr(third);
1557 let Ok(Const::Float(value)) = c.eval_constant(id) else { panic!("a folded float") };
1558 assert_eq!(value.to_bits(), 0x3fd5_5555_5555_5555, "the correctly rounded double third");
1559 assert!(messages(&c).is_empty());
1560 }
1561
1562 #[test]
1563 fn a_comparison_against_a_nan_is_false_except_for_the_inequality() {
1564 for (op, expected) in [(BinaryOp::Eq, 0), (BinaryOp::Ne, 1), (BinaryOp::Lt, 0)] {
1565 let mut f = Fixture::new();
1566 let (a, b) = (f.double("0.0"), f.double("0.0"));
1567 let nan = f.binary(BinaryOp::Div, a, b);
1568 let (c1, c2) = (f.double("0.0"), f.double("0.0"));
1569 let other = f.binary(BinaryOp::Div, c1, c2);
1570 let compared = f.binary(op, nan, other);
1571
1572 let mut c = f.checker();
1573 assert_eq!(fold(&mut c, compared), Ok(expected));
1574 assert!(messages(&c).is_empty());
1577 }
1578 }
1579
1580 #[test]
1581 fn a_conversion_between_arithmetic_types_folds_through_the_node_the_checking_wrote() {
1582 let mut f = Fixture::new();
1583 let (half, one) = (f.double("0.5"), f.int(1, IntKind::Int));
1584 let sum = f.binary(BinaryOp::Add, half, one);
1585
1586 let mut c = f.checker();
1587 let id = c.check_expr(sum);
1588 let Ok(Const::Float(value)) = c.eval_constant(id) else { panic!("a folded float") };
1589 assert_eq!(value.to_bits(), 0x3ff8_0000_0000_0000, "one and a half, in a double");
1592 assert!(messages(&c).is_empty());
1593 }
1594}