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:
commit
b08d6cf529
@ -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`.
|
||||
|
@ -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| {
|
||||
|
@ -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()
|
||||
|
@ -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 {
|
||||
|
19
src/test/run-fail/overflowing-neg.rs
Normal file
19
src/test/run-fail/overflowing-neg.rs
Normal 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();
|
||||
}
|
Loading…
Reference in New Issue
Block a user