From 9204497c2999ce4a3df9802d48cb990be7ee1164 Mon Sep 17 00:00:00 2001
From: Patrick McCarter
Date: Tue, 5 Feb 2019 15:36:31 -0500
Subject: [PATCH] Allow const assignment for int saturating_add() calls for
#58030
---
src/libcore/num/mod.rs | 57 +++++++++++++++----
src/librustc/mir/interpret/mod.rs | 2 +-
src/librustc_mir/interpret/intrinsics.rs | 29 +++++++++-
src/librustc_mir/transform/qualify_consts.rs | 1 +
.../transform/qualify_min_const_fn.rs | 1 +
.../run-pass/const-int-saturating-arith.rs | 13 +++++
6 files changed, 91 insertions(+), 12 deletions(-)
create mode 100644 src/test/run-pass/const-int-saturating-arith.rs
diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs
index f80f8392827..55de04db028 100644
--- a/src/libcore/num/mod.rs
+++ b/src/libcore/num/mod.rs
@@ -882,17 +882,37 @@ pub fn checked_pow(self, mut exp: u32) -> Option {
```"),
#[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)
}
}
@@ -2753,16 +2773,33 @@ pub fn checked_pow(self, mut exp: u32) -> Option {
```"),
#[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))]
+ pub const fn saturating_add(self, rhs: Self) -> Self {
+ intrinsics::saturating_add(self, rhs)
}
}
diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs
index e6a560b2ad7..4013cfb9558 100644
--- a/src/librustc/mir/interpret/mod.rs
+++ b/src/librustc/mir/interpret/mod.rs
@@ -418,4 +418,4 @@ pub fn truncate(value: u128, size: Size) -> u128 {
let shift = 128 - size;
// truncate (shift left to drop out leftover values, shift right to fill with zeroes)
(value << shift) >> shift
-}
+}
\ No newline at end of file
diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs
index d8778dfeef7..64be3969640 100644
--- a/src/librustc_mir/interpret/intrinsics.rs
+++ b/src/librustc_mir/interpret/intrinsics.rs
@@ -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,33 @@ pub fn emulate_intrinsic(
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 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)) // max signed val
+ } else { // signed term is negative
+ Scalar::from_uint(1u128 << (num_bits - 1), Size::from_bits(num_bits)) // min signed val
+ }
+ } else {
+ if num_bits == 128 { // General bit shift method causes overflow for u128 terms
+ Scalar::from_uint(u128::max_value(), Size::from_bits(128))
+ } else {
+ Scalar::from_uint(u128::max_value() & ((1 << num_bits) - 1),
+ 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])?;
diff --git a/src/librustc_mir/transform/qualify_consts.rs b/src/librustc_mir/transform/qualify_consts.rs
index 2d941902deb..ac75a95dbe0 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -820,6 +820,7 @@ fn visit_terminator_kind(&mut self,
| "add_with_overflow"
| "sub_with_overflow"
| "mul_with_overflow"
+ | "saturating_add"
// no need to check feature gates, intrinsics are only callable
// from the libstd or with forever unstable feature gates
=> is_const_fn = true,
diff --git a/src/librustc_mir/transform/qualify_min_const_fn.rs b/src/librustc_mir/transform/qualify_min_const_fn.rs
index 85bf1e70ebf..bb7fe0dab54 100644
--- a/src/librustc_mir/transform/qualify_min_const_fn.rs
+++ b/src/librustc_mir/transform/qualify_min_const_fn.rs
@@ -374,6 +374,7 @@ 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
| "unchecked_shl" // ~> .wrapping_shl
| "unchecked_shr" // ~> .wrapping_shr
| "rotate_left" // ~> .rotate_left
diff --git a/src/test/run-pass/const-int-saturating-arith.rs b/src/test/run-pass/const-int-saturating-arith.rs
new file mode 100644
index 00000000000..3ff6a1fc08a
--- /dev/null
+++ b/src/test/run-pass/const-int-saturating-arith.rs
@@ -0,0 +1,13 @@
+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);
+
+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());
+}
\ No newline at end of file