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