diff options
| author | bors <bors@rust-lang.org> | 2015-04-17 22:45:10 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2015-04-17 22:45:10 +0000 |
| commit | b08d6cf529a83caec2f408cd8b1287e493ec57ca (patch) | |
| tree | 6ef971f443e9797d767a2b05f837f9026ba68117 | |
| parent | f305579e490a9fa046b1b7a14e62daf643e41865 (diff) | |
| parent | b8ec7e88fc6c5a81194fd09dad042dc291a1cbb5 (diff) | |
| download | rust-b08d6cf529a83caec2f408cd8b1287e493ec57ca.tar.gz rust-b08d6cf529a83caec2f408cd8b1287e493ec57ca.zip | |
Auto merge of #24500 - pnkfelix:oflo-checked-neg, r=nikomatsakis
Add conditional overflow-checking to signed negate operator. I argue this can land independently of #24420 , because one can write the implementation of `wrapped_neg()` inline if necessary (as illustrated in two cases on this PR). This needs to go into beta channel.
| -rw-r--r-- | src/libcore/num/mod.rs | 6 | ||||
| -rw-r--r-- | src/librustc_trans/trans/base.rs | 35 | ||||
| -rw-r--r-- | src/librustc_trans/trans/expr.rs | 21 | ||||
| -rw-r--r-- | src/libserialize/json.rs | 2 | ||||
| -rw-r--r-- | src/test/run-fail/overflowing-neg.rs | 19 |
5 files changed, 63 insertions, 20 deletions
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`. 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<F, G>(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| { 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() 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<T: Iterator<Item=char>> Parser<T> { 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 { 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 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern:thread '<main>' 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(); +} |
