Skip to content

Commit 0efb8e4

Browse files
committed
Allow const assignment for int saturating_sub() for #58030
1 parent 9204497 commit 0efb8e4

File tree

6 files changed

+86
-16
lines changed

6 files changed

+86
-16
lines changed

src/libcore/num/mod.rs

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -932,17 +932,35 @@ $EndFeature, "
932932
```"),
933933
#[stable(feature = "rust1", since = "1.0.0")]
934934
#[inline]
935+
#[cfg(stage0)]
935936
pub fn saturating_sub(self, rhs: Self) -> Self {
936-
#[cfg(stage0)]
937937
match self.checked_sub(rhs) {
938938
Some(x) => x,
939939
None if rhs >= 0 => Self::min_value(),
940940
None => Self::max_value(),
941941
}
942-
#[cfg(not(stage0))]
943-
{
944-
intrinsics::saturating_sub(self, rhs)
945-
}
942+
}
943+
}
944+
945+
doc_comment! {
946+
concat!("Saturating integer subtraction. Computes `self - rhs`, saturating at the
947+
numeric bounds instead of overflowing.
948+
949+
# Examples
950+
951+
Basic usage:
952+
953+
```
954+
", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_sub(127), -27);
955+
assert_eq!(", stringify!($SelfT), "::min_value().saturating_sub(100), ", stringify!($SelfT),
956+
"::min_value());",
957+
$EndFeature, "
958+
```"),
959+
#[stable(feature = "rust1", since = "1.0.0")]
960+
#[inline]
961+
#[cfg(not(stage0))]
962+
pub const fn saturating_sub(self, rhs: Self) -> Self {
963+
intrinsics::saturating_sub(self, rhs)
946964
}
947965
}
948966

@@ -2817,16 +2835,32 @@ assert_eq!(13", stringify!($SelfT), ".saturating_sub(127), 0);", $EndFeature, "
28172835
```"),
28182836
#[stable(feature = "rust1", since = "1.0.0")]
28192837
#[inline]
2838+
#[cfg(stage0)]
28202839
pub fn saturating_sub(self, rhs: Self) -> Self {
2821-
#[cfg(stage0)]
28222840
match self.checked_sub(rhs) {
28232841
Some(x) => x,
28242842
None => Self::min_value(),
28252843
}
2826-
#[cfg(not(stage0))]
2827-
{
2828-
intrinsics::saturating_sub(self, rhs)
2829-
}
2844+
}
2845+
}
2846+
2847+
doc_comment! {
2848+
concat!("Saturating integer subtraction. Computes `self - rhs`, saturating
2849+
at the numeric bounds instead of overflowing.
2850+
2851+
# Examples
2852+
2853+
Basic usage:
2854+
2855+
```
2856+
", $Feature, "assert_eq!(100", stringify!($SelfT), ".saturating_sub(27), 73);
2857+
assert_eq!(13", stringify!($SelfT), ".saturating_sub(127), 0);", $EndFeature, "
2858+
```"),
2859+
#[stable(feature = "rust1", since = "1.0.0")]
2860+
#[inline]
2861+
#[cfg(not(stage0))]
2862+
pub const fn saturating_sub(self, rhs: Self) -> Self {
2863+
intrinsics::saturating_sub(self, rhs)
28302864
}
28312865
}
28322866

src/librustc/mir/interpret/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,4 +418,4 @@ pub fn truncate(value: u128, size: Size) -> u128 {
418418
let shift = 128 - size;
419419
// truncate (shift left to drop out leftover values, shift right to fill with zeroes)
420420
(value << shift) >> shift
421-
}
421+
}

