diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 237a5a64f8b..c7bf2e963ec 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -28,9 +28,9 @@ use rustc_trait_selection::traits; use crate::const_eval::error_to_const_error; use crate::interpret::{ - self, compile_time_machine, AllocId, Allocation, Frame, ImmTy, Immediate, InterpCx, LocalState, - LocalValue, MemPlace, Memory, MemoryKind, OpTy, Operand as InterpOperand, PlaceTy, Pointer, - ScalarMaybeUninit, StackPopCleanup, + self, compile_time_machine, truncate, AllocId, Allocation, Frame, ImmTy, Immediate, InterpCx, + LocalState, LocalValue, MemPlace, Memory, MemoryKind, OpTy, Operand as InterpOperand, PlaceTy, + Pointer, ScalarMaybeUninit, StackPopCleanup, }; use crate::transform::{MirPass, MirSource}; @@ -527,11 +527,11 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { right: &Operand<'tcx>, source_info: SourceInfo, ) -> Option<()> { - let r = - self.use_ecx(|this| this.ecx.read_immediate(this.ecx.eval_operand(right, None)?))?; + let r = self.use_ecx(|this| this.ecx.read_immediate(this.ecx.eval_operand(right, None)?)); let l = self.use_ecx(|this| this.ecx.read_immediate(this.ecx.eval_operand(left, None)?)); // Check for exceeding shifts *even if* we cannot evaluate the LHS. if op == BinOp::Shr || op == BinOp::Shl { + let r = r?; // We need the type of the LHS. We cannot use `place_layout` as that is the type // of the result, which for checked binops is not the same! let left_ty = left.ty(&self.local_decls, self.tcx); @@ -564,21 +564,20 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } } - let l = l?; - - // The remaining operators are handled through `overflowing_binary_op`. - if self.use_ecx(|this| { - let (_res, overflow, _ty) = this.ecx.overflowing_binary_op(op, l, r)?; - Ok(overflow) - })? { - self.report_assert_as_lint( - lint::builtin::ARITHMETIC_OVERFLOW, - source_info, - "this arithmetic operation will overflow", - AssertKind::Overflow(op, l.to_const_int(), r.to_const_int()), - )?; + if let (Some(l), Some(r)) = (l, r) { + // The remaining operators are handled through `overflowing_binary_op`. + if self.use_ecx(|this| { + let (_res, overflow, _ty) = this.ecx.overflowing_binary_op(op, l, r)?; + Ok(overflow) + })? { + self.report_assert_as_lint( + lint::builtin::ARITHMETIC_OVERFLOW, + source_info, + "this arithmetic operation will overflow", + AssertKind::Overflow(op, l.to_const_int(), r.to_const_int()), + )?; + } } - Some(()) } @@ -659,9 +658,74 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { return None; } + if self.tcx.sess.opts.debugging_opts.mir_opt_level >= 3 { + self.eval_rvalue_with_identities(rvalue, place) + } else { + self.use_ecx(|this| this.ecx.eval_rvalue_into_place(rvalue, place)) + } + } + + // Attempt to use albegraic identities to eliminate constant expressions + fn eval_rvalue_with_identities( + &mut self, + rvalue: &Rvalue<'tcx>, + place: Place<'tcx>, + ) -> Option<()> { self.use_ecx(|this| { - trace!("calling eval_rvalue_into_place(rvalue = {:?}, place = {:?})", rvalue, place); - this.ecx.eval_rvalue_into_place(rvalue, place)?; + match rvalue { + Rvalue::BinaryOp(op, left, right) | Rvalue::CheckedBinaryOp(op, left, right) => { + let l = this.ecx.eval_operand(left, None); + let r = this.ecx.eval_operand(right, None); + + let const_arg = match (l, r) { + (Ok(x), Err(_)) | (Err(_), Ok(x)) => this.ecx.read_immediate(x)?, + (Err(e), Err(_)) => return Err(e), + (Ok(_), Ok(_)) => { + this.ecx.eval_rvalue_into_place(rvalue, place)?; + return Ok(()); + } + }; + + let arg_value = + this.ecx.force_bits(const_arg.to_scalar()?, const_arg.layout.size)?; + let dest = this.ecx.eval_place(place)?; + + match op { + BinOp::BitAnd => { + if arg_value == 0 { + this.ecx.write_immediate(*const_arg, dest)?; + } + } + BinOp::BitOr => { + if arg_value == truncate(u128::MAX, const_arg.layout.size) + || (const_arg.layout.ty.is_bool() && arg_value == 1) + { + this.ecx.write_immediate(*const_arg, dest)?; + } + } + BinOp::Mul => { + if const_arg.layout.ty.is_integral() && arg_value == 0 { + if let Rvalue::CheckedBinaryOp(_, _, _) = rvalue { + let val = Immediate::ScalarPair( + const_arg.to_scalar()?.into(), + Scalar::from_bool(false).into(), + ); + this.ecx.write_immediate(val, dest)?; + } else { + this.ecx.write_immediate(*const_arg, dest)?; + } + } + } + _ => { + this.ecx.eval_rvalue_into_place(rvalue, place)?; + } + } + } + _ => { + this.ecx.eval_rvalue_into_place(rvalue, place)?; + } + } + Ok(()) }) } diff --git a/src/test/mir-opt/const_prop/boolean_identities.rs b/src/test/mir-opt/const_prop/boolean_identities.rs new file mode 100644 index 00000000000..4e09acbaa53 --- /dev/null +++ b/src/test/mir-opt/const_prop/boolean_identities.rs @@ -0,0 +1,10 @@ +// compile-flags: -O -Zmir-opt-level=3 + +// EMIT_MIR rustc.test.ConstProp.diff +pub fn test(x: bool, y: bool) -> bool { + (y | true) & (x & false) +} + +fn main() { + test(true, false); +} diff --git a/src/test/mir-opt/const_prop/boolean_identities/rustc.test.ConstProp.diff b/src/test/mir-opt/const_prop/boolean_identities/rustc.test.ConstProp.diff new file mode 100644 index 00000000000..b8f0ad4d434 --- /dev/null +++ b/src/test/mir-opt/const_prop/boolean_identities/rustc.test.ConstProp.diff @@ -0,0 +1,53 @@ +- // MIR for `test` before ConstProp ++ // MIR for `test` after ConstProp + + fn test(_1: bool, _2: bool) -> bool { + debug x => _1; // in scope 0 at $DIR/boolean_identities.rs:4:13: 4:14 + debug y => _2; // in scope 0 at $DIR/boolean_identities.rs:4:22: 4:23 + let mut _0: bool; // return place in scope 0 at $DIR/boolean_identities.rs:4:34: 4:38 + let mut _3: bool; // in scope 0 at $DIR/boolean_identities.rs:5:5: 5:15 + let mut _4: bool; // in scope 0 at $DIR/boolean_identities.rs:5:6: 5:7 + let mut _5: bool; // in scope 0 at $DIR/boolean_identities.rs:5:18: 5:29 + let mut _6: bool; // in scope 0 at $DIR/boolean_identities.rs:5:19: 5:20 + + bb0: { + StorageLive(_3); // scope 0 at $DIR/boolean_identities.rs:5:5: 5:15 + StorageLive(_4); // scope 0 at $DIR/boolean_identities.rs:5:6: 5:7 + _4 = _2; // scope 0 at $DIR/boolean_identities.rs:5:6: 5:7 +- _3 = BitOr(move _4, const true); // scope 0 at $DIR/boolean_identities.rs:5:5: 5:15 ++ _3 = const true; // scope 0 at $DIR/boolean_identities.rs:5:5: 5:15 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x01)) + // mir::Constant +- // + span: $DIR/boolean_identities.rs:5:10: 5:14 ++ // + span: $DIR/boolean_identities.rs:5:5: 5:15 + // + literal: Const { ty: bool, val: Value(Scalar(0x01)) } + StorageDead(_4); // scope 0 at $DIR/boolean_identities.rs:5:14: 5:15 + StorageLive(_5); // scope 0 at $DIR/boolean_identities.rs:5:18: 5:29 + StorageLive(_6); // scope 0 at $DIR/boolean_identities.rs:5:19: 5:20 + _6 = _1; // scope 0 at $DIR/boolean_identities.rs:5:19: 5:20 +- _5 = BitAnd(move _6, const false); // scope 0 at $DIR/boolean_identities.rs:5:18: 5:29 ++ _5 = const false; // scope 0 at $DIR/boolean_identities.rs:5:18: 5:29 + // ty::Const + // + ty: bool + // + val: Value(Scalar(0x00)) + // mir::Constant +- // + span: $DIR/boolean_identities.rs:5:23: 5:28 ++ // + span: $DIR/boolean_identities.rs:5:18: 5:29 + // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + StorageDead(_6); // scope 0 at $DIR/boolean_identities.rs:5:28: 5:29 +- _0 = BitAnd(move _3, move _5); // scope 0 at $DIR/boolean_identities.rs:5:5: 5:29 ++ _0 = const false; // scope 0 at $DIR/boolean_identities.rs:5:5: 5:29 ++ // ty::Const ++ // + ty: bool ++ // + val: Value(Scalar(0x00)) ++ // mir::Constant ++ // + span: $DIR/boolean_identities.rs:5:5: 5:29 ++ // + literal: Const { ty: bool, val: Value(Scalar(0x00)) } + StorageDead(_5); // scope 0 at $DIR/boolean_identities.rs:5:28: 5:29 + StorageDead(_3); // scope 0 at $DIR/boolean_identities.rs:5:28: 5:29 + return; // scope 0 at $DIR/boolean_identities.rs:6:2: 6:2 + } + } + diff --git a/src/test/mir-opt/const_prop/mult_by_zero.rs b/src/test/mir-opt/const_prop/mult_by_zero.rs new file mode 100644 index 00000000000..f40faee3110 --- /dev/null +++ b/src/test/mir-opt/const_prop/mult_by_zero.rs @@ -0,0 +1,10 @@ +// compile-flags: -O -Zmir-opt-level=3 + +// EMIT_MIR rustc.test.ConstProp.diff +fn test(x : i32) -> i32 { + x * 0 +} + +fn main() { + test(10); +} diff --git a/src/test/mir-opt/const_prop/mult_by_zero/rustc.test.ConstProp.diff b/src/test/mir-opt/const_prop/mult_by_zero/rustc.test.ConstProp.diff new file mode 100644 index 00000000000..7b36669bf15 --- /dev/null +++ b/src/test/mir-opt/const_prop/mult_by_zero/rustc.test.ConstProp.diff @@ -0,0 +1,25 @@ +- // MIR for `test` before ConstProp ++ // MIR for `test` after ConstProp + + fn test(_1: i32) -> i32 { + debug x => _1; // in scope 0 at $DIR/mult_by_zero.rs:4:9: 4:10 + let mut _0: i32; // return place in scope 0 at $DIR/mult_by_zero.rs:4:21: 4:24 + let mut _2: i32; // in scope 0 at $DIR/mult_by_zero.rs:5:3: 5:4 + + bb0: { + StorageLive(_2); // scope 0 at $DIR/mult_by_zero.rs:5:3: 5:4 + _2 = _1; // scope 0 at $DIR/mult_by_zero.rs:5:3: 5:4 +- _0 = Mul(move _2, const 0_i32); // scope 0 at $DIR/mult_by_zero.rs:5:3: 5:8 ++ _0 = const 0_i32; // scope 0 at $DIR/mult_by_zero.rs:5:3: 5:8 + // ty::Const + // + ty: i32 + // + val: Value(Scalar(0x00000000)) + // mir::Constant +- // + span: $DIR/mult_by_zero.rs:5:7: 5:8 ++ // + span: $DIR/mult_by_zero.rs:5:3: 5:8 + // + literal: Const { ty: i32, val: Value(Scalar(0x00000000)) } + StorageDead(_2); // scope 0 at $DIR/mult_by_zero.rs:5:7: 5:8 + return; // scope 0 at $DIR/mult_by_zero.rs:6:2: 6:2 + } + } + diff --git a/src/test/ui/consts/const-eval/index-out-of-bounds-never-type.rs b/src/test/ui/consts/const-eval/index-out-of-bounds-never-type.rs index 2a983e42683..795c5154f81 100644 --- a/src/test/ui/consts/const-eval/index-out-of-bounds-never-type.rs +++ b/src/test/ui/consts/const-eval/index-out-of-bounds-never-type.rs @@ -1,7 +1,7 @@ // build-fail // Regression test for #66975 -#![warn(const_err)] +#![warn(const_err, unconditional_panic)] #![feature(never_type)] struct PrintName(T); @@ -9,6 +9,7 @@ struct PrintName(T); impl PrintName { const VOID: ! = { let x = 0 * std::mem::size_of::(); [][x] }; //~^ WARN any use of this value will cause an error + } fn f() { diff --git a/src/test/ui/consts/const-eval/index-out-of-bounds-never-type.stderr b/src/test/ui/consts/const-eval/index-out-of-bounds-never-type.stderr index d78e0da00f5..33e60dd7c91 100644 --- a/src/test/ui/consts/const-eval/index-out-of-bounds-never-type.stderr +++ b/src/test/ui/consts/const-eval/index-out-of-bounds-never-type.stderr @@ -9,11 +9,11 @@ LL | const VOID: ! = { let x = 0 * std::mem::size_of::(); [][x] }; note: the lint level is defined here --> $DIR/index-out-of-bounds-never-type.rs:4:9 | -LL | #![warn(const_err)] +LL | #![warn(const_err, unconditional_panic)] | ^^^^^^^^^ error: erroneous constant encountered - --> $DIR/index-out-of-bounds-never-type.rs:15:13 + --> $DIR/index-out-of-bounds-never-type.rs:16:13 | LL | let _ = PrintName::::VOID; | ^^^^^^^^^^^^^^^^^^^^