about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPatrick McCarter <p.mccarter@gmail.com>2019-02-06 16:15:17 -0500
committerPatrick McCarter <p.mccarter@gmail.com>2019-02-06 16:15:17 -0500
commit0efb8e4d7392cd4b9c410b96ea209a18c161d92b (patch)
treee21a117c1ce8307f06fc39f5ca1167b516aeaf7a
parent9204497c2999ce4a3df9802d48cb990be7ee1164 (diff)
downloadrust-0efb8e4d7392cd4b9c410b96ea209a18c161d92b.tar.gz
rust-0efb8e4d7392cd4b9c410b96ea209a18c161d92b.zip
Allow const assignment for int saturating_sub() for #58030
-rw-r--r--src/libcore/num/mod.rs54
-rw-r--r--src/librustc/mir/interpret/mod.rs2
-rw-r--r--src/librustc_mir/interpret/intrinsics.rs28
-rw-r--r--src/librustc_mir/transform/qualify_consts.rs1
-rw-r--r--src/librustc_mir/transform/qualify_min_const_fn.rs1
-rw-r--r--src/test/run-pass/const-int-saturating-arith.rs16
6 files changed, 86 insertions, 16 deletions
diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs
index 55de04db028..104b124bad4 100644
--- a/src/libcore/num/mod.rs
+++ b/src/libcore/num/mod.rs
@@ -932,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)
             }
         }
 
@@ -2817,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)
             }
         }
 
diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs
index 4013cfb9558..e6a560b2ad7 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 64be3969640..dd165d9cb2f 100644
--- a/src/librustc_mir/interpret/intrinsics.rs
+++ b/src/librustc_mir/interpret/intrinsics.rs
@@ -130,14 +130,15 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
                     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
+                        // 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
+                            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))  // min signed val
+                            Scalar::from_uint(1u128 << (num_bits - 1), Size::from_bits(num_bits))
                         }
                     } else {
-                        if num_bits == 128 {  // General bit shift method causes overflow for u128 terms
+                        if num_bits == 128 {
                             Scalar::from_uint(u128::max_value(), Size::from_bits(128))
                         } else {
                             Scalar::from_uint(u128::max_value() & ((1 << num_bits) - 1),
@@ -149,6 +150,25 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
                     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 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))
+                    };
+                    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 ac75a95dbe0..d6ec2074342 100644
--- a/src/librustc_mir/transform/qualify_consts.rs
+++ b/src/librustc_mir/transform/qualify_consts.rs
@@ -821,6 +821,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
                                 | "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,
diff --git a/src/librustc_mir/transform/qualify_min_const_fn.rs b/src/librustc_mir/transform/qualify_min_const_fn.rs
index bb7fe0dab54..d8dc6c2845b 100644
--- a/src/librustc_mir/transform/qualify_min_const_fn.rs
+++ b/src/librustc_mir/transform/qualify_min_const_fn.rs
@@ -375,6 +375,7 @@ fn is_intrinsic_whitelisted(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool
         | "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
diff --git a/src/test/run-pass/const-int-saturating-arith.rs b/src/test/run-pass/const-int-saturating-arith.rs
index 3ff6a1fc08a..283a2a2484f 100644
--- a/src/test/run-pass/const-int-saturating-arith.rs
+++ b/src/test/run-pass/const-int-saturating-arith.rs
@@ -4,10 +4,24 @@ 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_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_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());
-}
\ No newline at end of file
+
+    assert_eq!(INT_U32_NO_SUB, 40);
+    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_I128_NEG_SUB, i128::min_value());
+    assert_eq!(INT_I128_POS_SUB, i128::max_value());
+}