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