Skip to content

Commit 4028cb5

Browse files
committed
Auto merge of rust-lang#121571 - clarfonthey:unchecked-math-preconditions, r=<try>
Add debug_assert_nounwind to unchecked_{add,sub,neg,mul,shl,shr} methods (Old PR is haunted, opening a new one. See rust-lang#117494 for previous discussion.) This ensures that these preconditions are actually checked in debug mode, and hopefully should let people know if they messed up. I've also replaced the calls (I could find) in the code that use these intrinsics directly with those that use these methods, so that the asserts actually apply. More discussions on people misusing these methods in the tracking issue: rust-lang#85122.
2 parents 381d699 + 949d1f9 commit 4028cb5

File tree

27 files changed

+620
-161
lines changed

27 files changed

+620
-161
lines changed

library/core/src/num/int_macros.rs

+57-22
Original file line numberDiff line numberDiff line change
@@ -510,9 +510,14 @@ macro_rules! int_impl {
510510
#[inline(always)]
511511
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
512512
pub const unsafe fn unchecked_add(self, rhs: Self) -> Self {
513-
// SAFETY: the caller must uphold the safety contract for
514-
// `unchecked_add`.
515-
unsafe { intrinsics::unchecked_add(self, rhs) }
513+
debug_assert_nounwind!(
514+
!self.overflowing_add(rhs).1,
515+
concat!(stringify!($SelfT), "::unchecked_add cannot overflow"),
516+
);
517+
// SAFETY: this is guaranteed to be safe by the caller.
518+
unsafe {
519+
intrinsics::unchecked_add(self, rhs)
520+
}
516521
}
517522

518523
/// Checked addition with an unsigned integer. Computes `self + rhs`,
@@ -648,9 +653,14 @@ macro_rules! int_impl {
648653
#[inline(always)]
649654
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
650655
pub const unsafe fn unchecked_sub(self, rhs: Self) -> Self {
651-
// SAFETY: the caller must uphold the safety contract for
652-
// `unchecked_sub`.
653-
unsafe { intrinsics::unchecked_sub(self, rhs) }
656+
debug_assert_nounwind!(
657+
!self.overflowing_sub(rhs).1,
658+
concat!(stringify!($SelfT), "::unchecked_sub cannot overflow"),
659+
);
660+
// SAFETY: this is guaranteed to be safe by the caller.
661+
unsafe {
662+
intrinsics::unchecked_sub(self, rhs)
663+
}
654664
}
655665

656666
/// Checked subtraction with an unsigned integer. Computes `self - rhs`,
@@ -786,9 +796,14 @@ macro_rules! int_impl {
786796
#[inline(always)]
787797
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
788798
pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self {
789-
// SAFETY: the caller must uphold the safety contract for
790-
// `unchecked_mul`.
791-
unsafe { intrinsics::unchecked_mul(self, rhs) }
799+
debug_assert_nounwind!(
800+
!self.overflowing_mul(rhs).1,
801+
concat!(stringify!($SelfT), "::unchecked_mul cannot overflow"),
802+
);
803+
// SAFETY: this is guaranteed to be safe by the caller.
804+
unsafe {
805+
intrinsics::unchecked_mul(self, rhs)
806+
}
792807
}
793808

794809
/// Checked integer division. Computes `self / rhs`, returning `None` if `rhs == 0`
@@ -1125,9 +1140,15 @@ macro_rules! int_impl {
11251140
#[inline(always)]
11261141
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
11271142
pub const unsafe fn unchecked_neg(self) -> Self {
1128-
// SAFETY: the caller must uphold the safety contract for
1129-
// `unchecked_neg`.
1130-
unsafe { intrinsics::unchecked_sub(0, self) }
1143+
debug_assert_nounwind!(
1144+
!self.overflowing_neg().1,
1145+
concat!(stringify!($SelfT), "::unchecked_neg cannot overflow"),
1146+
);
1147+
// SAFETY: this is guaranteed to be safe by the caller.
1148+
unsafe {
1149+
let n = self;
1150+
intrinsics::unchecked_sub(0, n)
1151+
}
11311152
}
11321153

11331154
/// Strict negation. Computes `-self`, panicking if `self == MIN`.
@@ -1179,7 +1200,7 @@ macro_rules! int_impl {
11791200
#[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")]
11801201
#[must_use = "this returns the result of the operation, \
11811202
without modifying the original"]
1182-
#[inline]
1203+
#[inline(always)]
11831204
pub const fn checked_shl(self, rhs: u32) -> Option<Self> {
11841205
let (a, b) = self.overflowing_shl(rhs);
11851206
if unlikely!(b) { None } else { Some(a) }
@@ -1241,10 +1262,15 @@ macro_rules! int_impl {
12411262
#[inline(always)]
12421263
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
12431264
pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self {
1244-
// SAFETY: the caller must uphold the safety contract for
1245-
// `unchecked_shl`.
1265+
debug_assert_nounwind!(
1266+
rhs < Self::BITS,
1267+
concat!(stringify!($SelfT), "::unchecked_shl cannot overflow"),
1268+
);
1269+
// SAFETY: this is guaranteed to be safe by the caller.
12461270
// Any legal shift amount is losslessly representable in the self type.
1247-
unsafe { intrinsics::unchecked_shl(self, conv_rhs_for_unchecked_shift!($SelfT, rhs)) }
1271+
unsafe {
1272+
intrinsics::unchecked_shl(self, conv_rhs_for_unchecked_shift!($SelfT, rhs))
1273+
}
12481274
}
12491275

12501276
/// Checked shift right. Computes `self >> rhs`, returning `None` if `rhs` is
@@ -1262,7 +1288,7 @@ macro_rules! int_impl {
12621288
#[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")]
12631289
#[must_use = "this returns the result of the operation, \
12641290
without modifying the original"]
1265-
#[inline]
1291+
#[inline(always)]
12661292
pub const fn checked_shr(self, rhs: u32) -> Option<Self> {
12671293
let (a, b) = self.overflowing_shr(rhs);
12681294
if unlikely!(b) { None } else { Some(a) }
@@ -1324,10 +1350,15 @@ macro_rules! int_impl {
13241350
#[inline(always)]
13251351
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
13261352
pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self {
1327-
// SAFETY: the caller must uphold the safety contract for
1328-
// `unchecked_shr`.
1353+
debug_assert_nounwind!(
1354+
rhs < Self::BITS,
1355+
concat!(stringify!($SelfT), "::unchecked_shr cannot overflow"),
1356+
);
1357+
// SAFETY: this is guaranteed to be safe by the caller.
13291358
// Any legal shift amount is losslessly representable in the self type.
1330-
unsafe { intrinsics::unchecked_shr(self, conv_rhs_for_unchecked_shift!($SelfT, rhs)) }
1359+
unsafe {
1360+
intrinsics::unchecked_shr(self, conv_rhs_for_unchecked_shift!($SelfT, rhs))
1361+
}
13311362
}
13321363

13331364
/// Checked absolute value. Computes `self.abs()`, returning `None` if
@@ -1991,7 +2022,9 @@ macro_rules! int_impl {
19912022
// SAFETY: the masking by the bitsize of the type ensures that we do not shift
19922023
// out of bounds
19932024
unsafe {
1994-
self.unchecked_shl(rhs & (Self::BITS - 1))
2025+
// FIXME: we can't optimize out the extra check here,
2026+
// so, we can't just call the method for now
2027+
intrinsics::unchecked_shl(self, conv_rhs_for_unchecked_shift!($SelfT, rhs & (Self::BITS - 1)))
19952028
}
19962029
}
19972030

@@ -2021,7 +2054,9 @@ macro_rules! int_impl {
20212054
// SAFETY: the masking by the bitsize of the type ensures that we do not shift
20222055
// out of bounds
20232056
unsafe {
2024-
self.unchecked_shr(rhs & (Self::BITS - 1))
2057+
// FIXME: we can't optimize out the extra check here,
2058+
// so, we can't just call the method for now
2059+
intrinsics::unchecked_shr(self, conv_rhs_for_unchecked_shift!($SelfT, rhs & (Self::BITS - 1)))
20252060
}
20262061
}
20272062

library/core/src/num/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::hint;
77
use crate::intrinsics;
88
use crate::mem;
99
use crate::ops::{Add, Mul, Sub};
10+
use crate::panic::debug_assert_nounwind;
1011
use crate::str::FromStr;
1112

1213
// Used because the `?` operator is not allowed in a const context.

library/core/src/num/uint_macros.rs

+49-19
Original file line numberDiff line numberDiff line change
@@ -518,9 +518,15 @@ macro_rules! uint_impl {
518518
#[inline(always)]
519519
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
520520
pub const unsafe fn unchecked_add(self, rhs: Self) -> Self {
521-
// SAFETY: the caller must uphold the safety contract for
522-
// `unchecked_add`.
523-
unsafe { intrinsics::unchecked_add(self, rhs) }
521+
debug_assert_nounwind!(
522+
!self.overflowing_add(rhs).1,
523+
concat!(stringify!($SelfT), "::unchecked_add cannot overflow"),
524+
);
525+
526+
// SAFETY: this is guaranteed to be safe by the caller.
527+
unsafe {
528+
intrinsics::unchecked_add(self, rhs)
529+
}
524530
}
525531

526532
/// Checked addition with a signed integer. Computes `self + rhs`,
@@ -662,9 +668,14 @@ macro_rules! uint_impl {
662668
#[inline(always)]
663669
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
664670
pub const unsafe fn unchecked_sub(self, rhs: Self) -> Self {
665-
// SAFETY: the caller must uphold the safety contract for
666-
// `unchecked_sub`.
667-
unsafe { intrinsics::unchecked_sub(self, rhs) }
671+
debug_assert_nounwind!(
672+
!self.overflowing_sub(rhs).1,
673+
concat!(stringify!($SelfT), "::unchecked_sub cannot overflow"),
674+
);
675+
// SAFETY: this is guaranteed to be safe by the caller.
676+
unsafe {
677+
intrinsics::unchecked_sub(self, rhs)
678+
}
668679
}
669680

670681
/// Checked integer multiplication. Computes `self * rhs`, returning
@@ -744,9 +755,14 @@ macro_rules! uint_impl {
744755
#[inline(always)]
745756
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
746757
pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self {
747-
// SAFETY: the caller must uphold the safety contract for
748-
// `unchecked_mul`.
749-
unsafe { intrinsics::unchecked_mul(self, rhs) }
758+
debug_assert_nounwind!(
759+
!self.overflowing_mul(rhs).1,
760+
concat!(stringify!($SelfT), "::unchecked_mul cannot overflow"),
761+
);
762+
// SAFETY: this is guaranteed to be safe by the caller.
763+
unsafe {
764+
intrinsics::unchecked_mul(self, rhs)
765+
}
750766
}
751767

752768
/// Checked integer division. Computes `self / rhs`, returning `None`
@@ -1239,7 +1255,7 @@ macro_rules! uint_impl {
12391255
#[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")]
12401256
#[must_use = "this returns the result of the operation, \
12411257
without modifying the original"]
1242-
#[inline]
1258+
#[inline(always)]
12431259
pub const fn checked_shl(self, rhs: u32) -> Option<Self> {
12441260
let (a, b) = self.overflowing_shl(rhs);
12451261
if unlikely!(b) { None } else { Some(a) }
@@ -1301,10 +1317,15 @@ macro_rules! uint_impl {
13011317
#[inline(always)]
13021318
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
13031319
pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self {
1304-
// SAFETY: the caller must uphold the safety contract for
1305-
// `unchecked_shl`.
1320+
debug_assert_nounwind!(
1321+
rhs < Self::BITS,
1322+
concat!(stringify!($SelfT), "::unchecked_shl cannot overflow"),
1323+
);
1324+
// SAFETY: this is guaranteed to be safe by the caller.
13061325
// Any legal shift amount is losslessly representable in the self type.
1307-
unsafe { intrinsics::unchecked_shl(self, conv_rhs_for_unchecked_shift!($SelfT, rhs)) }
1326+
unsafe {
1327+
intrinsics::unchecked_shl(self, conv_rhs_for_unchecked_shift!($SelfT, rhs))
1328+
}
13081329
}
13091330

13101331
/// Checked shift right. Computes `self >> rhs`, returning `None`
@@ -1322,7 +1343,7 @@ macro_rules! uint_impl {
13221343
#[rustc_const_stable(feature = "const_checked_int_methods", since = "1.47.0")]
13231344
#[must_use = "this returns the result of the operation, \
13241345
without modifying the original"]
1325-
#[inline]
1346+
#[inline(always)]
13261347
pub const fn checked_shr(self, rhs: u32) -> Option<Self> {
13271348
let (a, b) = self.overflowing_shr(rhs);
13281349
if unlikely!(b) { None } else { Some(a) }
@@ -1384,10 +1405,15 @@ macro_rules! uint_impl {
13841405
#[inline(always)]
13851406
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
13861407
pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self {
1387-
// SAFETY: the caller must uphold the safety contract for
1388-
// `unchecked_shr`.
1408+
debug_assert_nounwind!(
1409+
rhs < Self::BITS,
1410+
concat!(stringify!($SelfT), "::unchecked_shr cannot overflow"),
1411+
);
1412+
// SAFETY: this is guaranteed to be safe by the caller.
13891413
// Any legal shift amount is losslessly representable in the self type.
1390-
unsafe { intrinsics::unchecked_shr(self, conv_rhs_for_unchecked_shift!($SelfT, rhs)) }
1414+
unsafe {
1415+
intrinsics::unchecked_shr(self, conv_rhs_for_unchecked_shift!($SelfT, rhs))
1416+
}
13911417
}
13921418

13931419
/// Checked exponentiation. Computes `self.pow(exp)`, returning `None` if
@@ -1878,7 +1904,9 @@ macro_rules! uint_impl {
18781904
// SAFETY: the masking by the bitsize of the type ensures that we do not shift
18791905
// out of bounds
18801906
unsafe {
1881-
self.unchecked_shl(rhs & (Self::BITS - 1))
1907+
// FIXME: we can't optimize out the extra check here,
1908+
// so, we can't just call the method for now
1909+
intrinsics::unchecked_shl(self, conv_rhs_for_unchecked_shift!($SelfT, rhs & (Self::BITS - 1)))
18821910
}
18831911
}
18841912

@@ -1911,7 +1939,9 @@ macro_rules! uint_impl {
19111939
// SAFETY: the masking by the bitsize of the type ensures that we do not shift
19121940
// out of bounds
19131941
unsafe {
1914-
self.unchecked_shr(rhs & (Self::BITS - 1))
1942+
// FIXME: we can't optimize out the extra check here,
1943+
// so, we can't just call the method for now
1944+
intrinsics::unchecked_shr(self, conv_rhs_for_unchecked_shift!($SelfT, rhs & (Self::BITS - 1)))
19151945
}
19161946
}
19171947

library/core/src/ops/index_range.rs

+5-6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use crate::intrinsics::{unchecked_add, unchecked_sub};
21
use crate::iter::{FusedIterator, TrustedLen};
32
use crate::num::NonZero;
43

@@ -44,7 +43,7 @@ impl IndexRange {
4443
#[inline]
4544
pub const fn len(&self) -> usize {
4645
// SAFETY: By invariant, this cannot wrap
47-
unsafe { unchecked_sub(self.end, self.start) }
46+
unsafe { self.end.unchecked_sub(self.start) }
4847
}
4948

5049
/// # Safety
@@ -55,7 +54,7 @@ impl IndexRange {
5554

5655
let value = self.start;
5756
// SAFETY: The range isn't empty, so this cannot overflow
58-
self.start = unsafe { unchecked_add(value, 1) };
57+
self.start = unsafe { value.unchecked_add(1) };
5958
value
6059
}
6160

@@ -66,7 +65,7 @@ impl IndexRange {
6665
debug_assert!(self.start < self.end);
6766

6867
// SAFETY: The range isn't empty, so this cannot overflow
69-
let value = unsafe { unchecked_sub(self.end, 1) };
68+
let value = unsafe { self.end.unchecked_sub(1) };
7069
self.end = value;
7170
value
7271
}
@@ -81,7 +80,7 @@ impl IndexRange {
8180
let mid = if n <= self.len() {
8281
// SAFETY: We just checked that this will be between start and end,
8382
// and thus the addition cannot overflow.
84-
unsafe { unchecked_add(self.start, n) }
83+
unsafe { self.start.unchecked_add(n) }
8584
} else {
8685
self.end
8786
};
@@ -100,7 +99,7 @@ impl IndexRange {
10099
let mid = if n <= self.len() {
101100
// SAFETY: We just checked that this will be between start and end,
102101
// and thus the addition cannot overflow.
103-
unsafe { unchecked_sub(self.end, n) }
102+
unsafe { self.end.unchecked_sub(n) }
104103
} else {
105104
self.start
106105
};

library/core/src/panic.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ pub macro debug_assert_nounwind {
167167
}
168168
}
169169
},
170-
($cond:expr, $message:expr) => {
170+
($cond:expr, $message:expr $(,)?) => {
171171
if $crate::intrinsics::debug_assertions() {
172172
if !$cond {
173173
$crate::panicking::panic_nounwind($message);

library/core/src/ptr/const_ptr.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -1021,8 +1021,8 @@ impl<T: ?Sized> *const T {
10211021
#[must_use = "returns a new pointer rather than modifying its argument"]
10221022
#[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
10231023
// We could always go back to wrapping if unchecked becomes unacceptable
1024-
#[rustc_allow_const_fn_unstable(const_int_unchecked_arith)]
10251024
#[inline(always)]
1025+
#[rustc_allow_const_fn_unstable(unchecked_math)]
10261026
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
10271027
pub const unsafe fn sub(self, count: usize) -> Self
10281028
where
@@ -1035,7 +1035,10 @@ impl<T: ?Sized> *const T {
10351035
// SAFETY: the caller must uphold the safety contract for `offset`.
10361036
// Because the pointee is *not* a ZST, that means that `count` is
10371037
// at most `isize::MAX`, and thus the negation cannot overflow.
1038-
unsafe { self.offset(intrinsics::unchecked_sub(0, count as isize)) }
1038+
// FIXME: replacing unchecked_sub with unchecked_neg and replacing the
1039+
// unchecked_math flag with unchecked_neg will anger the UnstableInStable lint
1040+
// and I cannot for the life of me understand why
1041+
unsafe { self.offset(0isize.unchecked_sub(count as isize)) }
10391042
}
10401043
}
10411044

library/core/src/ptr/mut_ptr.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -1121,7 +1121,7 @@ impl<T: ?Sized> *mut T {
11211121
#[must_use = "returns a new pointer rather than modifying its argument"]
11221122
#[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
11231123
// We could always go back to wrapping if unchecked becomes unacceptable
1124-
#[rustc_allow_const_fn_unstable(const_int_unchecked_arith)]
1124+
#[rustc_allow_const_fn_unstable(unchecked_math)]
11251125
#[inline(always)]
11261126
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
11271127
pub const unsafe fn sub(self, count: usize) -> Self
@@ -1135,7 +1135,10 @@ impl<T: ?Sized> *mut T {
11351135
// SAFETY: the caller must uphold the safety contract for `offset`.
11361136
// Because the pointee is *not* a ZST, that means that `count` is
11371137
// at most `isize::MAX`, and thus the negation cannot overflow.
1138-
unsafe { self.offset(intrinsics::unchecked_sub(0, count as isize)) }
1138+
// FIXME: replacing unchecked_sub with unchecked_neg and replacing the
1139+
// unchecked_math flag with unchecked_neg will anger the UnstableInStable lint
1140+
// and I cannot for the life of me understand why
1141+
unsafe { self.offset(0isize.unchecked_sub(count as isize)) }
11391142
}
11401143
}
11411144

0 commit comments

Comments
 (0)