From 202d401c2504f17133c50505b82fe4278ab2c842 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 9 Feb 2020 15:23:34 +0100 Subject: [PATCH 1/8] miri: simplify singed operator overflow detection --- src/librustc_mir/interpret/operator.rs | 38 ++++++++++---------------- 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/src/librustc_mir/interpret/operator.rs b/src/librustc_mir/interpret/operator.rs index 5050fe4906474..b4f6b5f89996b 100644 --- a/src/librustc_mir/interpret/operator.rs +++ b/src/librustc_mir/interpret/operator.rs @@ -134,9 +134,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { let mut r = r as u32; let size = left_layout.size; oflo |= r >= size.bits() as u32; - if oflo { - r %= size.bits() as u32; - } + r %= size.bits() as u32; let result = if signed { let l = self.sign_extend(l, left_layout) as i128; let result = match bin_op { @@ -168,6 +166,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { ) } + let size = left_layout.size; + // Operations that need special treatment for signed integers if left_layout.abi.is_signed() { let op: Option bool> = match bin_op { @@ -195,32 +195,20 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { if let Some(op) = op { let l128 = self.sign_extend(l, left_layout) as i128; let r = self.sign_extend(r, right_layout) as i128; - let size = left_layout.size; - match bin_op { - Rem | Div => { - // int_min / -1 - if r == -1 && l == (1 << (size.bits() - 1)) { - return Ok((Scalar::from_uint(l, size), true, left_layout.ty)); - } - } - _ => {} - } - trace!("{}, {}, {}", l, l128, r); - let (result, mut oflo) = op(l128, r); - trace!("{}, {}", result, oflo); - if !oflo && size.bits() != 128 { - let max = 1 << (size.bits() - 1); - oflo = result >= max || result < -max; - } - // this may be out-of-bounds for the result type, so we have to truncate ourselves + + let (result, oflo) = op(l128, r); + // This may be out-of-bounds for the result type, so we have to truncate ourselves. + // If that truncation loses any information, we have an overflow. let result = result as u128; let truncated = self.truncate(result, left_layout); - return Ok((Scalar::from_uint(truncated, size), oflo, left_layout.ty)); + return Ok(( + Scalar::from_uint(truncated, size), + oflo || self.sign_extend(truncated, left_layout) != result, + left_layout.ty, + )); } } - let size = left_layout.size; - let (val, ty) = match bin_op { Eq => (Scalar::from_bool(l == r), self.tcx.types.bool), Ne => (Scalar::from_bool(l != r), self.tcx.types.bool), @@ -247,6 +235,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { _ => bug!(), }; let (result, oflo) = op(l, r); + // Truncate to target type. + // If that truncation loses any information, we have an overflow. let truncated = self.truncate(result, left_layout); return Ok(( Scalar::from_uint(truncated, size), From 28f85c6ffad77554150e7cab4ccac38b26621bdb Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 9 Feb 2020 15:41:40 +0100 Subject: [PATCH 2/8] bring back extra check for int_min%-1 --- src/librustc_mir/interpret/operator.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/librustc_mir/interpret/operator.rs b/src/librustc_mir/interpret/operator.rs index b4f6b5f89996b..abe437bd8d7c6 100644 --- a/src/librustc_mir/interpret/operator.rs +++ b/src/librustc_mir/interpret/operator.rs @@ -195,6 +195,17 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { if let Some(op) = op { let l128 = self.sign_extend(l, left_layout) as i128; let r = self.sign_extend(r, right_layout) as i128; + // We need a special check for overflowing remainder: + // "int_min % -1" overflows and returns 0, but after casting things to a larger int + // type it does *not* overflow nor give an unrepresentable result! + match bin_op { + Rem => { + if r == -1 && l == (1 << (size.bits() - 1)) { + return Ok((Scalar::from_int(0, size), true, left_layout.ty)); + } + } + _ => {} + } let (result, oflo) = op(l128, r); // This may be out-of-bounds for the result type, so we have to truncate ourselves. From 7d2f6ae00149e4fdfeb9eedc9cb7433f6e67cf42 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 8 Feb 2020 23:27:29 +0100 Subject: [PATCH 3/8] miri: equip unary_op with overflow detection --- src/librustc_mir/interpret/operator.rs | 36 +++++++++++++++++++------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/src/librustc_mir/interpret/operator.rs b/src/librustc_mir/interpret/operator.rs index abe437bd8d7c6..9a3c08248b5ef 100644 --- a/src/librustc_mir/interpret/operator.rs +++ b/src/librustc_mir/interpret/operator.rs @@ -342,7 +342,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { } } - /// Typed version of `checked_binary_op`, returning an `ImmTy`. Also ignores overflows. + /// Typed version of `overflowing_binary_op`, returning an `ImmTy`. Also ignores overflows. #[inline] pub fn binary_op( &self, @@ -354,11 +354,13 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(ImmTy::from_scalar(val, self.layout_of(ty)?)) } - pub fn unary_op( + /// Returns the result of the specified operation, whether it overflowed, and + /// the result type. + pub fn overflowing_unary_op( &self, un_op: mir::UnOp, val: ImmTy<'tcx, M::PointerTag>, - ) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> { + ) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> { use rustc::mir::UnOp::*; let layout = val.layout; @@ -372,7 +374,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Not => !val, _ => bug!("Invalid bool op {:?}", un_op), }; - Ok(ImmTy::from_scalar(Scalar::from_bool(res), self.layout_of(self.tcx.types.bool)?)) + Ok((Scalar::from_bool(res), false, self.tcx.types.bool)) } ty::Float(fty) => { let res = match (un_op, fty) { @@ -380,21 +382,37 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { (Neg, FloatTy::F64) => Scalar::from_f64(-val.to_f64()?), _ => bug!("Invalid float op {:?}", un_op), }; - Ok(ImmTy::from_scalar(res, layout)) + Ok((res, false, layout.ty)) } _ => { assert!(layout.ty.is_integral()); let val = self.force_bits(val, layout.size)?; - let res = match un_op { - Not => !val, + let (res, overflow) = match un_op { + Not => (self.truncate(!val, layout), false), // bitwise negation, then truncate Neg => { + // arithmetic negation assert!(layout.abi.is_signed()); - (-(val as i128)) as u128 + let val = self.sign_extend(val, layout) as i128; + let (res, overflow) = val.overflowing_neg(); + let res = res as u128; + // Truncate to target type. + // If that truncation loses any information, we have an overflow. + let truncated = self.truncate(res, layout); + (truncated, overflow || self.sign_extend(truncated, layout) != res) } }; // res needs tuncating - Ok(ImmTy::from_uint(self.truncate(res, layout), layout)) + Ok((Scalar::from_uint(res, layout.size), overflow, layout.ty)) } } } + + pub fn unary_op( + &self, + un_op: mir::UnOp, + val: ImmTy<'tcx, M::PointerTag>, + ) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> { + let (val, _overflow, ty) = self.overflowing_unary_op(un_op, val)?; + Ok(ImmTy::from_scalar(val, self.layout_of(ty)?)) + } } From ae23f7020a5cb9a201e83f20f151282368b1f494 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 9 Feb 2020 15:48:18 +0100 Subject: [PATCH 4/8] const-prop: use overflowing_unary_op for overflowing checking of unary ops --- src/librustc_mir/transform/const_prop.rs | 34 ++++++++++++------------ 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index d645f6cf183b4..d09165f904ab2 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -518,18 +518,19 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { } } - fn check_unary_op(&mut self, arg: &Operand<'tcx>, source_info: SourceInfo) -> Option<()> { + fn check_unary_op( + &mut self, + op: UnOp, + arg: &Operand<'tcx>, + source_info: SourceInfo, + ) -> Option<()> { self.use_ecx(source_info, |this| { - let ty = arg.ty(&this.local_decls, this.tcx); - - if ty.is_integral() { - let arg = this.ecx.eval_operand(arg, None)?; - let prim = this.ecx.read_immediate(arg)?; - // Need to do overflow check here: For actual CTFE, MIR - // generation emits code that does this before calling the op. - if prim.to_bits()? == (1 << (prim.layout.size.bits() - 1)) { - throw_panic!(OverflowNeg) - } + let val = this.ecx.read_immediate(this.ecx.eval_operand(arg, None)?)?; + let (_res, overflow, _ty) = this.ecx.overflowing_unary_op(op, val)?; + + if overflow { + assert_eq!(op, UnOp::Neg, "Neg is the only UnOp that can overflow"); + throw_panic!(OverflowNeg); } Ok(()) @@ -574,11 +575,10 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { if !overflow_check { self.use_ecx(source_info, |this| { let l = this.ecx.read_immediate(this.ecx.eval_operand(left, None)?)?; - let (_, overflow, _ty) = this.ecx.overflowing_binary_op(op, l, r)?; + let (_res, overflow, _ty) = this.ecx.overflowing_binary_op(op, l, r)?; if overflow { - let err = err_panic!(Overflow(op)).into(); - return Err(err); + throw_panic!(Overflow(op)); } Ok(()) @@ -618,9 +618,9 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { // Additional checking: if overflow checks are disabled (which is usually the case in // release mode), then we need to do additional checking here to give lints to the user // if an overflow would occur. - Rvalue::UnaryOp(UnOp::Neg, arg) if !overflow_check => { - trace!("checking UnaryOp(op = Neg, arg = {:?})", arg); - self.check_unary_op(arg, source_info)?; + Rvalue::UnaryOp(op, arg) if !overflow_check => { + trace!("checking UnaryOp(op = {:?}, arg = {:?})", op, arg); + self.check_unary_op(*op, arg, source_info)?; } // Additional checking: check for overflows on integer binary operations and report From b434d7ef8ae19c145dd9348b70bb955147dfab70 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 10 Feb 2020 09:56:17 +0100 Subject: [PATCH 5/8] add test that checks overflows on arithmetic operators --- .../consts/const-int-arithmetic-overflow.rs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 src/test/ui/consts/const-int-arithmetic-overflow.rs diff --git a/src/test/ui/consts/const-int-arithmetic-overflow.rs b/src/test/ui/consts/const-int-arithmetic-overflow.rs new file mode 100644 index 0000000000000..75dac812f1e3a --- /dev/null +++ b/src/test/ui/consts/const-int-arithmetic-overflow.rs @@ -0,0 +1,26 @@ +// run-pass +// compile-flags: -O +#![allow(const_err)] + +// Make sure arithmetic unary/binary ops actually return the right result, even when overflowing. +// We have to put them in `const fn` and turn on optimizations to avoid overflow checks. + +const fn add(x: i8, y: i8) -> i8 { x+y } +const fn sub(x: i8, y: i8) -> i8 { x-y } +const fn mul(x: i8, y: i8) -> i8 { x*y } +// div and rem are always checked, so we cannot test their result in case of oveflow. +const fn neg(x: i8) -> i8 { -x } + +fn main() { + const ADD_OFLOW: i8 = add(100, 100); + assert_eq!(ADD_OFLOW, -56); + + const SUB_OFLOW: i8 = sub(100, -100); + assert_eq!(SUB_OFLOW, -56); + + const MUL_OFLOW: i8 = mul(-100, -2); + assert_eq!(MUL_OFLOW, -56); + + const NEG_OFLOW: i8 = neg(-128); + assert_eq!(NEG_OFLOW, -128); +} From 1ddb0503ff1e203de40f5bbc1e0b00d1b4e99d12 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 10 Feb 2020 11:23:04 +0100 Subject: [PATCH 6/8] div/rem overflow tests: also test i128 --- src/test/ui/issues/issue-8460-const.rs | 12 +++- src/test/ui/issues/issue-8460-const.stderr | 78 +++++++++++++++------ src/test/ui/issues/issue-8460-const2.rs | 10 ++- src/test/ui/issues/issue-8460-const2.stderr | 56 ++++++++++----- 4 files changed, 117 insertions(+), 39 deletions(-) diff --git a/src/test/ui/issues/issue-8460-const.rs b/src/test/ui/issues/issue-8460-const.rs index c18a0d4d6cbbe..5866cef2d2c70 100644 --- a/src/test/ui/issues/issue-8460-const.rs +++ b/src/test/ui/issues/issue-8460-const.rs @@ -3,7 +3,7 @@ #![deny(const_err)] -use std::{isize, i8, i16, i32, i64}; +use std::{isize, i8, i16, i32, i64, i128}; use std::thread; fn main() { @@ -22,6 +22,9 @@ fn main() { assert!(thread::spawn(move|| { i64::MIN / -1; }).join().is_err()); //~^ ERROR attempt to divide with overflow //~| ERROR this expression will panic at runtime + assert!(thread::spawn(move|| { i128::MIN / -1; }).join().is_err()); + //~^ ERROR attempt to divide with overflow + //~| ERROR this expression will panic at runtime assert!(thread::spawn(move|| { 1isize / 0; }).join().is_err()); //~^ ERROR attempt to divide by zero assert!(thread::spawn(move|| { 1i8 / 0; }).join().is_err()); @@ -32,6 +35,8 @@ fn main() { //~^ ERROR attempt to divide by zero assert!(thread::spawn(move|| { 1i64 / 0; }).join().is_err()); //~^ ERROR attempt to divide by zero + assert!(thread::spawn(move|| { 1i128 / 0; }).join().is_err()); + //~^ ERROR attempt to divide by zero assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with overflow //~| ERROR this expression will panic at runtime @@ -47,6 +52,9 @@ fn main() { assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with overflow //~| ERROR this expression will panic at runtime + assert!(thread::spawn(move|| { i128::MIN % -1; }).join().is_err()); + //~^ ERROR attempt to calculate the remainder with overflow + //~| ERROR this expression will panic at runtime assert!(thread::spawn(move|| { 1isize % 0; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with a divisor of zero assert!(thread::spawn(move|| { 1i8 % 0; }).join().is_err()); @@ -57,4 +65,6 @@ fn main() { //~^ ERROR attempt to calculate the remainder with a divisor of zero assert!(thread::spawn(move|| { 1i64 % 0; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with a divisor of zero + assert!(thread::spawn(move|| { 1i128 % 0; }).join().is_err()); + //~^ ERROR attempt to calculate the remainder with a divisor of zero } diff --git a/src/test/ui/issues/issue-8460-const.stderr b/src/test/ui/issues/issue-8460-const.stderr index 6b1d74094a10b..d7373948cb9e0 100644 --- a/src/test/ui/issues/issue-8460-const.stderr +++ b/src/test/ui/issues/issue-8460-const.stderr @@ -64,125 +64,161 @@ error: this expression will panic at runtime LL | assert!(thread::spawn(move|| { i64::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to divide with overflow -error: attempt to divide by zero +error: attempt to divide with overflow + --> $DIR/issue-8460-const.rs:25:36 + | +LL | assert!(thread::spawn(move|| { i128::MIN / -1; }).join().is_err()); + | ^^^^^^^^^^^^^^ + +error: this expression will panic at runtime --> $DIR/issue-8460-const.rs:25:36 | +LL | assert!(thread::spawn(move|| { i128::MIN / -1; }).join().is_err()); + | ^^^^^^^^^^^^^^ attempt to divide with overflow + +error: attempt to divide by zero + --> $DIR/issue-8460-const.rs:28:36 + | LL | assert!(thread::spawn(move|| { 1isize / 0; }).join().is_err()); | ^^^^^^^^^^ error: attempt to divide by zero - --> $DIR/issue-8460-const.rs:27:36 + --> $DIR/issue-8460-const.rs:30:36 | LL | assert!(thread::spawn(move|| { 1i8 / 0; }).join().is_err()); | ^^^^^^^ error: attempt to divide by zero - --> $DIR/issue-8460-const.rs:29:36 + --> $DIR/issue-8460-const.rs:32:36 | LL | assert!(thread::spawn(move|| { 1i16 / 0; }).join().is_err()); | ^^^^^^^^ error: attempt to divide by zero - --> $DIR/issue-8460-const.rs:31:36 + --> $DIR/issue-8460-const.rs:34:36 | LL | assert!(thread::spawn(move|| { 1i32 / 0; }).join().is_err()); | ^^^^^^^^ error: attempt to divide by zero - --> $DIR/issue-8460-const.rs:33:36 + --> $DIR/issue-8460-const.rs:36:36 | LL | assert!(thread::spawn(move|| { 1i64 / 0; }).join().is_err()); | ^^^^^^^^ +error: attempt to divide by zero + --> $DIR/issue-8460-const.rs:38:36 + | +LL | assert!(thread::spawn(move|| { 1i128 / 0; }).join().is_err()); + | ^^^^^^^^^ + error: attempt to calculate the remainder with overflow - --> $DIR/issue-8460-const.rs:35:36 + --> $DIR/issue-8460-const.rs:40:36 | LL | assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^^^ error: this expression will panic at runtime - --> $DIR/issue-8460-const.rs:35:36 + --> $DIR/issue-8460-const.rs:40:36 | LL | assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^^^ attempt to calculate the remainder with overflow error: attempt to calculate the remainder with overflow - --> $DIR/issue-8460-const.rs:38:36 + --> $DIR/issue-8460-const.rs:43:36 | LL | assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^ error: this expression will panic at runtime - --> $DIR/issue-8460-const.rs:38:36 + --> $DIR/issue-8460-const.rs:43:36 | LL | assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^ attempt to calculate the remainder with overflow error: attempt to calculate the remainder with overflow - --> $DIR/issue-8460-const.rs:41:36 + --> $DIR/issue-8460-const.rs:46:36 | LL | assert!(thread::spawn(move|| { i16::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ error: this expression will panic at runtime - --> $DIR/issue-8460-const.rs:41:36 + --> $DIR/issue-8460-const.rs:46:36 | LL | assert!(thread::spawn(move|| { i16::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to calculate the remainder with overflow error: attempt to calculate the remainder with overflow - --> $DIR/issue-8460-const.rs:44:36 + --> $DIR/issue-8460-const.rs:49:36 | LL | assert!(thread::spawn(move|| { i32::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ error: this expression will panic at runtime - --> $DIR/issue-8460-const.rs:44:36 + --> $DIR/issue-8460-const.rs:49:36 | LL | assert!(thread::spawn(move|| { i32::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to calculate the remainder with overflow error: attempt to calculate the remainder with overflow - --> $DIR/issue-8460-const.rs:47:36 + --> $DIR/issue-8460-const.rs:52:36 | LL | assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ error: this expression will panic at runtime - --> $DIR/issue-8460-const.rs:47:36 + --> $DIR/issue-8460-const.rs:52:36 | LL | assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ attempt to calculate the remainder with overflow +error: attempt to calculate the remainder with overflow + --> $DIR/issue-8460-const.rs:55:36 + | +LL | assert!(thread::spawn(move|| { i128::MIN % -1; }).join().is_err()); + | ^^^^^^^^^^^^^^ + +error: this expression will panic at runtime + --> $DIR/issue-8460-const.rs:55:36 + | +LL | assert!(thread::spawn(move|| { i128::MIN % -1; }).join().is_err()); + | ^^^^^^^^^^^^^^ attempt to calculate the remainder with overflow + error: attempt to calculate the remainder with a divisor of zero - --> $DIR/issue-8460-const.rs:50:36 + --> $DIR/issue-8460-const.rs:58:36 | LL | assert!(thread::spawn(move|| { 1isize % 0; }).join().is_err()); | ^^^^^^^^^^ error: attempt to calculate the remainder with a divisor of zero - --> $DIR/issue-8460-const.rs:52:36 + --> $DIR/issue-8460-const.rs:60:36 | LL | assert!(thread::spawn(move|| { 1i8 % 0; }).join().is_err()); | ^^^^^^^ error: attempt to calculate the remainder with a divisor of zero - --> $DIR/issue-8460-const.rs:54:36 + --> $DIR/issue-8460-const.rs:62:36 | LL | assert!(thread::spawn(move|| { 1i16 % 0; }).join().is_err()); | ^^^^^^^^ error: attempt to calculate the remainder with a divisor of zero - --> $DIR/issue-8460-const.rs:56:36 + --> $DIR/issue-8460-const.rs:64:36 | LL | assert!(thread::spawn(move|| { 1i32 % 0; }).join().is_err()); | ^^^^^^^^ error: attempt to calculate the remainder with a divisor of zero - --> $DIR/issue-8460-const.rs:58:36 + --> $DIR/issue-8460-const.rs:66:36 | LL | assert!(thread::spawn(move|| { 1i64 % 0; }).join().is_err()); | ^^^^^^^^ -error: aborting due to 30 previous errors +error: attempt to calculate the remainder with a divisor of zero + --> $DIR/issue-8460-const.rs:68:36 + | +LL | assert!(thread::spawn(move|| { 1i128 % 0; }).join().is_err()); + | ^^^^^^^^^ + +error: aborting due to 36 previous errors diff --git a/src/test/ui/issues/issue-8460-const2.rs b/src/test/ui/issues/issue-8460-const2.rs index 0ca850abc1b60..afea859bb65a9 100644 --- a/src/test/ui/issues/issue-8460-const2.rs +++ b/src/test/ui/issues/issue-8460-const2.rs @@ -3,7 +3,7 @@ #![deny(const_err)] -use std::{isize, i8, i16, i32, i64}; +use std::{isize, i8, i16, i32, i64, i128}; use std::thread; fn main() { @@ -17,6 +17,8 @@ fn main() { //~^ ERROR attempt to divide with overflow assert!(thread::spawn(move|| { i64::MIN / -1; }).join().is_err()); //~^ ERROR attempt to divide with overflow + assert!(thread::spawn(move|| { i128::MIN / -1; }).join().is_err()); + //~^ ERROR attempt to divide with overflow assert!(thread::spawn(move|| { 1isize / 0; }).join().is_err()); //~^ ERROR attempt to divide by zero assert!(thread::spawn(move|| { 1i8 / 0; }).join().is_err()); @@ -27,6 +29,8 @@ fn main() { //~^ ERROR attempt to divide by zero assert!(thread::spawn(move|| { 1i64 / 0; }).join().is_err()); //~^ ERROR attempt to divide by zero + assert!(thread::spawn(move|| { 1i128 / 0; }).join().is_err()); + //~^ ERROR attempt to divide by zero assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with overflow assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err()); @@ -37,6 +41,8 @@ fn main() { //~^ ERROR attempt to calculate the remainder with overflow assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with overflow + assert!(thread::spawn(move|| { i128::MIN % -1; }).join().is_err()); + //~^ ERROR attempt to calculate the remainder with overflow assert!(thread::spawn(move|| { 1isize % 0; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with a divisor of zero assert!(thread::spawn(move|| { 1i8 % 0; }).join().is_err()); @@ -47,4 +53,6 @@ fn main() { //~^ ERROR attempt to calculate the remainder with a divisor of zero assert!(thread::spawn(move|| { 1i64 % 0; }).join().is_err()); //~^ ERROR attempt to calculate the remainder with a divisor of zero + assert!(thread::spawn(move|| { 1i128 % 0; }).join().is_err()); + //~^ ERROR attempt to calculate the remainder with a divisor of zero } diff --git a/src/test/ui/issues/issue-8460-const2.stderr b/src/test/ui/issues/issue-8460-const2.stderr index 63b9123e95097..e25d560fe0ce3 100644 --- a/src/test/ui/issues/issue-8460-const2.stderr +++ b/src/test/ui/issues/issue-8460-const2.stderr @@ -34,95 +34,119 @@ error: attempt to divide with overflow LL | assert!(thread::spawn(move|| { i64::MIN / -1; }).join().is_err()); | ^^^^^^^^^^^^^ -error: attempt to divide by zero +error: attempt to divide with overflow --> $DIR/issue-8460-const2.rs:20:36 | +LL | assert!(thread::spawn(move|| { i128::MIN / -1; }).join().is_err()); + | ^^^^^^^^^^^^^^ + +error: attempt to divide by zero + --> $DIR/issue-8460-const2.rs:22:36 + | LL | assert!(thread::spawn(move|| { 1isize / 0; }).join().is_err()); | ^^^^^^^^^^ error: attempt to divide by zero - --> $DIR/issue-8460-const2.rs:22:36 + --> $DIR/issue-8460-const2.rs:24:36 | LL | assert!(thread::spawn(move|| { 1i8 / 0; }).join().is_err()); | ^^^^^^^ error: attempt to divide by zero - --> $DIR/issue-8460-const2.rs:24:36 + --> $DIR/issue-8460-const2.rs:26:36 | LL | assert!(thread::spawn(move|| { 1i16 / 0; }).join().is_err()); | ^^^^^^^^ error: attempt to divide by zero - --> $DIR/issue-8460-const2.rs:26:36 + --> $DIR/issue-8460-const2.rs:28:36 | LL | assert!(thread::spawn(move|| { 1i32 / 0; }).join().is_err()); | ^^^^^^^^ error: attempt to divide by zero - --> $DIR/issue-8460-const2.rs:28:36 + --> $DIR/issue-8460-const2.rs:30:36 | LL | assert!(thread::spawn(move|| { 1i64 / 0; }).join().is_err()); | ^^^^^^^^ +error: attempt to divide by zero + --> $DIR/issue-8460-const2.rs:32:36 + | +LL | assert!(thread::spawn(move|| { 1i128 / 0; }).join().is_err()); + | ^^^^^^^^^ + error: attempt to calculate the remainder with overflow - --> $DIR/issue-8460-const2.rs:30:36 + --> $DIR/issue-8460-const2.rs:34:36 | LL | assert!(thread::spawn(move|| { isize::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^^^ error: attempt to calculate the remainder with overflow - --> $DIR/issue-8460-const2.rs:32:36 + --> $DIR/issue-8460-const2.rs:36:36 | LL | assert!(thread::spawn(move|| { i8::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^ error: attempt to calculate the remainder with overflow - --> $DIR/issue-8460-const2.rs:34:36 + --> $DIR/issue-8460-const2.rs:38:36 | LL | assert!(thread::spawn(move|| { i16::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ error: attempt to calculate the remainder with overflow - --> $DIR/issue-8460-const2.rs:36:36 + --> $DIR/issue-8460-const2.rs:40:36 | LL | assert!(thread::spawn(move|| { i32::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ error: attempt to calculate the remainder with overflow - --> $DIR/issue-8460-const2.rs:38:36 + --> $DIR/issue-8460-const2.rs:42:36 | LL | assert!(thread::spawn(move|| { i64::MIN % -1; }).join().is_err()); | ^^^^^^^^^^^^^ +error: attempt to calculate the remainder with overflow + --> $DIR/issue-8460-const2.rs:44:36 + | +LL | assert!(thread::spawn(move|| { i128::MIN % -1; }).join().is_err()); + | ^^^^^^^^^^^^^^ + error: attempt to calculate the remainder with a divisor of zero - --> $DIR/issue-8460-const2.rs:40:36 + --> $DIR/issue-8460-const2.rs:46:36 | LL | assert!(thread::spawn(move|| { 1isize % 0; }).join().is_err()); | ^^^^^^^^^^ error: attempt to calculate the remainder with a divisor of zero - --> $DIR/issue-8460-const2.rs:42:36 + --> $DIR/issue-8460-const2.rs:48:36 | LL | assert!(thread::spawn(move|| { 1i8 % 0; }).join().is_err()); | ^^^^^^^ error: attempt to calculate the remainder with a divisor of zero - --> $DIR/issue-8460-const2.rs:44:36 + --> $DIR/issue-8460-const2.rs:50:36 | LL | assert!(thread::spawn(move|| { 1i16 % 0; }).join().is_err()); | ^^^^^^^^ error: attempt to calculate the remainder with a divisor of zero - --> $DIR/issue-8460-const2.rs:46:36 + --> $DIR/issue-8460-const2.rs:52:36 | LL | assert!(thread::spawn(move|| { 1i32 % 0; }).join().is_err()); | ^^^^^^^^ error: attempt to calculate the remainder with a divisor of zero - --> $DIR/issue-8460-const2.rs:48:36 + --> $DIR/issue-8460-const2.rs:54:36 | LL | assert!(thread::spawn(move|| { 1i64 % 0; }).join().is_err()); | ^^^^^^^^ -error: aborting due to 20 previous errors +error: attempt to calculate the remainder with a divisor of zero + --> $DIR/issue-8460-const2.rs:56:36 + | +LL | assert!(thread::spawn(move|| { 1i128 % 0; }).join().is_err()); + | ^^^^^^^^^ + +error: aborting due to 24 previous errors From d6c5a04eff9643b634cb2c98411f973b8f7aa1e2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 10 Feb 2020 11:37:02 +0100 Subject: [PATCH 7/8] some more tests for i128 oveflow behavior --- src/test/ui/consts/const-err2.rs | 8 +++++++- src/test/ui/consts/const-err2.stderr | 22 +++++++++++++++++----- src/test/ui/consts/const-err3.rs | 6 ++++++ src/test/ui/consts/const-err3.stderr | 22 +++++++++++++++++----- src/test/ui/consts/const-int-arithmetic.rs | 21 +++++++++++++++++++-- 5 files changed, 66 insertions(+), 13 deletions(-) diff --git a/src/test/ui/consts/const-err2.rs b/src/test/ui/consts/const-err2.rs index 351dfd2e0f58c..7c5aaedda35fa 100644 --- a/src/test/ui/consts/const-err2.rs +++ b/src/test/ui/consts/const-err2.rs @@ -17,16 +17,22 @@ fn black_box(_: T) { fn main() { let a = -std::i8::MIN; //~^ ERROR const_err + let a_i128 = -std::i128::MIN; + //~^ ERROR const_err let b = 200u8 + 200u8 + 200u8; //~^ ERROR const_err + let b_i128 = std::i128::MIN - std::i128::MAX; + //~^ ERROR const_err let c = 200u8 * 4; //~^ ERROR const_err let d = 42u8 - (42u8 + 1); //~^ ERROR const_err let _e = [5u8][1]; - //~^ ERROR index out of bounds + //~^ ERROR const_err black_box(a); + black_box(a_i128); black_box(b); + black_box(b_i128); black_box(c); black_box(d); } diff --git a/src/test/ui/consts/const-err2.stderr b/src/test/ui/consts/const-err2.stderr index a76b6d1775f04..f135bf0b06cad 100644 --- a/src/test/ui/consts/const-err2.stderr +++ b/src/test/ui/consts/const-err2.stderr @@ -11,28 +11,40 @@ LL | #![deny(const_err)] | ^^^^^^^^^ error: this expression will panic at runtime - --> $DIR/const-err2.rs:20:13 + --> $DIR/const-err2.rs:20:18 + | +LL | let a_i128 = -std::i128::MIN; + | ^^^^^^^^^^^^^^^ attempt to negate with overflow + +error: this expression will panic at runtime + --> $DIR/const-err2.rs:22:13 | LL | let b = 200u8 + 200u8 + 200u8; | ^^^^^^^^^^^^^ attempt to add with overflow error: this expression will panic at runtime - --> $DIR/const-err2.rs:22:13 + --> $DIR/const-err2.rs:24:18 + | +LL | let b_i128 = std::i128::MIN - std::i128::MAX; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attempt to subtract with overflow + +error: this expression will panic at runtime + --> $DIR/const-err2.rs:26:13 | LL | let c = 200u8 * 4; | ^^^^^^^^^ attempt to multiply with overflow error: this expression will panic at runtime - --> $DIR/const-err2.rs:24:13 + --> $DIR/const-err2.rs:28:13 | LL | let d = 42u8 - (42u8 + 1); | ^^^^^^^^^^^^^^^^^ attempt to subtract with overflow error: index out of bounds: the len is 1 but the index is 1 - --> $DIR/const-err2.rs:26:14 + --> $DIR/const-err2.rs:30:14 | LL | let _e = [5u8][1]; | ^^^^^^^^ -error: aborting due to 5 previous errors +error: aborting due to 7 previous errors diff --git a/src/test/ui/consts/const-err3.rs b/src/test/ui/consts/const-err3.rs index ab3823efd301d..43aba4a8b012b 100644 --- a/src/test/ui/consts/const-err3.rs +++ b/src/test/ui/consts/const-err3.rs @@ -17,8 +17,12 @@ fn black_box(_: T) { fn main() { let a = -std::i8::MIN; //~^ ERROR const_err + let a_i128 = -std::i128::MIN; + //~^ ERROR const_err let b = 200u8 + 200u8 + 200u8; //~^ ERROR const_err + let b_i128 = std::i128::MIN - std::i128::MAX; + //~^ ERROR const_err let c = 200u8 * 4; //~^ ERROR const_err let d = 42u8 - (42u8 + 1); @@ -26,7 +30,9 @@ fn main() { let _e = [5u8][1]; //~^ ERROR const_err black_box(a); + black_box(a_i128); black_box(b); + black_box(b_i128); black_box(c); black_box(d); } diff --git a/src/test/ui/consts/const-err3.stderr b/src/test/ui/consts/const-err3.stderr index 02b912e928c80..05f64b87fcce6 100644 --- a/src/test/ui/consts/const-err3.stderr +++ b/src/test/ui/consts/const-err3.stderr @@ -10,29 +10,41 @@ note: the lint level is defined here LL | #![deny(const_err)] | ^^^^^^^^^ +error: attempt to negate with overflow + --> $DIR/const-err3.rs:20:18 + | +LL | let a_i128 = -std::i128::MIN; + | ^^^^^^^^^^^^^^^ + error: attempt to add with overflow - --> $DIR/const-err3.rs:20:13 + --> $DIR/const-err3.rs:22:13 | LL | let b = 200u8 + 200u8 + 200u8; | ^^^^^^^^^^^^^ +error: attempt to subtract with overflow + --> $DIR/const-err3.rs:24:18 + | +LL | let b_i128 = std::i128::MIN - std::i128::MAX; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + error: attempt to multiply with overflow - --> $DIR/const-err3.rs:22:13 + --> $DIR/const-err3.rs:26:13 | LL | let c = 200u8 * 4; | ^^^^^^^^^ error: attempt to subtract with overflow - --> $DIR/const-err3.rs:24:13 + --> $DIR/const-err3.rs:28:13 | LL | let d = 42u8 - (42u8 + 1); | ^^^^^^^^^^^^^^^^^ error: index out of bounds: the len is 1 but the index is 1 - --> $DIR/const-err3.rs:26:14 + --> $DIR/const-err3.rs:30:14 | LL | let _e = [5u8][1]; | ^^^^^^^^ -error: aborting due to 5 previous errors +error: aborting due to 7 previous errors diff --git a/src/test/ui/consts/const-int-arithmetic.rs b/src/test/ui/consts/const-int-arithmetic.rs index cfa2873c68bad..2c3421b7a8d0b 100644 --- a/src/test/ui/consts/const-int-arithmetic.rs +++ b/src/test/ui/consts/const-int-arithmetic.rs @@ -7,7 +7,7 @@ #![feature(const_saturating_int_methods)] #![feature(const_wrapping_int_methods)] -use std::i8; +use std::{i8, i128}; macro_rules! suite { ($( @@ -65,6 +65,10 @@ suite!( C26: 5i8.checked_rem_euclid(0), None; C27: i8::MIN.checked_rem_euclid(-1), None; } + checked_i128 -> Option { + CHK_ADD_I128: i128::MAX.checked_add(1), None; + CHK_MUL_I128: i128::MIN.checked_mul(-1), None; + } saturating_and_wrapping -> i8 { // `const_saturating_int_methods` @@ -104,6 +108,13 @@ suite!( C47: 100i8.wrapping_rem_euclid(10), 0; C48: (-128i8).wrapping_rem_euclid(-1), 0; } + saturating_and_wrapping_i128 -> i128 { + SAT_ADD_I128: i128::MAX.saturating_add(1), i128::MAX; + SAT_MUL_I128: i128::MAX.saturating_mul(2), i128::MAX; + + WRP_ADD_I128: i128::MAX.wrapping_add(1), i128::MIN; + WRP_MUL_I128: i128::MAX.wrapping_mul(3), i128::MAX-2; + } overflowing -> (i8, bool) { // `const_overflowing_int_methods` @@ -119,12 +130,18 @@ suite!( C55: 5i8.overflowing_rem_euclid(2), (1, false); C56: i8::MIN.overflowing_rem_euclid(-1), (0, true); - + } + overflowing_i128 -> (i128, bool) { + OFL_ADD_I128: i128::MAX.overflowing_add(1), (i128::MIN, true); + OFL_MUL_I128: i128::MAX.overflowing_mul(3), (i128::MAX-2, true); } ); fn main() { checked(); + checked_i128(); saturating_and_wrapping(); + saturating_and_wrapping_i128(); overflowing(); + overflowing_i128(); } From c561d23a6105122a517c14394a46c3faab8e01b6 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 10 Feb 2020 22:58:31 +0100 Subject: [PATCH 8/8] remove outdated comment --- src/librustc_mir/interpret/operator.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/librustc_mir/interpret/operator.rs b/src/librustc_mir/interpret/operator.rs index 9a3c08248b5ef..2e8c94903ca46 100644 --- a/src/librustc_mir/interpret/operator.rs +++ b/src/librustc_mir/interpret/operator.rs @@ -401,7 +401,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { (truncated, overflow || self.sign_extend(truncated, layout) != res) } }; - // res needs tuncating Ok((Scalar::from_uint(res, layout.size), overflow, layout.ty)) } }