From 9a6d7fb9b068a0c35e469975d4b00992e0f9b8a8 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Wed, 15 Apr 2015 02:19:26 +0200 Subject: [PATCH 1/5] factor out useful helper. --- src/librustc_trans/trans/base.rs | 35 ++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 93eb24a47de..023f9e0bda1 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -566,6 +566,25 @@ fn cast_shift_rhs(op: ast::BinOp_, } } +pub fn llty_and_min_for_signed_ty<'blk, 'tcx>(cx: Block<'blk, 'tcx>, + val_t: Ty<'tcx>) -> (Type, u64) { + match val_t.sty { + ty::ty_int(t) => { + let llty = Type::int_from_ty(cx.ccx(), t); + let min = match t { + ast::TyIs if llty == Type::i32(cx.ccx()) => i32::MIN as u64, + ast::TyIs => i64::MIN as u64, + ast::TyI8 => i8::MIN as u64, + ast::TyI16 => i16::MIN as u64, + ast::TyI32 => i32::MIN as u64, + ast::TyI64 => i64::MIN as u64, + }; + (llty, min) + } + _ => unreachable!(), + } +} + pub fn fail_if_zero_or_overflows<'blk, 'tcx>( cx: Block<'blk, 'tcx>, call_info: NodeIdAndSpan, @@ -620,21 +639,7 @@ pub fn fail_if_zero_or_overflows<'blk, 'tcx>( // signed division/remainder which would trigger overflow. For unsigned // integers, no action beyond checking for zero need be taken. if is_signed { - let (llty, min) = match rhs_t.sty { - ty::ty_int(t) => { - let llty = Type::int_from_ty(cx.ccx(), t); - let min = match t { - ast::TyIs if llty == Type::i32(cx.ccx()) => i32::MIN as u64, - ast::TyIs => i64::MIN as u64, - ast::TyI8 => i8::MIN as u64, - ast::TyI16 => i16::MIN as u64, - ast::TyI32 => i32::MIN as u64, - ast::TyI64 => i64::MIN as u64, - }; - (llty, min) - } - _ => unreachable!(), - }; + let (llty, min) = llty_and_min_for_signed_ty(cx, rhs_t); let minus_one = ICmp(bcx, llvm::IntEQ, rhs, C_integral(llty, !0, false), debug_loc); with_cond(bcx, minus_one, |bcx| { From c34fa8b2c58aa891a99da397f317793be69bfdb1 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Wed, 15 Apr 2015 02:19:52 +0200 Subject: [PATCH 2/5] Add conditional overflow-checking to signed negate operator. --- src/librustc_trans/trans/expr.rs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index b91f50222a2..27919d645b6 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -1530,11 +1530,26 @@ fn trans_unary<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, ast::UnNeg => { let datum = unpack_datum!(bcx, trans(bcx, sub_expr)); let val = datum.to_llscalarish(bcx); - let llneg = { + let (bcx, llneg) = { if ty::type_is_fp(un_ty) { - FNeg(bcx, val, debug_loc) + let result = FNeg(bcx, val, debug_loc); + (bcx, result) } else { - Neg(bcx, val, debug_loc) + let is_signed = ty::type_is_signed(un_ty); + let result = Neg(bcx, val, debug_loc); + let bcx = if bcx.ccx().check_overflow() && is_signed { + let (llty, min) = base::llty_and_min_for_signed_ty(bcx, un_ty); + let is_min = ICmp(bcx, llvm::IntEQ, val, + C_integral(llty, min, true), debug_loc); + with_cond(bcx, is_min, |bcx| { + let msg = InternedString::new( + "attempted to negate with overflow"); + controlflow::trans_fail(bcx, expr_info(expr), msg) + }) + } else { + bcx + }; + (bcx, result) } }; immediate_rvalue_bcx(bcx, llneg, un_ty).to_expr_datumblock() From e9f892acc44f37887fe1b9f3039ba6efdfc2154e Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Wed, 15 Apr 2015 12:27:23 +0200 Subject: [PATCH 3/5] side-step potentially panic'ing negate in `fn abs`. --- src/libcore/num/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index c7714afc4fa..a056e585fee 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -1321,7 +1321,11 @@ macro_rules! int_impl { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn abs(self) -> $T { - if self.is_negative() { -self } else { self } + if self.is_negative() { + self.wrapping_neg() + } else { + self + } } /// Returns a number representing sign of `self`. From 5e7785cabc1c1bfb0efb2d99de5eda173fd146de Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 16 Apr 2015 16:48:14 +0200 Subject: [PATCH 4/5] Workaround deliberate overflowing negation in serialize::json. --- src/libserialize/json.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libserialize/json.rs b/src/libserialize/json.rs index 8f020d0857d..b4f679a8109 100644 --- a/src/libserialize/json.rs +++ b/src/libserialize/json.rs @@ -1540,7 +1540,7 @@ impl> Parser { F64Value(res) } else { if neg { - let res = -(res as i64); + let res = (res as i64).wrapping_neg(); // Make sure we didn't underflow. if res > 0 { From b8ec7e88fc6c5a81194fd09dad042dc291a1cbb5 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 17 Apr 2015 15:32:30 +0200 Subject: [PATCH 5/5] unit test for checked overflow during signed negation. --- src/test/run-fail/overflowing-neg.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/test/run-fail/overflowing-neg.rs diff --git a/src/test/run-fail/overflowing-neg.rs b/src/test/run-fail/overflowing-neg.rs new file mode 100644 index 00000000000..cdb74c7d7e2 --- /dev/null +++ b/src/test/run-fail/overflowing-neg.rs @@ -0,0 +1,19 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern:thread '
' panicked at 'attempted to negate with overflow' +// compile-flags: -C debug-assertions + +// (Work around constant-evaluation) +fn value() -> i8 { std::i8::MIN } + +fn main() { + let _x = -value(); +}