Skip to content

Commit ba2853b

Browse files
committed
Auto merge of #58246 - pmccarter:master, r=oli-obk
Make `saturating_add` and `saturating_sub` `const` functions Fixes #58030
2 parents 16ca0b9 + b04d8aa commit ba2853b

File tree

5 files changed

+177
-21
lines changed

5 files changed

+177
-21
lines changed

src/libcore/num/mod.rs

+95-20
Original file line numberDiff line numberDiff line change
@@ -882,17 +882,38 @@ $EndFeature, "
882882
```"),
883883
#[stable(feature = "rust1", since = "1.0.0")]
884884
#[inline]
885+
#[cfg(stage0)]
885886
pub fn saturating_add(self, rhs: Self) -> Self {
886-
#[cfg(stage0)]
887887
match self.checked_add(rhs) {
888888
Some(x) => x,
889889
None if rhs >= 0 => Self::max_value(),
890890
None => Self::min_value(),
891891
}
892-
#[cfg(not(stage0))]
893-
{
894-
intrinsics::saturating_add(self, rhs)
895-
}
892+
}
893+
894+
}
895+
896+
doc_comment! {
897+
concat!("Saturating integer addition. Computes `self + rhs`, saturating at the numeric
898+
bounds instead of overflowing.
899+
900+
# Examples
901+
902+
Basic usage:
903+
904+
```
905+
", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_add(1), 101);
906+
assert_eq!(", stringify!($SelfT), "::max_value().saturating_add(100), ", stringify!($SelfT),
907+
"::max_value());",
908+
$EndFeature, "
909+
```"),
910+
911+
#[stable(feature = "rust1", since = "1.0.0")]
912+
#[rustc_const_unstable(feature = "const_saturating_int_methods")]
913+
#[inline]
914+
#[cfg(not(stage0))]
915+
pub const fn saturating_add(self, rhs: Self) -> Self {
916+
intrinsics::saturating_add(self, rhs)
896917
}
897918
}
898919

@@ -912,17 +933,36 @@ $EndFeature, "
912933
```"),
913934
#[stable(feature = "rust1", since = "1.0.0")]
914935
#[inline]
936+
#[cfg(stage0)]
915937
pub fn saturating_sub(self, rhs: Self) -> Self {
916-
#[cfg(stage0)]
917938
match self.checked_sub(rhs) {
918939
Some(x) => x,
919940
None if rhs >= 0 => Self::min_value(),
920941
None => Self::max_value(),
921942
}
922-
#[cfg(not(stage0))]
923-
{
924-
intrinsics::saturating_sub(self, rhs)
925-
}
943+
}
944+
}
945+
946+
doc_comment! {
947+
concat!("Saturating integer subtraction. Computes `self - rhs`, saturating at the
948+
numeric bounds instead of overflowing.
949+
950+
# Examples
951+
952+
Basic usage:
953+
954+
```
955+
", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_sub(127), -27);
956+
assert_eq!(", stringify!($SelfT), "::min_value().saturating_sub(100), ", stringify!($SelfT),
957+
"::min_value());",
958+
$EndFeature, "
959+
```"),
960+
#[stable(feature = "rust1", since = "1.0.0")]
961+
#[rustc_const_unstable(feature = "const_saturating_int_methods")]
962+
#[inline]
963+
#[cfg(not(stage0))]
964+
pub const fn saturating_sub(self, rhs: Self) -> Self {
965+
intrinsics::saturating_sub(self, rhs)
926966
}
927967
}
928968

@@ -2753,16 +2793,34 @@ assert_eq!(200u8.saturating_add(127), 255);", $EndFeature, "
27532793
```"),
27542794
#[stable(feature = "rust1", since = "1.0.0")]
27552795
#[inline]
2796+
#[cfg(stage0)]
27562797
pub fn saturating_add(self, rhs: Self) -> Self {
2757-
#[cfg(stage0)]
27582798
match self.checked_add(rhs) {
27592799
Some(x) => x,
27602800
None => Self::max_value(),
27612801
}
2762-
#[cfg(not(stage0))]
2763-
{
2764-
intrinsics::saturating_add(self, rhs)
2765-
}
2802+
}
2803+
}
2804+
2805+
doc_comment! {
2806+
concat!("Saturating integer addition. Computes `self + rhs`, saturating at
2807+
the numeric bounds instead of overflowing.
2808+
2809+
# Examples
2810+
2811+
Basic usage:
2812+
2813+
```
2814+
", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_add(1), 101);
2815+
assert_eq!(200u8.saturating_add(127), 255);", $EndFeature, "
2816+
```"),
2817+
2818+
#[stable(feature = "rust1", since = "1.0.0")]
2819+
#[rustc_const_unstable(feature = "const_saturating_int_methods")]
2820+
#[inline]
2821+
#[cfg(not(stage0))]
2822+
pub const fn saturating_add(self, rhs: Self) -> Self {
2823+
intrinsics::saturating_add(self, rhs)
27662824
}
27672825
}
27682826

@@ -2780,16 +2838,33 @@ assert_eq!(13", stringify!($SelfT), ".saturating_sub(127), 0);", $EndFeature, "
27802838
```"),
27812839
#[stable(feature = "rust1", since = "1.0.0")]
27822840
#[inline]
2841+
#[cfg(stage0)]
27832842
pub fn saturating_sub(self, rhs: Self) -> Self {
2784-
#[cfg(stage0)]
27852843
match self.checked_sub(rhs) {
27862844
Some(x) => x,
27872845
None => Self::min_value(),
27882846
}
2789-
#[cfg(not(stage0))]
2790-
{
2791-
intrinsics::saturating_sub(self, rhs)
2792-
}
2847+
}
2848+
}
2849+
2850+
doc_comment! {
2851+
concat!("Saturating integer subtraction. Computes `self - rhs`, saturating
2852+
at the numeric bounds instead of overflowing.
2853+
2854+
# Examples
2855+
2856+
Basic usage:
2857+
2858+
```
2859+
", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_sub(27), 73);
2860+
assert_eq!(13", stringify!($SelfT), ".saturating_sub(127), 0);", $EndFeature, "
2861+
```"),
2862+
#[stable(feature = "rust1", since = "1.0.0")]
2863+
#[rustc_const_unstable(feature = "const_saturating_int_methods")]
2864+
#[inline]
2865+
#[cfg(not(stage0))]
2866+
pub const fn saturating_sub(self, rhs: Self) -> Self {
2867+
intrinsics::saturating_sub(self, rhs)
27932868
}
27942869
}
27952870

src/librustc_mir/interpret/intrinsics.rs

+44-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
55
use syntax::symbol::Symbol;
66
use rustc::ty;
7-
use rustc::ty::layout::{LayoutOf, Primitive};
7+
use rustc::ty::layout::{LayoutOf, Primitive, Size};
88
use rustc::mir::BinOp;
99
use rustc::mir::interpret::{
1010
EvalResult, EvalErrorKind, Scalar,
@@ -122,6 +122,49 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
122122
self.binop_with_overflow(bin_op, lhs, rhs, dest)?;
123123
}
124124
}
125+
"saturating_add" | "saturating_sub" => {
126+
let l = self.read_immediate(args[0])?;
127+
let r = self.read_immediate(args[1])?;
128+
let is_add = intrinsic_name == "saturating_add";
129+
let (val, overflowed) = self.binary_op_imm(if is_add {
130+
BinOp::Add
131+
} else {
132+
BinOp::Sub
133+
}, l, r)?;
134+
let val = if overflowed {
135+
let num_bits = l.layout.size.bits();
136+
if l.layout.abi.is_signed() {
137+
// For signed ints the saturated value depends on the sign of the first
138+
// term since the sign of the second term can be inferred from this and
139+
// the fact that the operation has overflowed (if either is 0 no
140+
// overflow can occur)
141+
let first_term: u128 = l.to_scalar()?.to_bits(l.layout.size)?;
142+
let first_term_positive = first_term & (1 << (num_bits-1)) == 0;
143+
if first_term_positive {
144+
// Negative overflow not possible since the positive first term
145+
// can only increase an (in range) negative term for addition
146+
// or corresponding negated positive term for subtraction
147+
Scalar::from_uint((1u128 << (num_bits - 1)) - 1, // max positive
148+
Size::from_bits(num_bits))
149+
} else {
150+
// Positive overflow not possible for similar reason
151+
// max negative
152+
Scalar::from_uint(1u128 << (num_bits - 1), Size::from_bits(num_bits))
153+
}
154+
} else { // unsigned
155+
if is_add {
156+
// max unsigned
157+
Scalar::from_uint(u128::max_value() >> (128 - num_bits),
158+
Size::from_bits(num_bits))
159+
} else { // underflow to 0
160+
Scalar::from_uint(0u128, Size::from_bits(num_bits))
161+
}
162+
}
163+
} else {
164+
val
165+
};
166+
self.write_scalar(val, dest)?;
167+
}
125168
"unchecked_shl" | "unchecked_shr" => {
126169
let l = self.read_immediate(args[0])?;
127170
let r = self.read_immediate(args[1])?;

src/librustc_mir/transform/qualify_consts.rs

+2
Original file line numberDiff line numberDiff line change
@@ -836,6 +836,8 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
836836
| "add_with_overflow"
837837
| "sub_with_overflow"
838838
| "mul_with_overflow"
839+
| "saturating_add"
840+
| "saturating_sub"
839841
// no need to check feature gates, intrinsics are only callable
840842
// from the libstd or with forever unstable feature gates
841843
=> is_const_fn = true,

src/librustc_mir/transform/qualify_min_const_fn.rs

+2
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,8 @@ fn is_intrinsic_whitelisted(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool
374374
| "overflowing_add" // ~> .wrapping_add
375375
| "overflowing_sub" // ~> .wrapping_sub
376376
| "overflowing_mul" // ~> .wrapping_mul
377+
| "saturating_add" // ~> .saturating_add
378+
| "saturating_sub" // ~> .saturating_sub
377379
| "unchecked_shl" // ~> .wrapping_shl
378380
| "unchecked_shr" // ~> .wrapping_shr
379381
| "rotate_left" // ~> .rotate_left
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// ignore-emscripten no i128 support
2+
#![feature(const_saturating_int_methods)]
3+
4+
const INT_U32_NO: u32 = (42 as u32).saturating_add(2);
5+
const INT_U32: u32 = u32::max_value().saturating_add(1);
6+
const INT_U128: u128 = u128::max_value().saturating_add(1);
7+
const INT_I128: i128 = i128::max_value().saturating_add(1);
8+
const INT_I128_NEG: i128 = i128::min_value().saturating_add(-1);
9+
10+
const INT_U32_NO_SUB: u32 = (42 as u32).saturating_sub(2);
11+
const INT_U32_SUB: u32 = (1 as u32).saturating_sub(2);
12+
const INT_I32_NO_SUB: i32 = (-42 as i32).saturating_sub(2);
13+
const INT_I32_NEG_SUB: i32 = i32::min_value().saturating_sub(1);
14+
const INT_I32_POS_SUB: i32 = i32::max_value().saturating_sub(-1);
15+
const INT_U128_SUB: u128 = (0 as u128).saturating_sub(1);
16+
const INT_I128_NEG_SUB: i128 = i128::min_value().saturating_sub(1);
17+
const INT_I128_POS_SUB: i128 = i128::max_value().saturating_sub(-1);
18+
19+
fn main() {
20+
assert_eq!(INT_U32_NO, 44);
21+
assert_eq!(INT_U32, u32::max_value());
22+
assert_eq!(INT_U128, u128::max_value());
23+
assert_eq!(INT_I128, i128::max_value());
24+
assert_eq!(INT_I128_NEG, i128::min_value());
25+
26+
assert_eq!(INT_U32_NO_SUB, 40);
27+
assert_eq!(INT_U32_SUB, 0);
28+
assert_eq!(INT_I32_NO_SUB, -44);
29+
assert_eq!(INT_I32_NEG_SUB, i32::min_value());
30+
assert_eq!(INT_I32_POS_SUB, i32::max_value());
31+
assert_eq!(INT_U128_SUB, 0);
32+
assert_eq!(INT_I128_NEG_SUB, i128::min_value());
33+
assert_eq!(INT_I128_POS_SUB, i128::max_value());
34+
}

0 commit comments

Comments
 (0)