about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2015-04-17 22:45:10 +0000
committerbors <bors@rust-lang.org>2015-04-17 22:45:10 +0000
commitb08d6cf529a83caec2f408cd8b1287e493ec57ca (patch)
tree6ef971f443e9797d767a2b05f837f9026ba68117
parentf305579e490a9fa046b1b7a14e62daf643e41865 (diff)
parentb8ec7e88fc6c5a81194fd09dad042dc291a1cbb5 (diff)
downloadrust-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.rs6
-rw-r--r--src/librustc_trans/trans/base.rs35
-rw-r--r--src/librustc_trans/trans/expr.rs21
-rw-r--r--src/libserialize/json.rs2
-rw-r--r--src/test/run-fail/overflowing-neg.rs19
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();
+}