From 0a443eb4c6cb9f964251f0ea2677f182cce1a432 Mon Sep 17 00:00:00 2001 From: ltdk Date: Wed, 1 Nov 2023 14:28:55 -0400 Subject: [PATCH] Add assert_unsafe_precondition to unchecked_{add,sub,neg,mul,shl,shr} methods --- library/core/src/num/int_macros.rs | 74 ++++++++++++++++++++++------- library/core/src/num/uint_macros.rs | 62 ++++++++++++++++++------ 2 files changed, 103 insertions(+), 33 deletions(-) diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index fd01f1b261012..8ed077828edf4 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -475,9 +475,15 @@ macro_rules! int_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_add(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_add`. - unsafe { intrinsics::unchecked_add(self, rhs) } + // SAFETY: this is guaranteed to be safe by the caller. + unsafe { + let lhs = self; + intrinsics::assert_unsafe_precondition!( + concat!(stringify!($SelfT), "::unchecked_add cannot overflow"), + (lhs: $SelfT, rhs: $SelfT) => !lhs.overflowing_add(rhs).1 + ); + intrinsics::unchecked_add(lhs, rhs) + } } /// Checked addition with an unsigned integer. Computes `self + rhs`, @@ -543,9 +549,15 @@ macro_rules! int_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_sub(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_sub`. - unsafe { intrinsics::unchecked_sub(self, rhs) } + // SAFETY: this is guaranteed to be safe by the caller. + unsafe { + let lhs = self; + intrinsics::assert_unsafe_precondition!( + concat!(stringify!($SelfT), "::unchecked_sub cannot overflow"), + (lhs: $SelfT, rhs: $SelfT) => !lhs.overflowing_sub(rhs).1 + ); + intrinsics::unchecked_sub(lhs, rhs) + } } /// Checked subtraction with an unsigned integer. Computes `self - rhs`, @@ -611,9 +623,15 @@ macro_rules! int_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_mul`. - unsafe { intrinsics::unchecked_mul(self, rhs) } + // SAFETY: this is guaranteed to be safe by the caller. + unsafe { + let lhs = self; + intrinsics::assert_unsafe_precondition!( + concat!(stringify!($SelfT), "::unchecked_mul cannot overflow"), + (lhs: $SelfT, rhs: $SelfT) => !lhs.overflowing_mul(rhs).1 + ); + intrinsics::unchecked_mul(lhs, rhs) + } } /// Checked integer division. Computes `self / rhs`, returning `None` if `rhs == 0` @@ -760,9 +778,15 @@ macro_rules! int_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_neg(self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_neg`. - unsafe { intrinsics::unchecked_sub(0, self) } + // SAFETY: this is guaranteed to be safe by the caller. + unsafe { + let n = self; + intrinsics::assert_unsafe_precondition!( + concat!(stringify!($SelfT), "::unchecked_neg cannot overflow"), + (n: $SelfT) => !n.overflowing_neg().1 + ); + intrinsics::unchecked_sub(0, n) + } } /// Checked shift left. Computes `self << rhs`, returning `None` if `rhs` is larger @@ -807,10 +831,17 @@ macro_rules! int_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_shl`. + // SAFETY: this is guaranteed to be safe by the caller. // Any legal shift amount is losslessly representable in the self type. - unsafe { intrinsics::unchecked_shl(self, conv_rhs_for_unchecked_shift!($SelfT, rhs)) } + unsafe { + let lhs = self; + let rhs = conv_rhs_for_unchecked_shift!($SelfT, rhs); + intrinsics::assert_unsafe_precondition!( + concat!(stringify!($SelfT), "::unchecked_shl cannot overflow"), + (lhs: $SelfT, rhs: $SelfT) => !lhs.overflowing_shl(rhs).1 + ); + intrinsics::unchecked_shl(lhs, rhs) + } } /// Checked shift right. Computes `self >> rhs`, returning `None` if `rhs` is @@ -855,10 +886,17 @@ macro_rules! int_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_shr`. + // SAFETY: this is guaranteed to be safe by the caller. // Any legal shift amount is losslessly representable in the self type. - unsafe { intrinsics::unchecked_shr(self, conv_rhs_for_unchecked_shift!($SelfT, rhs)) } + unsafe { + let lhs = self; + let rhs = conv_rhs_for_unchecked_shift!($SelfT, rhs); + intrinsics::assert_unsafe_precondition!( + concat!(stringify!($SelfT), "::unchecked_shr cannot overflow"), + (lhs: $SelfT, rhs: $SelfT) => !lhs.overflowing_shr(rhs).1 + ); + intrinsics::unchecked_shr(lhs, rhs) + } } /// Checked absolute value. Computes `self.abs()`, returning `None` if diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 11a53aaf122ec..f1bd2fbc27d53 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -483,9 +483,15 @@ macro_rules! uint_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_add(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_add`. - unsafe { intrinsics::unchecked_add(self, rhs) } + // SAFETY: this is guaranteed to be safe by the caller. + unsafe { + let lhs = self; + intrinsics::assert_unsafe_precondition!( + concat!(stringify!($SelfT), "::unchecked_add cannot overflow"), + (lhs: $SelfT, rhs: $SelfT) => !lhs.overflowing_add(rhs).1 + ); + intrinsics::unchecked_add(lhs, rhs) + } } /// Checked addition with a signed integer. Computes `self + rhs`, @@ -552,9 +558,15 @@ macro_rules! uint_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_sub(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_sub`. - unsafe { intrinsics::unchecked_sub(self, rhs) } + // SAFETY: this is guaranteed to be safe by the caller. + unsafe { + let lhs = self; + intrinsics::assert_unsafe_precondition!( + concat!(stringify!($SelfT), "::unchecked_sub cannot overflow"), + (lhs: $SelfT, rhs: $SelfT) => !lhs.overflowing_sub(rhs).1 + ); + intrinsics::unchecked_sub(lhs, rhs) + } } /// Checked integer multiplication. Computes `self * rhs`, returning @@ -599,9 +611,15 @@ macro_rules! uint_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_mul(self, rhs: Self) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_mul`. - unsafe { intrinsics::unchecked_mul(self, rhs) } + // SAFETY: this is guaranteed to be safe by the caller. + unsafe { + let lhs = self; + intrinsics::assert_unsafe_precondition!( + concat!(stringify!($SelfT), "::unchecked_mul cannot overflow"), + (lhs: $SelfT, rhs: $SelfT) => !lhs.overflowing_mul(rhs).1 + ); + intrinsics::unchecked_mul(lhs, rhs) + } } /// Checked integer division. Computes `self / rhs`, returning `None` @@ -936,10 +954,17 @@ macro_rules! uint_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_shl(self, rhs: u32) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_shl`. + // SAFETY: this is guaranteed to be safe by the caller. // Any legal shift amount is losslessly representable in the self type. - unsafe { intrinsics::unchecked_shl(self, conv_rhs_for_unchecked_shift!($SelfT, rhs)) } + unsafe { + let lhs = self; + let rhs = conv_rhs_for_unchecked_shift!($SelfT, rhs); + intrinsics::assert_unsafe_precondition!( + concat!(stringify!($SelfT), "::unchecked_shl cannot overflow"), + (lhs: $SelfT, rhs: $SelfT) => !lhs.overflowing_shl(rhs).1 + ); + intrinsics::unchecked_shl(lhs, rhs) + } } /// Checked shift right. Computes `self >> rhs`, returning `None` @@ -984,10 +1009,17 @@ macro_rules! uint_impl { #[inline(always)] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn unchecked_shr(self, rhs: u32) -> Self { - // SAFETY: the caller must uphold the safety contract for - // `unchecked_shr`. + // SAFETY: this is guaranteed to be safe by the caller. // Any legal shift amount is losslessly representable in the self type. - unsafe { intrinsics::unchecked_shr(self, conv_rhs_for_unchecked_shift!($SelfT, rhs)) } + unsafe { + let lhs = self; + let rhs = conv_rhs_for_unchecked_shift!($SelfT, rhs); + intrinsics::assert_unsafe_precondition!( + concat!(stringify!($SelfT), "::unchecked_shl cannot overflow"), + (lhs: $SelfT, rhs: $SelfT) => !lhs.overflowing_shl(rhs).1 + ); + intrinsics::unchecked_shl(lhs, rhs) + } } /// Checked exponentiation. Computes `self.pow(exp)`, returning `None` if