Auto merge of #58246 - pmccarter:master, r=oli-obk
Make `saturating_add` and `saturating_sub` `const` functions Fixes #58030
This commit is contained in:
commit
ba2853b6d3
5 changed files with 177 additions and 21 deletions
|
@ -882,17 +882,38 @@ $EndFeature, "
|
|||
```"),
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[inline]
|
||||
#[cfg(stage0)]
|
||||
pub fn saturating_add(self, rhs: Self) -> Self {
|
||||
#[cfg(stage0)]
|
||||
match self.checked_add(rhs) {
|
||||
Some(x) => x,
|
||||
None if rhs >= 0 => Self::max_value(),
|
||||
None => Self::min_value(),
|
||||
}
|
||||
#[cfg(not(stage0))]
|
||||
{
|
||||
intrinsics::saturating_add(self, rhs)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
doc_comment! {
|
||||
concat!("Saturating integer addition. Computes `self + rhs`, saturating at the numeric
|
||||
bounds instead of overflowing.
|
||||
|
||||
# Examples
|
||||
|
||||
Basic usage:
|
||||
|
||||
```
|
||||
", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_add(1), 101);
|
||||
assert_eq!(", stringify!($SelfT), "::max_value().saturating_add(100), ", stringify!($SelfT),
|
||||
"::max_value());",
|
||||
$EndFeature, "
|
||||
```"),
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_saturating_int_methods")]
|
||||
#[inline]
|
||||
#[cfg(not(stage0))]
|
||||
pub const fn saturating_add(self, rhs: Self) -> Self {
|
||||
intrinsics::saturating_add(self, rhs)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -912,17 +933,36 @@ $EndFeature, "
|
|||
```"),
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[inline]
|
||||
#[cfg(stage0)]
|
||||
pub fn saturating_sub(self, rhs: Self) -> Self {
|
||||
#[cfg(stage0)]
|
||||
match self.checked_sub(rhs) {
|
||||
Some(x) => x,
|
||||
None if rhs >= 0 => Self::min_value(),
|
||||
None => Self::max_value(),
|
||||
}
|
||||
#[cfg(not(stage0))]
|
||||
{
|
||||
intrinsics::saturating_sub(self, rhs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
doc_comment! {
|
||||
concat!("Saturating integer subtraction. Computes `self - rhs`, saturating at the
|
||||
numeric bounds instead of overflowing.
|
||||
|
||||
# Examples
|
||||
|
||||
Basic usage:
|
||||
|
||||
```
|
||||
", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_sub(127), -27);
|
||||
assert_eq!(", stringify!($SelfT), "::min_value().saturating_sub(100), ", stringify!($SelfT),
|
||||
"::min_value());",
|
||||
$EndFeature, "
|
||||
```"),
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_saturating_int_methods")]
|
||||
#[inline]
|
||||
#[cfg(not(stage0))]
|
||||
pub const fn saturating_sub(self, rhs: Self) -> Self {
|
||||
intrinsics::saturating_sub(self, rhs)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2753,16 +2793,34 @@ assert_eq!(200u8.saturating_add(127), 255);", $EndFeature, "
|
|||
```"),
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[inline]
|
||||
#[cfg(stage0)]
|
||||
pub fn saturating_add(self, rhs: Self) -> Self {
|
||||
#[cfg(stage0)]
|
||||
match self.checked_add(rhs) {
|
||||
Some(x) => x,
|
||||
None => Self::max_value(),
|
||||
}
|
||||
#[cfg(not(stage0))]
|
||||
{
|
||||
intrinsics::saturating_add(self, rhs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
doc_comment! {
|
||||
concat!("Saturating integer addition. Computes `self + rhs`, saturating at
|
||||
the numeric bounds instead of overflowing.
|
||||
|
||||
# Examples
|
||||
|
||||
Basic usage:
|
||||
|
||||
```
|
||||
", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_add(1), 101);
|
||||
assert_eq!(200u8.saturating_add(127), 255);", $EndFeature, "
|
||||
```"),
|
||||
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_saturating_int_methods")]
|
||||
#[inline]
|
||||
#[cfg(not(stage0))]
|
||||
pub const fn saturating_add(self, rhs: Self) -> Self {
|
||||
intrinsics::saturating_add(self, rhs)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2780,16 +2838,33 @@ assert_eq!(13", stringify!($SelfT), ".saturating_sub(127), 0);", $EndFeature, "
|
|||
```"),
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[inline]
|
||||
#[cfg(stage0)]
|
||||
pub fn saturating_sub(self, rhs: Self) -> Self {
|
||||
#[cfg(stage0)]
|
||||
match self.checked_sub(rhs) {
|
||||
Some(x) => x,
|
||||
None => Self::min_value(),
|
||||
}
|
||||
#[cfg(not(stage0))]
|
||||
{
|
||||
intrinsics::saturating_sub(self, rhs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
doc_comment! {
|
||||
concat!("Saturating integer subtraction. Computes `self - rhs`, saturating
|
||||
at the numeric bounds instead of overflowing.
|
||||
|
||||
# Examples
|
||||
|
||||
Basic usage:
|
||||
|
||||
```
|
||||
", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_sub(27), 73);
|
||||
assert_eq!(13", stringify!($SelfT), ".saturating_sub(127), 0);", $EndFeature, "
|
||||
```"),
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_const_unstable(feature = "const_saturating_int_methods")]
|
||||
#[inline]
|
||||
#[cfg(not(stage0))]
|
||||
pub const fn saturating_sub(self, rhs: Self) -> Self {
|
||||
intrinsics::saturating_sub(self, rhs)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
use syntax::symbol::Symbol;
|
||||
use rustc::ty;
|
||||
use rustc::ty::layout::{LayoutOf, Primitive};
|
||||
use rustc::ty::layout::{LayoutOf, Primitive, Size};
|
||||
use rustc::mir::BinOp;
|
||||
use rustc::mir::interpret::{
|
||||
EvalResult, EvalErrorKind, Scalar,
|
||||
|
@ -122,6 +122,49 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
|
|||
self.binop_with_overflow(bin_op, lhs, rhs, dest)?;
|
||||
}
|
||||
}
|
||||
"saturating_add" | "saturating_sub" => {
|
||||
let l = self.read_immediate(args[0])?;
|
||||
let r = self.read_immediate(args[1])?;
|
||||
let is_add = intrinsic_name == "saturating_add";
|
||||
let (val, overflowed) = self.binary_op_imm(if is_add {
|
||||
BinOp::Add
|
||||
} else {
|
||||
BinOp::Sub
|
||||
}, l, r)?;
|
||||
let val = if overflowed {
|
||||
let num_bits = l.layout.size.bits();
|
||||
if l.layout.abi.is_signed() {
|
||||
// For signed ints the saturated value depends on the sign of the first
|
||||
// term since the sign of the second term can be inferred from this and
|
||||
// the fact that the operation has overflowed (if either is 0 no
|
||||
// overflow can occur)
|
||||
let first_term: u128 = l.to_scalar()?.to_bits(l.layout.size)?;
|
||||
let first_term_positive = first_term & (1 << (num_bits-1)) == 0;
|
||||
if first_term_positive {
|
||||
// Negative overflow not possible since the positive first term
|
||||
// can only increase an (in range) negative term for addition
|
||||
// or corresponding negated positive term for subtraction
|
||||
Scalar::from_uint((1u128 << (num_bits - 1)) - 1, // max positive
|
||||
Size::from_bits(num_bits))
|
||||
} else {
|
||||
// Positive overflow not possible for similar reason
|
||||
// max negative
|
||||
Scalar::from_uint(1u128 << (num_bits - 1), Size::from_bits(num_bits))
|
||||
}
|
||||
} else { // unsigned
|
||||
if is_add {
|
||||
// max unsigned
|
||||
Scalar::from_uint(u128::max_value() >> (128 - num_bits),
|
||||
Size::from_bits(num_bits))
|
||||
} else { // underflow to 0
|
||||
Scalar::from_uint(0u128, Size::from_bits(num_bits))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val
|
||||
};
|
||||
self.write_scalar(val, dest)?;
|
||||
}
|
||||
"unchecked_shl" | "unchecked_shr" => {
|
||||
let l = self.read_immediate(args[0])?;
|
||||
let r = self.read_immediate(args[1])?;
|
||||
|
|
|
@ -836,6 +836,8 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
|
|||
| "add_with_overflow"
|
||||
| "sub_with_overflow"
|
||||
| "mul_with_overflow"
|
||||
| "saturating_add"
|
||||
| "saturating_sub"
|
||||
// no need to check feature gates, intrinsics are only callable
|
||||
// from the libstd or with forever unstable feature gates
|
||||
=> is_const_fn = true,
|
||||
|
|
|
@ -374,6 +374,8 @@ fn is_intrinsic_whitelisted(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool
|
|||
| "overflowing_add" // ~> .wrapping_add
|
||||
| "overflowing_sub" // ~> .wrapping_sub
|
||||
| "overflowing_mul" // ~> .wrapping_mul
|
||||
| "saturating_add" // ~> .saturating_add
|
||||
| "saturating_sub" // ~> .saturating_sub
|
||||
| "unchecked_shl" // ~> .wrapping_shl
|
||||
| "unchecked_shr" // ~> .wrapping_shr
|
||||
| "rotate_left" // ~> .rotate_left
|
||||
|
|
34
src/test/run-pass/const-int-saturating-arith.rs
Normal file
34
src/test/run-pass/const-int-saturating-arith.rs
Normal file
|
@ -0,0 +1,34 @@
|
|||
// ignore-emscripten no i128 support
|
||||
#![feature(const_saturating_int_methods)]
|
||||
|
||||
const INT_U32_NO: u32 = (42 as u32).saturating_add(2);
|
||||
const INT_U32: u32 = u32::max_value().saturating_add(1);
|
||||
const INT_U128: u128 = u128::max_value().saturating_add(1);
|
||||
const INT_I128: i128 = i128::max_value().saturating_add(1);
|
||||
const INT_I128_NEG: i128 = i128::min_value().saturating_add(-1);
|
||||
|
||||
const INT_U32_NO_SUB: u32 = (42 as u32).saturating_sub(2);
|
||||
const INT_U32_SUB: u32 = (1 as u32).saturating_sub(2);
|
||||
const INT_I32_NO_SUB: i32 = (-42 as i32).saturating_sub(2);
|
||||
const INT_I32_NEG_SUB: i32 = i32::min_value().saturating_sub(1);
|
||||
const INT_I32_POS_SUB: i32 = i32::max_value().saturating_sub(-1);
|
||||
const INT_U128_SUB: u128 = (0 as u128).saturating_sub(1);
|
||||
const INT_I128_NEG_SUB: i128 = i128::min_value().saturating_sub(1);
|
||||
const INT_I128_POS_SUB: i128 = i128::max_value().saturating_sub(-1);
|
||||
|
||||
fn main() {
|
||||
assert_eq!(INT_U32_NO, 44);
|
||||
assert_eq!(INT_U32, u32::max_value());
|
||||
assert_eq!(INT_U128, u128::max_value());
|
||||
assert_eq!(INT_I128, i128::max_value());
|
||||
assert_eq!(INT_I128_NEG, i128::min_value());
|
||||
|
||||
assert_eq!(INT_U32_NO_SUB, 40);
|
||||
assert_eq!(INT_U32_SUB, 0);
|
||||
assert_eq!(INT_I32_NO_SUB, -44);
|
||||
assert_eq!(INT_I32_NEG_SUB, i32::min_value());
|
||||
assert_eq!(INT_I32_POS_SUB, i32::max_value());
|
||||
assert_eq!(INT_U128_SUB, 0);
|
||||
assert_eq!(INT_I128_NEG_SUB, i128::min_value());
|
||||
assert_eq!(INT_I128_POS_SUB, i128::max_value());
|
||||
}
|
Loading…
Reference in a new issue