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")]
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn abs(self) -> $T {
|
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`.
|
/// 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>(
|
pub fn fail_if_zero_or_overflows<'blk, 'tcx>(
|
||||||
cx: Block<'blk, 'tcx>,
|
cx: Block<'blk, 'tcx>,
|
||||||
call_info: NodeIdAndSpan,
|
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
|
// signed division/remainder which would trigger overflow. For unsigned
|
||||||
// integers, no action beyond checking for zero need be taken.
|
// integers, no action beyond checking for zero need be taken.
|
||||||
if is_signed {
|
if is_signed {
|
||||||
let (llty, min) = match rhs_t.sty {
|
let (llty, min) = llty_and_min_for_signed_ty(cx, rhs_t);
|
||||||
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 minus_one = ICmp(bcx, llvm::IntEQ, rhs,
|
let minus_one = ICmp(bcx, llvm::IntEQ, rhs,
|
||||||
C_integral(llty, !0, false), debug_loc);
|
C_integral(llty, !0, false), debug_loc);
|
||||||
with_cond(bcx, minus_one, |bcx| {
|
with_cond(bcx, minus_one, |bcx| {
|
||||||
|
@ -1530,11 +1530,26 @@ fn trans_unary<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
|||||||
ast::UnNeg => {
|
ast::UnNeg => {
|
||||||
let datum = unpack_datum!(bcx, trans(bcx, sub_expr));
|
let datum = unpack_datum!(bcx, trans(bcx, sub_expr));
|
||||||
let val = datum.to_llscalarish(bcx);
|
let val = datum.to_llscalarish(bcx);
|
||||||
let llneg = {
|
let (bcx, llneg) = {
|
||||||
if ty::type_is_fp(un_ty) {
|
if ty::type_is_fp(un_ty) {
|
||||||
FNeg(bcx, val, debug_loc)
|
let result = FNeg(bcx, val, debug_loc);
|
||||||
|
(bcx, result)
|
||||||
} else {
|
} 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()
|
immediate_rvalue_bcx(bcx, llneg, un_ty).to_expr_datumblock()
|
||||||
|
@ -1540,7 +1540,7 @@ impl<T: Iterator<Item=char>> Parser<T> {
|
|||||||
F64Value(res)
|
F64Value(res)
|
||||||
} else {
|
} else {
|
||||||
if neg {
|
if neg {
|
||||||
let res = -(res as i64);
|
let res = (res as i64).wrapping_neg();
|
||||||
|
|
||||||
// Make sure we didn't underflow.
|
// Make sure we didn't underflow.
|
||||||
if res > 0 {
|
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