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