Skip to content

Commit 04d11bc

Browse files
committed
Auto merge of rust-lang#121571 - clarfonthey:unchecked-math-preconditions, r=<try>
Add assert_unsafe_precondition 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 bb78dba + 0229426 commit 04d11bc

18 files changed

+255
-57
lines changed

library/core/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@
192192
#![feature(str_split_inclusive_remainder)]
193193
#![feature(str_split_remainder)]
194194
#![feature(strict_provenance)]
195+
#![feature(unchecked_neg)]
195196
#![feature(unchecked_shifts)]
196197
#![feature(utf16_extra)]
197198
#![feature(utf16_extra_const)]

library/core/src/num/int_macros.rs

+78-18
Original file line numberDiff line numberDiff line change
@@ -488,9 +488,19 @@ macro_rules! int_impl {
488488
#[inline(always)]
489489
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
490490
pub const unsafe fn unchecked_add(self, rhs: Self) -> Self {
491-
// SAFETY: the caller must uphold the safety contract for
492-
// `unchecked_add`.
493-
unsafe { intrinsics::unchecked_add(self, rhs) }
491+
assert_unsafe_precondition!(
492+
check_language_ub,
493+
concat!(stringify!($SelfT), "::unchecked_add cannot overflow"),
494+
(
495+
lhs: $SelfT = self,
496+
rhs: $SelfT = rhs,
497+
) => !lhs.overflowing_add(rhs).1,
498+
);
499+
500+
// SAFETY: this is guaranteed to be safe by the caller.
501+
unsafe {
502+
intrinsics::unchecked_add(self, rhs)
503+
}
494504
}
495505

496506
/// Checked addition with an unsigned integer. Computes `self + rhs`,
@@ -630,9 +640,19 @@ macro_rules! int_impl {
630640
#[inline(always)]
631641
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
632642
pub const unsafe fn unchecked_sub(self, rhs: Self) -> Self {
633-
// SAFETY: the caller must uphold the safety contract for
634-
// `unchecked_sub`.
635-
unsafe { intrinsics::unchecked_sub(self, rhs) }
643+
assert_unsafe_precondition!(
644+
check_language_ub,
645+
concat!(stringify!($SelfT), "::unchecked_sub cannot overflow"),
646+
(
647+
lhs: $SelfT = self,
648+
rhs: $SelfT = rhs,
649+
) => !lhs.overflowing_sub(rhs).1,
650+
);
651+
652+
// SAFETY: this is guaranteed to be safe by the caller.
653+
unsafe {
654+
intrinsics::unchecked_sub(self, rhs)
655+
}
636656
}
637657

638658
/// Checked subtraction with an unsigned integer. Computes `self - rhs`,
@@ -772,9 +792,19 @@ macro_rules! int_impl {
772792
#[inline(always)]
773793
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
774794
pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self {
775-
// SAFETY: the caller must uphold the safety contract for
776-
// `unchecked_mul`.
777-
unsafe { intrinsics::unchecked_mul(self, rhs) }
795+
assert_unsafe_precondition!(
796+
check_language_ub,
797+
concat!(stringify!($SelfT), "::unchecked_mul cannot overflow"),
798+
(
799+
lhs: $SelfT = self,
800+
rhs: $SelfT = rhs,
801+
) => !lhs.overflowing_mul(rhs).1,
802+
);
803+
804+
// SAFETY: this is guaranteed to be safe by the caller.
805+
unsafe {
806+
intrinsics::unchecked_mul(self, rhs)
807+
}
778808
}
779809

780810
/// Checked integer division. Computes `self / rhs`, returning `None` if `rhs == 0`
@@ -1111,9 +1141,18 @@ macro_rules! int_impl {
11111141
#[inline(always)]
11121142
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
11131143
pub const unsafe fn unchecked_neg(self) -> Self {
1114-
// SAFETY: the caller must uphold the safety contract for
1115-
// `unchecked_neg`.
1116-
unsafe { intrinsics::unchecked_sub(0, self) }
1144+
assert_unsafe_precondition!(
1145+
check_language_ub,
1146+
concat!(stringify!($SelfT), "::unchecked_neg cannot overflow"),
1147+
(
1148+
lhs: $SelfT = self,
1149+
) => !lhs.overflowing_neg().1,
1150+
);
1151+
1152+
// SAFETY: this is guaranteed to be safe by the caller.
1153+
unsafe {
1154+
intrinsics::unchecked_sub(0, self)
1155+
}
11171156
}
11181157

11191158
/// Strict negation. Computes `-self`, panicking if `self == MIN`.
@@ -1235,9 +1274,19 @@ macro_rules! int_impl {
12351274
}
12361275
#[cfg(not(bootstrap))]
12371276
{
1238-
// SAFETY: the caller must uphold the safety contract for
1239-
// `unchecked_shl`.
1240-
unsafe { intrinsics::unchecked_shl(self, rhs) }
1277+
assert_unsafe_precondition!(
1278+
check_language_ub,
1279+
concat!(stringify!($SelfT), "::unchecked_shl cannot overflow"),
1280+
(
1281+
rhs: u32 = rhs,
1282+
bits: u32 = Self::BITS,
1283+
) => rhs < bits,
1284+
);
1285+
1286+
// SAFETY: this is guaranteed to be safe by the caller.
1287+
unsafe {
1288+
intrinsics::unchecked_shl(self, rhs)
1289+
}
12411290
}
12421291
}
12431292

@@ -1326,9 +1375,20 @@ macro_rules! int_impl {
13261375
}
13271376
#[cfg(not(bootstrap))]
13281377
{
1329-
// SAFETY: the caller must uphold the safety contract for
1330-
// `unchecked_shr`.
1331-
unsafe { intrinsics::unchecked_shr(self, rhs) }
1378+
assert_unsafe_precondition!(
1379+
check_language_ub,
1380+
concat!(stringify!($SelfT), "::unchecked_shr cannot overflow"),
1381+
(
1382+
rhs: u32 = rhs,
1383+
bits: u32 = Self::BITS,
1384+
) => rhs < bits,
1385+
);
1386+
1387+
// SAFETY: this is guaranteed to be safe by the caller.
1388+
// Any legal shift amount is losslessly representable in the self type.
1389+
unsafe {
1390+
intrinsics::unchecked_shr(self, rhs)
1391+
}
13321392
}
13331393
}
13341394

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::str::FromStr;
10+
use crate::ub_checks::assert_unsafe_precondition;
1011

1112
// Used because the `?` operator is not allowed in a const context.
1213
macro_rules! try_opt {

library/core/src/num/uint_macros.rs

+66-15
Original file line numberDiff line numberDiff line change
@@ -495,9 +495,19 @@ macro_rules! uint_impl {
495495
#[inline(always)]
496496
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
497497
pub const unsafe fn unchecked_add(self, rhs: Self) -> Self {
498-
// SAFETY: the caller must uphold the safety contract for
499-
// `unchecked_add`.
500-
unsafe { intrinsics::unchecked_add(self, rhs) }
498+
assert_unsafe_precondition!(
499+
check_language_ub,
500+
concat!(stringify!($SelfT), "::unchecked_add cannot overflow"),
501+
(
502+
lhs: $SelfT = self,
503+
rhs: $SelfT = rhs,
504+
) => !lhs.overflowing_add(rhs).1,
505+
);
506+
507+
// SAFETY: this is guaranteed to be safe by the caller.
508+
unsafe {
509+
intrinsics::unchecked_add(self, rhs)
510+
}
501511
}
502512

503513
/// Checked addition with a signed integer. Computes `self + rhs`,
@@ -643,9 +653,19 @@ macro_rules! uint_impl {
643653
#[inline(always)]
644654
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
645655
pub const unsafe fn unchecked_sub(self, rhs: Self) -> Self {
646-
// SAFETY: the caller must uphold the safety contract for
647-
// `unchecked_sub`.
648-
unsafe { intrinsics::unchecked_sub(self, rhs) }
656+
assert_unsafe_precondition!(
657+
check_language_ub,
658+
concat!(stringify!($SelfT), "::unchecked_sub cannot overflow"),
659+
(
660+
lhs: $SelfT = self,
661+
rhs: $SelfT = rhs,
662+
) => !lhs.overflowing_sub(rhs).1,
663+
);
664+
665+
// SAFETY: this is guaranteed to be safe by the caller.
666+
unsafe {
667+
intrinsics::unchecked_sub(self, rhs)
668+
}
649669
}
650670

651671
/// Checked integer multiplication. Computes `self * rhs`, returning
@@ -729,9 +749,19 @@ macro_rules! uint_impl {
729749
#[inline(always)]
730750
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
731751
pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self {
732-
// SAFETY: the caller must uphold the safety contract for
733-
// `unchecked_mul`.
734-
unsafe { intrinsics::unchecked_mul(self, rhs) }
752+
assert_unsafe_precondition!(
753+
check_language_ub,
754+
concat!(stringify!($SelfT), "::unchecked_mul cannot overflow"),
755+
(
756+
lhs: $SelfT = self,
757+
rhs: $SelfT = rhs,
758+
) => !lhs.overflowing_mul(rhs).1,
759+
);
760+
761+
// SAFETY: this is guaranteed to be safe by the caller.
762+
unsafe {
763+
intrinsics::unchecked_mul(self, rhs)
764+
}
735765
}
736766

737767
/// Checked integer division. Computes `self / rhs`, returning `None`
@@ -1294,9 +1324,19 @@ macro_rules! uint_impl {
12941324
}
12951325
#[cfg(not(bootstrap))]
12961326
{
1297-
// SAFETY: the caller must uphold the safety contract for
1298-
// `unchecked_shl`.
1299-
unsafe { intrinsics::unchecked_shl(self, rhs) }
1327+
assert_unsafe_precondition!(
1328+
check_language_ub,
1329+
concat!(stringify!($SelfT), "::unchecked_shl cannot overflow"),
1330+
(
1331+
rhs: u32 = rhs,
1332+
bits: u32 = Self::BITS,
1333+
) => rhs < bits,
1334+
);
1335+
1336+
// SAFETY: this is guaranteed to be safe by the caller.
1337+
unsafe {
1338+
intrinsics::unchecked_shl(self, rhs)
1339+
}
13001340
}
13011341
}
13021342

@@ -1385,9 +1425,20 @@ macro_rules! uint_impl {
13851425
}
13861426
#[cfg(not(bootstrap))]
13871427
{
1388-
// SAFETY: the caller must uphold the safety contract for
1389-
// `unchecked_shr`.
1390-
unsafe { intrinsics::unchecked_shr(self, rhs) }
1428+
assert_unsafe_precondition!(
1429+
check_language_ub,
1430+
concat!(stringify!($SelfT), "::unchecked_shr cannot overflow"),
1431+
(
1432+
rhs: u32 = rhs,
1433+
bits: u32 = Self::BITS,
1434+
) => rhs < bits,
1435+
);
1436+
1437+
// SAFETY: this is guaranteed to be safe by the caller.
1438+
// Any legal shift amount is losslessly representable in the self type.
1439+
unsafe {
1440+
intrinsics::unchecked_shr(self, rhs)
1441+
}
13911442
}
13921443
}
13931444

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
use crate::ub_checks;
@@ -46,7 +45,7 @@ impl IndexRange {
4645
#[inline]
4746
pub const fn len(&self) -> usize {
4847
// SAFETY: By invariant, this cannot wrap
49-
unsafe { unchecked_sub(self.end, self.start) }
48+
unsafe { self.end.unchecked_sub(self.start) }
5049
}
5150

5251
/// # Safety
@@ -57,7 +56,7 @@ impl IndexRange {
5756

5857
let value = self.start;
5958
// SAFETY: The range isn't empty, so this cannot overflow
60-
self.start = unsafe { unchecked_add(value, 1) };
59+
self.start = unsafe { value.unchecked_add(1) };
6160
value
6261
}
6362

@@ -68,7 +67,7 @@ impl IndexRange {
6867
debug_assert!(self.start < self.end);
6968

7069
// SAFETY: The range isn't empty, so this cannot overflow
71-
let value = unsafe { unchecked_sub(self.end, 1) };
70+
let value = unsafe { self.end.unchecked_sub(1) };
7271
self.end = value;
7372
value
7473
}
@@ -83,7 +82,7 @@ impl IndexRange {
8382
let mid = if n <= self.len() {
8483
// SAFETY: We just checked that this will be between start and end,
8584
// and thus the addition cannot overflow.
86-
unsafe { unchecked_add(self.start, n) }
85+
unsafe { self.start.unchecked_add(n) }
8786
} else {
8887
self.end
8988
};
@@ -102,7 +101,7 @@ impl IndexRange {
102101
let mid = if n <= self.len() {
103102
// SAFETY: We just checked that this will be between start and end,
104103
// and thus the addition cannot overflow.
105-
unsafe { unchecked_sub(self.end, n) }
104+
unsafe { self.end.unchecked_sub(n) }
106105
} else {
107106
self.start
108107
};

library/core/src/ptr/const_ptr.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1029,6 +1029,7 @@ impl<T: ?Sized> *const T {
10291029
#[stable(feature = "pointer_methods", since = "1.26.0")]
10301030
#[must_use = "returns a new pointer rather than modifying its argument"]
10311031
#[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
1032+
#[rustc_allow_const_fn_unstable(unchecked_neg)]
10321033
#[inline(always)]
10331034
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
10341035
pub const unsafe fn sub(self, count: usize) -> Self
@@ -1042,7 +1043,7 @@ impl<T: ?Sized> *const T {
10421043
// SAFETY: the caller must uphold the safety contract for `offset`.
10431044
// Because the pointee is *not* a ZST, that means that `count` is
10441045
// at most `isize::MAX`, and thus the negation cannot overflow.
1045-
unsafe { self.offset(intrinsics::unchecked_sub(0, count as isize)) }
1046+
unsafe { self.offset((count as isize).unchecked_neg()) }
10461047
}
10471048
}
10481049

library/core/src/ptr/mut_ptr.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1118,6 +1118,7 @@ impl<T: ?Sized> *mut T {
11181118
#[stable(feature = "pointer_methods", since = "1.26.0")]
11191119
#[must_use = "returns a new pointer rather than modifying its argument"]
11201120
#[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
1121+
#[rustc_allow_const_fn_unstable(unchecked_neg)]
11211122
#[inline(always)]
11221123
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
11231124
pub const unsafe fn sub(self, count: usize) -> Self
@@ -1131,7 +1132,7 @@ impl<T: ?Sized> *mut T {
11311132
// SAFETY: the caller must uphold the safety contract for `offset`.
11321133
// Because the pointee is *not* a ZST, that means that `count` is
11331134
// at most `isize::MAX`, and thus the negation cannot overflow.
1134-
unsafe { self.offset(intrinsics::unchecked_sub(0, count as isize)) }
1135+
unsafe { self.offset((count as isize).unchecked_neg()) }
11351136
}
11361137
}
11371138

library/core/src/ptr/non_null.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,7 @@ impl<T: ?Sized> NonNull<T> {
701701
/// ```
702702
#[unstable(feature = "non_null_convenience", issue = "117691")]
703703
#[rustc_const_unstable(feature = "non_null_convenience", issue = "117691")]
704+
#[rustc_allow_const_fn_unstable(unchecked_neg)]
704705
#[must_use = "returns a new pointer rather than modifying its argument"]
705706
#[inline(always)]
706707
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
@@ -715,7 +716,7 @@ impl<T: ?Sized> NonNull<T> {
715716
// SAFETY: the caller must uphold the safety contract for `offset`.
716717
// Because the pointee is *not* a ZST, that means that `count` is
717718
// at most `isize::MAX`, and thus the negation cannot overflow.
718-
unsafe { self.offset(intrinsics::unchecked_sub(0, count as isize)) }
719+
unsafe { self.offset((count as isize).unchecked_neg()) }
719720
}
720721
}
721722

library/core/src/slice/index.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
//! Indexing implementations for `[T]`.
22
33
use crate::intrinsics::const_eval_select;
4-
use crate::intrinsics::unchecked_sub;
54
use crate::ops;
65
use crate::ptr;
76
use crate::ub_checks::assert_unsafe_precondition;
@@ -374,7 +373,7 @@ unsafe impl<T> SliceIndex<[T]> for ops::Range<usize> {
374373
// `self` is in bounds of `slice` so `self` cannot overflow an `isize`,
375374
// so the call to `add` is safe and the length calculation cannot overflow.
376375
unsafe {
377-
let new_len = unchecked_sub(self.end, self.start);
376+
let new_len = self.end.unchecked_sub(self.start);
378377
ptr::slice_from_raw_parts(slice.as_ptr().add(self.start), new_len)
379378
}
380379
}
@@ -392,7 +391,7 @@ unsafe impl<T> SliceIndex<[T]> for ops::Range<usize> {
392391
);
393392
// SAFETY: see comments for `get_unchecked` above.
394393
unsafe {
395-
let new_len = unchecked_sub(self.end, self.start);
394+
let new_len = self.end.unchecked_sub(self.start);
396395
ptr::slice_from_raw_parts_mut(slice.as_mut_ptr().add(self.start), new_len)
397396
}
398397
}

0 commit comments

Comments
 (0)