diff --git a/compiler/rustc_mir_transform/src/const_prop_lint.rs b/compiler/rustc_mir_transform/src/const_prop_lint.rs index 99eecb567f277..42ad7cc9f806e 100644 --- a/compiler/rustc_mir_transform/src/const_prop_lint.rs +++ b/compiler/rustc_mir_transform/src/const_prop_lint.rs @@ -17,9 +17,7 @@ use rustc_middle::mir::visit::Visitor; use rustc_middle::mir::*; use rustc_middle::ty::layout::{LayoutError, LayoutOf, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::GenericArgs; -use rustc_middle::ty::{ - self, ConstInt, Instance, ParamEnv, ScalarInt, Ty, TyCtxt, TypeVisitableExt, -}; +use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TyCtxt, TypeVisitableExt}; use rustc_span::Span; use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout}; @@ -312,43 +310,16 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { right: &Operand<'tcx>, location: Location, ) -> Option<()> { + if matches!(op, BinOp::Shr | BinOp::Shl) { + // Shifts are linted in check_assertion() not here + return None; + } + let r = self.use_ecx(location, |this| { this.ecx.read_immediate(&this.ecx.eval_operand(right, None)?) }); let l = self .use_ecx(location, |this| this.ecx.read_immediate(&this.ecx.eval_operand(left, None)?)); - // Check for exceeding shifts *even if* we cannot evaluate the LHS. - if matches!(op, BinOp::Shr | BinOp::Shl) { - let r = r.clone()?; - // 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); - let left_size = self.ecx.layout_of(left_ty).ok()?.size; - let right_size = r.layout.size; - let r_bits = r.to_scalar().to_bits(right_size).ok(); - if r_bits.is_some_and(|b| b >= left_size.bits() as u128) { - debug!("check_binary_op: reporting assert for {:?}", location); - let source_info = self.body().source_info(location); - let panic = AssertKind::Overflow( - op, - match l { - Some(l) => l.to_const_int(), - // Invent a dummy value, the diagnostic ignores it anyway - None => ConstInt::new( - ScalarInt::try_from_uint(1_u8, left_size).unwrap(), - left_ty.is_signed(), - left_ty.is_ptr_sized_integral(), - ), - }, - r.to_const_int(), - ); - self.report_assert_as_lint( - source_info, - AssertLint::ArithmeticOverflow(source_info.span, panic), - ); - return None; - } - } if let (Some(l), Some(r)) = (l, r) { // The remaining operators are handled through `overflowing_binary_op`. @@ -485,18 +456,30 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { .and_then(|op| self.ecx.read_immediate(&op).ok()) .map_or(DbgVal::Underscore, |op| DbgVal::Val(op.to_const_int())) }; - let msg = match msg { - AssertKind::DivisionByZero(op) => AssertKind::DivisionByZero(eval_to_int(op)), - AssertKind::RemainderByZero(op) => AssertKind::RemainderByZero(eval_to_int(op)), + let (msg, emit_overflow_lint) = match msg { + AssertKind::DivisionByZero(op) => { + (AssertKind::DivisionByZero(eval_to_int(op)), false) + } + AssertKind::RemainderByZero(op) => { + (AssertKind::RemainderByZero(eval_to_int(op)), false) + } AssertKind::Overflow(bin_op @ (BinOp::Div | BinOp::Rem), op1, op2) => { // Division overflow is *UB* in the MIR, and different than the // other overflow checks. - AssertKind::Overflow(*bin_op, eval_to_int(op1), eval_to_int(op2)) + (AssertKind::Overflow(*bin_op, eval_to_int(op1), eval_to_int(op2)), false) + } + AssertKind::Overflow(bin_op @ (BinOp::Shl | BinOp::Shr), op1, op2) => { + // A hack that fixes #117949 + // Ideally check_binary_op() should check these shift ops, + // but it can't because they are getting removed from the MIR + // during const promotion. So we check the associated asserts + // here instead as they are not removed by promotion. + (AssertKind::Overflow(*bin_op, eval_to_int(op1), eval_to_int(op2)), true) } AssertKind::BoundsCheck { ref len, ref index } => { let len = eval_to_int(len); let index = eval_to_int(index); - AssertKind::BoundsCheck { len, index } + (AssertKind::BoundsCheck { len, index }, false) } // Remaining overflow errors are already covered by checks on the binary operators. AssertKind::Overflow(..) | AssertKind::OverflowNeg(_) => return None, @@ -506,7 +489,11 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { let source_info = self.body().source_info(location); self.report_assert_as_lint( source_info, - AssertLint::UnconditionalPanic(source_info.span, msg), + if emit_overflow_lint { + AssertLint::ArithmeticOverflow(source_info.span, msg) + } else { + AssertLint::UnconditionalPanic(source_info.span, msg) + }, ); } diff --git a/tests/ui/lint/lint-exceeding-bitshifts.opt.stderr b/tests/ui/lint/lint-exceeding-bitshifts.opt.stderr index 3a84c6c1fb1f9..77cea4b495235 100644 --- a/tests/ui/lint/lint-exceeding-bitshifts.opt.stderr +++ b/tests/ui/lint/lint-exceeding-bitshifts.opt.stderr @@ -10,143 +10,5 @@ note: the lint level is defined here LL | #![warn(arithmetic_overflow)] | ^^^^^^^^^^^^^^^^^^^ -warning: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:22:13 - | -LL | let _ = x << 42; - | ^^^^^^^ attempt to shift left by `42_i32`, which would overflow - -warning: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:27:15 - | -LL | let n = 1u8 << 8; - | ^^^^^^^^ attempt to shift left by `8_i32`, which would overflow - -warning: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:29:15 - | -LL | let n = 1u16 << 16; - | ^^^^^^^^^^ attempt to shift left by `16_i32`, which would overflow - -warning: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:31:15 - | -LL | let n = 1u32 << 32; - | ^^^^^^^^^^ attempt to shift left by `32_i32`, which would overflow - -warning: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:33:15 - | -LL | let n = 1u64 << 64; - | ^^^^^^^^^^ attempt to shift left by `64_i32`, which would overflow - -warning: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:35:15 - | -LL | let n = 1i8 << 8; - | ^^^^^^^^ attempt to shift left by `8_i32`, which would overflow - -warning: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:37:15 - | -LL | let n = 1i16 << 16; - | ^^^^^^^^^^ attempt to shift left by `16_i32`, which would overflow - -warning: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:39:15 - | -LL | let n = 1i32 << 32; - | ^^^^^^^^^^ attempt to shift left by `32_i32`, which would overflow - -warning: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:41:15 - | -LL | let n = 1i64 << 64; - | ^^^^^^^^^^ attempt to shift left by `64_i32`, which would overflow - -warning: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:44:15 - | -LL | let n = 1u8 >> 8; - | ^^^^^^^^ attempt to shift right by `8_i32`, which would overflow - -warning: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:46:15 - | -LL | let n = 1u16 >> 16; - | ^^^^^^^^^^ attempt to shift right by `16_i32`, which would overflow - -warning: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:48:15 - | -LL | let n = 1u32 >> 32; - | ^^^^^^^^^^ attempt to shift right by `32_i32`, which would overflow - -warning: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:50:15 - | -LL | let n = 1u64 >> 64; - | ^^^^^^^^^^ attempt to shift right by `64_i32`, which would overflow - -warning: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:52:15 - | -LL | let n = 1i8 >> 8; - | ^^^^^^^^ attempt to shift right by `8_i32`, which would overflow - -warning: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:54:15 - | -LL | let n = 1i16 >> 16; - | ^^^^^^^^^^ attempt to shift right by `16_i32`, which would overflow - -warning: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:56:15 - | -LL | let n = 1i32 >> 32; - | ^^^^^^^^^^ attempt to shift right by `32_i32`, which would overflow - -warning: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:58:15 - | -LL | let n = 1i64 >> 64; - | ^^^^^^^^^^ attempt to shift right by `64_i32`, which would overflow - -warning: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:62:15 - | -LL | let n = n << 8; - | ^^^^^^ attempt to shift left by `8_i32`, which would overflow - -warning: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:64:15 - | -LL | let n = 1u8 << -8; - | ^^^^^^^^^ attempt to shift left by `-8_i32`, which would overflow - -warning: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:69:15 - | -LL | let n = 1u8 << (4+4); - | ^^^^^^^^^^^^ attempt to shift left by `8_i32`, which would overflow - -warning: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:71:15 - | -LL | let n = 1i64 >> [64][0]; - | ^^^^^^^^^^^^^^^ attempt to shift right by `64_i32`, which would overflow - -warning: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:77:15 - | -LL | let n = 1_isize << BITS; - | ^^^^^^^^^^^^^^^ attempt to shift left by `%BITS%`, which would overflow - -warning: this arithmetic operation will overflow - --> $DIR/lint-exceeding-bitshifts.rs:78:15 - | -LL | let n = 1_usize << BITS; - | ^^^^^^^^^^^^^^^ attempt to shift left by `%BITS%`, which would overflow - -warning: 24 warnings emitted +warning: 1 warning emitted