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