src/librustc_mir/interpret/intrinsics.rs

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,14 +130,15 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M>
130130
let first_term: u128 = l.to_scalar()?.to_bits(l.layout.size)?;
131131
let num_bits = l.layout.size.bits();
132132
let val = if l.layout.abi.is_signed() {
133-
// For signed addition the saturated value depends on the sign of either term
133+
// For signed addition the saturated value depends on the
134+
// sign of either term
134135
if first_term & (1 << (num_bits-1)) == 0 { // signed term is positive
135-
Scalar::from_uint((1u128 << (num_bits - 1)) - 1, Size::from_bits(num_bits)) // max signed val
136+
Scalar::from_uint((1u128 << (num_bits - 1)) - 1, Size::from_bits(num_bits))
136137
} else { // signed term is negative
137-
Scalar::from_uint(1u128 << (num_bits - 1), Size::from_bits(num_bits)) // min signed val
138+
Scalar::from_uint(1u128 << (num_bits - 1), Size::from_bits(num_bits))
138139
}
139140
} else {
140-
if num_bits == 128 { // General bit shift method causes overflow for u128 terms
141+
if num_bits == 128 {
141142
Scalar::from_uint(u128::max_value(), Size::from_bits(128))
142143
} else {
143144
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>
149150
self.write_scalar(val, dest)?;
150151
}
151152
}
153+
"saturating_sub" => {
154+
let l = self.read_immediate(args[0])?;
155+
let r = self.read_immediate(args[1])?;
156+
let (val, overflowed) = self.binary_op_imm(BinOp::Sub, l, r)?;
157+
if overflowed {
158+
let first_term: u128 = l.to_scalar()?.to_bits(l.layout.size)?;
159+
let num_bits = l.layout.size.bits();
160+
let val = if first_term & (1 << (num_bits-1)) == 0 { // first term is positive
161+
// so overflow is positive
162+
Scalar::from_uint((1u128 << (num_bits - 1)) - 1, Size::from_bits(num_bits))
163+
} else {
164+
// if first term negative, overflow must be negative
165+
Scalar::from_uint(1u128 << (num_bits - 1), Size::from_bits(num_bits))
166+
};
167+
self.write_scalar(val, dest)?;
168+
} else {
169+
self.write_scalar(val, dest)?;
170+
}
171+
}
152172
"unchecked_shl" | "unchecked_shr" => {
153173
let l = self.read_immediate(args[0])?;
154174
let r = self.read_immediate(args[1])?;

src/librustc_mir/transform/qualify_consts.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -821,6 +821,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
821821
| "sub_with_overflow"
822822
| "mul_with_overflow"
823823
| "saturating_add"
824+
| "saturating_sub"
824825
// no need to check feature gates, intrinsics are only callable
825826
// from the libstd or with forever unstable feature gates
826827
=> is_const_fn = true,

src/librustc_mir/transform/qualify_min_const_fn.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,7 @@ fn is_intrinsic_whitelisted(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> bool
375375
| "overflowing_sub" // ~> .wrapping_sub
376376
| "overflowing_mul" // ~> .wrapping_mul
377377
| "saturating_add" // ~> .saturating_add
378+
| "saturating_sub" // ~> .saturating_sub
378379
| "unchecked_shl" // ~> .wrapping_shl
379380
| "unchecked_shr" // ~> .wrapping_shr
380381
| "rotate_left" // ~> .rotate_left

src/test/run-pass/const-int-saturating-arith.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,24 @@ const INT_U128: u128 = u128::max_value().saturating_add(1);
44
const INT_I128: i128 = i128::max_value().saturating_add(1);
55
const INT_I128_NEG: i128 = i128::min_value().saturating_add(-1);
66

7+
const INT_U32_NO_SUB: u32 = (42 as u32).saturating_sub(2);
8+
const INT_I32_NO_SUB: i32 = (-42 as i32).saturating_sub(2);
9+
const INT_I32_NEG_SUB: i32 = i32::min_value().saturating_sub(1);
10+
const INT_I32_POS_SUB: i32 = i32::max_value().saturating_sub(-1);
11+
const INT_I128_NEG_SUB: i128 = i128::min_value().saturating_sub(1);
12+
const INT_I128_POS_SUB: i128 = i128::max_value().saturating_sub(-1);
13+
714
fn main() {
815
assert_eq!(INT_U32_NO, 44);
916
assert_eq!(INT_U32, u32::max_value());
1017
assert_eq!(INT_U128, u128::max_value());
1118
assert_eq!(INT_I128, i128::max_value());
1219
assert_eq!(INT_I128_NEG, i128::min_value());
13-
}
20+
21+
assert_eq!(INT_U32_NO_SUB, 40);
22+
assert_eq!(INT_I32_NO_SUB, -44);
23+
assert_eq!(INT_I32_NEG_SUB, i32::min_value());
24+
assert_eq!(INT_I32_POS_SUB, i32::max_value());
25+
assert_eq!(INT_I128_NEG_SUB, i128::min_value());
26+
assert_eq!(INT_I128_POS_SUB, i128::max_value());
27+
}

0 commit comments

Comments
 (0)