Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make saturating_add and saturating_sub const functions #58246

Merged
merged 9 commits into from
Feb 12, 2019
111 changes: 91 additions & 20 deletions src/libcore/num/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -882,17 +882,37 @@ $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")]
#[inline]
#[cfg(not(stage0))]
pub const fn saturating_add(self, rhs: Self) -> Self {
intrinsics::saturating_add(self, rhs)
}
}

Expand All @@ -912,17 +932,35 @@ $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")]
#[inline]
#[cfg(not(stage0))]
pub const fn saturating_sub(self, rhs: Self) -> Self {
intrinsics::saturating_sub(self, rhs)
}
}

Expand Down Expand Up @@ -2753,16 +2791,33 @@ 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")]
#[inline]
#[cfg(not(stage0))]
nikic marked this conversation as resolved.
Show resolved Hide resolved
pub const fn saturating_add(self, rhs: Self) -> Self {
intrinsics::saturating_add(self, rhs)
}
}

Expand All @@ -2780,16 +2835,32 @@ 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")]
#[inline]
#[cfg(not(stage0))]
pub const fn saturating_sub(self, rhs: Self) -> Self {
intrinsics::saturating_sub(self, rhs)
}
}

Expand Down
52 changes: 51 additions & 1 deletion src/librustc_mir/interpret/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -122,6 +122,56 @@ 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" => {
let l = self.read_immediate(args[0])?;
let r = self.read_immediate(args[1])?;
let (val, overflowed) = self.binary_op_imm(BinOp::Add, l, r)?;
if overflowed {
let first_term: u128 = l.to_scalar()?.to_bits(l.layout.size)?;
let num_bits = l.layout.size.bits();
let val = if l.layout.abi.is_signed() {
// For signed addition the saturated value depends on the
nikic marked this conversation as resolved.
Show resolved Hide resolved
// sign of either term
if first_term & (1 << (num_bits-1)) == 0 { // signed term is positive
Scalar::from_uint((1u128 << (num_bits - 1)) - 1,
Size::from_bits(num_bits))
} else { // signed term is negative
Scalar::from_uint(1u128 << (num_bits - 1), Size::from_bits(num_bits))
RalfJung marked this conversation as resolved.
Show resolved Hide resolved
}
} else {
Scalar::from_uint(u128::max_value() >> (128 - num_bits),
Size::from_bits(num_bits))
};
self.write_scalar(val, dest)?;
} else {
self.write_scalar(val, dest)?;
}
}
"saturating_sub" => {
let l = self.read_immediate(args[0])?;
let r = self.read_immediate(args[1])?;
let (val, overflowed) = self.binary_op_imm(BinOp::Sub, l, r)?;
if overflowed {
let first_term: u128 = l.to_scalar()?.to_bits(l.layout.size)?;
let num_bits = l.layout.size.bits();
let val = if l.layout.abi.is_signed() {
if first_term & (1 << (num_bits-1)) == 0 { // first term is positive
// so overflow is positive
Scalar::from_uint((1u128 << (num_bits - 1)) - 1,
Size::from_bits(num_bits))
} else {
// if first term negative, overflow must be negative
Scalar::from_uint(1u128 << (num_bits - 1), Size::from_bits(num_bits))
}
} else {
// unsigned underflow saturates to 0
Scalar::from_uint(0u128, Size::from_bits(num_bits))
};
self.write_scalar(val, dest)?;
} else {
self.write_scalar(val, dest)?;
}
}
"unchecked_shl" | "unchecked_shr" => {
let l = self.read_immediate(args[0])?;
let r = self.read_immediate(args[1])?;
Expand Down
2 changes: 2 additions & 0 deletions src/librustc_mir/transform/qualify_consts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,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,
Expand Down
2 changes: 2 additions & 0 deletions src/librustc_mir/transform/qualify_min_const_fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc @rust-lang/lang

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm also

| "unchecked_shl" // ~> .wrapping_shl
| "unchecked_shr" // ~> .wrapping_shr
| "rotate_left" // ~> .rotate_left
Expand Down
31 changes: 31 additions & 0 deletions src/test/run-pass/const-int-saturating-arith.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
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);
nikic marked this conversation as resolved.
Show resolved Hide resolved
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());
}