Skip to content

Commit fd20dbe

Browse files
committed
Improve comments and structure of ConstProp::const_prop()
Per code review feedback
1 parent b71ea80 commit fd20dbe

File tree

1 file changed

+50
-32
lines changed

1 file changed

+50
-32
lines changed

src/librustc_mir/transform/const_prop.rs

+50-32
Original file line numberDiff line numberDiff line change
@@ -434,33 +434,41 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
434434
) -> Option<Const<'tcx>> {
435435
let span = source_info.span;
436436

437-
// perform any special checking for specific Rvalue types
437+
let overflow_check = self.tcx.sess.overflow_checks();
438+
439+
// Perform any special handling for specific Rvalue types.
440+
// Generally, checks here fall into one of two categories:
441+
// 1. Additional checking to provide useful lints to the user
442+
// - In this case, we will do some validation and then fall through to the
443+
// end of the function which evals the assignment.
444+
// 2. Working around bugs in other parts of the compiler
445+
// - In this case, we'll return `None` from this function to stop evaluation.
438446
match rvalue {
439-
Rvalue::UnaryOp(UnOp::Neg, arg) => {
447+
// Additional checking: if overflow checks are disabled (which is usually the case in
448+
// release mode), then we need to do additional checking here to give lints to the user
449+
// if an overflow would occur.
450+
Rvalue::UnaryOp(UnOp::Neg, arg) if !overflow_check => {
440451
trace!("checking UnaryOp(op = Neg, arg = {:?})", arg);
441-
let overflow_check = self.tcx.sess.overflow_checks();
442452

443453
self.use_ecx(source_info, |this| {
444-
// We check overflow in debug mode already
445-
// so should only check in release mode.
446-
if !overflow_check {
447-
let ty = arg.ty(&this.local_decls, this.tcx);
448-
449-
if ty.is_integral() {
450-
let arg = this.ecx.eval_operand(arg, None)?;
451-
let prim = this.ecx.read_immediate(arg)?;
452-
// Need to do overflow check here: For actual CTFE, MIR
453-
// generation emits code that does this before calling the op.
454-
if prim.to_bits()? == (1 << (prim.layout.size.bits() - 1)) {
455-
throw_panic!(OverflowNeg)
456-
}
454+
let ty = arg.ty(&this.local_decls, this.tcx);
455+
456+
if ty.is_integral() {
457+
let arg = this.ecx.eval_operand(arg, None)?;
458+
let prim = this.ecx.read_immediate(arg)?;
459+
// Need to do overflow check here: For actual CTFE, MIR
460+
// generation emits code that does this before calling the op.
461+
if prim.to_bits()? == (1 << (prim.layout.size.bits() - 1)) {
462+
throw_panic!(OverflowNeg)
457463
}
458464
}
459465

460466
Ok(())
461467
})?;
462468
}
463469

470+
// Additional checking: check for overflows on integer binary operations and report
471+
// them to the user as lints.
464472
Rvalue::BinaryOp(op, left, right) => {
465473
trace!("checking BinaryOp(op = {:?}, left = {:?}, right = {:?})", op, left, right);
466474

@@ -490,25 +498,34 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
490498
return None;
491499
}
492500
}
493-
self.use_ecx(source_info, |this| {
494-
let l = this.ecx.read_immediate(this.ecx.eval_operand(left, None)?)?;
495-
let (_, overflow, _ty) = this.ecx.overflowing_binary_op(*op, l, r)?;
496-
497-
// We check overflow in debug mode already
498-
// so should only check in release mode.
499-
if !this.tcx.sess.overflow_checks() && overflow {
500-
let err = err_panic!(Overflow(*op)).into();
501-
return Err(err);
502-
}
503501

504-
Ok(())
505-
})?;
502+
// If overflow checking is enabled (like in debug mode by default),
503+
// then we'll already catch overflow when we evaluate the `Assert` statement
504+
// in MIR. However, if overflow checking is disabled, then there won't be any
505+
// `Assert` statement and so we have to do additional checking here.
506+
if !overflow_check {
507+
self.use_ecx(source_info, |this| {
508+
let l = this.ecx.read_immediate(this.ecx.eval_operand(left, None)?)?;
509+
let (_, overflow, _ty) = this.ecx.overflowing_binary_op(*op, l, r)?;
510+
511+
if overflow {
512+
let err = err_panic!(Overflow(*op)).into();
513+
return Err(err);
514+
}
515+
516+
Ok(())
517+
})?;
518+
}
506519
}
507520

521+
// Work around: avoid ICE in miri.
522+
// FIXME(wesleywiser) we don't currently handle the case where we try to make a ref
523+
// from a function argument that hasn't been assigned to in this function. The main
524+
// issue is if an arg is a fat-pointer, miri `expects()` to be able to read the value
525+
// of that pointer to get size info. However, since this is `ConstProp`, that argument
526+
// doesn't actually have a backing value and so this causes an ICE.
508527
Rvalue::Ref(_, _, Place { base: PlaceBase::Local(local), projection: box [] }) => {
509528
trace!("checking Ref({:?})", place);
510-
// FIXME(wesleywiser) we don't currently handle the case where we try to make a ref
511-
// from a function argument that hasn't been assigned to in this function.
512529
let alive =
513530
if let LocalValue::Live(_) = self.ecx.frame().locals[*local].value {
514531
true
@@ -520,9 +537,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
520537
}
521538
}
522539

540+
// Work around: avoid extra unnecessary locals.
541+
// FIXME(wesleywiser): const eval will turn this into a `const Scalar(<ZST>)` that
542+
// `SimplifyLocals` doesn't know it can remove.
523543
Rvalue::Aggregate(_, operands) if operands.len() == 0 => {
524-
// FIXME(wesleywiser): const eval will turn this into a `const Scalar(<ZST>)` that
525-
// `SimplifyLocals` doesn't know it can remove.
526544
return None;
527545
}
528546

0 commit comments

Comments
 (0)