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.
This commit is contained in:
bors 2015-04-17 22:45:10 +00:00
commit b08d6cf529
5 changed files with 63 additions and 20 deletions

View File

@ -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`.

View File

@ -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| {

View File

@ -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()

View File

@ -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 {

View File

@ -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();
}