Skip to content

Commit f8cfafc

Browse files
committed
Extract check_assertion.
1 parent b833fca commit f8cfafc

File tree

1 file changed

+71
-72
lines changed

1 file changed

+71
-72
lines changed

compiler/rustc_mir_transform/src/const_prop_lint.rs

+71-72
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,76 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
480480
Some(())
481481
}
482482

483+
fn check_assertion(
484+
&mut self,
485+
expected: bool,
486+
msg: &AssertKind<Operand<'tcx>>,
487+
cond: &Operand<'tcx>,
488+
location: Location,
489+
) -> Option<!> {
490+
let ref value = self.eval_operand(&cond, location)?;
491+
trace!("assertion on {:?} should be {:?}", value, expected);
492+
493+
let expected = Scalar::from_bool(expected);
494+
let value_const = self.use_ecx(location, |this| this.ecx.read_scalar(&value))?;
495+
496+
if expected != value_const {
497+
// Poison all places this operand references so that further code
498+
// doesn't use the invalid value
499+
match cond {
500+
Operand::Move(ref place) | Operand::Copy(ref place) => {
501+
Self::remove_const(&mut self.ecx, place.local);
502+
}
503+
Operand::Constant(_) => {}
504+
}
505+
enum DbgVal<T> {
506+
Val(T),
507+
Underscore,
508+
}
509+
impl<T: std::fmt::Debug> std::fmt::Debug for DbgVal<T> {
510+
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
511+
match self {
512+
Self::Val(val) => val.fmt(fmt),
513+
Self::Underscore => fmt.write_str("_"),
514+
}
515+
}
516+
}
517+
let mut eval_to_int = |op| {
518+
// This can be `None` if the lhs wasn't const propagated and we just
519+
// triggered the assert on the value of the rhs.
520+
self.eval_operand(op, location)
521+
.and_then(|op| self.ecx.read_immediate(&op).ok())
522+
.map_or(DbgVal::Underscore, |op| DbgVal::Val(op.to_const_int()))
523+
};
524+
let msg = match msg {
525+
AssertKind::DivisionByZero(op) => AssertKind::DivisionByZero(eval_to_int(op)),
526+
AssertKind::RemainderByZero(op) => AssertKind::RemainderByZero(eval_to_int(op)),
527+
AssertKind::Overflow(bin_op @ (BinOp::Div | BinOp::Rem), op1, op2) => {
528+
// Division overflow is *UB* in the MIR, and different than the
529+
// other overflow checks.
530+
AssertKind::Overflow(*bin_op, eval_to_int(op1), eval_to_int(op2))
531+
}
532+
AssertKind::BoundsCheck { ref len, ref index } => {
533+
let len = eval_to_int(len);
534+
let index = eval_to_int(index);
535+
AssertKind::BoundsCheck { len, index }
536+
}
537+
// Remaining overflow errors are already covered by checks on the binary operators.
538+
AssertKind::Overflow(..) | AssertKind::OverflowNeg(_) => return None,
539+
// Need proper const propagator for these.
540+
_ => return None,
541+
};
542+
self.report_assert_as_lint(
543+
lint::builtin::UNCONDITIONAL_PANIC,
544+
location,
545+
"this operation will panic at runtime",
546+
msg,
547+
);
548+
}
549+
550+
None
551+
}
552+
483553
fn ensure_not_propagated(&self, local: Local) {
484554
if cfg!(debug_assertions) {
485555
assert!(
@@ -585,78 +655,7 @@ impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
585655
self.super_terminator(terminator, location);
586656
match &terminator.kind {
587657
TerminatorKind::Assert { expected, ref msg, ref cond, .. } => {
588-
if let Some(ref value) = self.eval_operand(&cond, location) {
589-
trace!("assertion on {:?} should be {:?}", value, expected);
590-
let expected = Scalar::from_bool(*expected);
591-
let Ok(value_const) = self.ecx.read_scalar(&value) else {
592-
// FIXME should be used use_ecx rather than a local match... but we have
593-
// quite a few of these read_scalar/read_immediate that need fixing.
594-
return
595-
};
596-
if expected != value_const {
597-
enum DbgVal<T> {
598-
Val(T),
599-
Underscore,
600-
}
601-
impl<T: std::fmt::Debug> std::fmt::Debug for DbgVal<T> {
602-
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
603-
match self {
604-
Self::Val(val) => val.fmt(fmt),
605-
Self::Underscore => fmt.write_str("_"),
606-
}
607-
}
608-
}
609-
let mut eval_to_int = |op| {
610-
// This can be `None` if the lhs wasn't const propagated and we just
611-
// triggered the assert on the value of the rhs.
612-
self.eval_operand(op, location)
613-
.and_then(|op| self.ecx.read_immediate(&op).ok())
614-
.map_or(DbgVal::Underscore, |op| DbgVal::Val(op.to_const_int()))
615-
};
616-
let msg = match msg {
617-
AssertKind::DivisionByZero(op) => {
618-
Some(AssertKind::DivisionByZero(eval_to_int(op)))
619-
}
620-
AssertKind::RemainderByZero(op) => {
621-
Some(AssertKind::RemainderByZero(eval_to_int(op)))
622-
}
623-
AssertKind::Overflow(bin_op @ (BinOp::Div | BinOp::Rem), op1, op2) => {
624-
// Division overflow is *UB* in the MIR, and different than the
625-
// other overflow checks.
626-
Some(AssertKind::Overflow(
627-
*bin_op,
628-
eval_to_int(op1),
629-
eval_to_int(op2),
630-
))
631-
}
632-
AssertKind::BoundsCheck { ref len, ref index } => {
633-
let len = eval_to_int(len);
634-
let index = eval_to_int(index);
635-
Some(AssertKind::BoundsCheck { len, index })
636-
}
637-
// Remaining overflow errors are already covered by checks on the binary operators.
638-
AssertKind::Overflow(..) | AssertKind::OverflowNeg(_) => None,
639-
// Need proper const propagator for these.
640-
_ => None,
641-
};
642-
// Poison all places this operand references so that further code
643-
// doesn't use the invalid value
644-
match cond {
645-
Operand::Move(ref place) | Operand::Copy(ref place) => {
646-
Self::remove_const(&mut self.ecx, place.local);
647-
}
648-
Operand::Constant(_) => {}
649-
}
650-
if let Some(msg) = msg {
651-
self.report_assert_as_lint(
652-
lint::builtin::UNCONDITIONAL_PANIC,
653-
location,
654-
"this operation will panic at runtime",
655-
msg,
656-
);
657-
}
658-
}
659-
}
658+
self.check_assertion(*expected, msg, cond, location);
660659
}
661660
// None of these have Operands to const-propagate.
662661
TerminatorKind::Goto { .. }

0 commit comments

Comments
 (0)