Rollup merge of #116090 - rmehri01:strict_integer_ops, r=m-ou-se

Implement strict integer operations that panic on overflow

This PR implements the first part of the ACP for adding panic on overflow style arithmetic operations (https://github.com/rust-lang/libs-team/issues/270), mentioned in #116064.

It adds the following operations on both signed and unsigned integers:

- `strict_add`
- `strict_sub`
- `strict_mul`
- `strict_div`
- `strict_div_euclid`
- `strict_rem`
- `strict_rem_euclid`
- `strict_neg`
- `strict_shl`
- `strict_shr`
- `strict_pow`

Additionally, signed integers have:

- `strict_add_unsigned`
- `strict_sub_unsigned`
- `strict_abs`

And unsigned integers have:

- `strict_add_signed`

The `div` and `rem` operations are the same as normal division and remainder but are added for completeness similar to the corresponding `wrapping_*` operations.

I'm not sure if I missed any operations, I basically found them from the `wrapping_*` and `checked_*` operations on both integer types.
This commit is contained in:
Nadrieril 2024-01-21 06:38:34 +01:00 committed by GitHub
commit b661cd6c57
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 996 additions and 15 deletions

View File

@ -161,6 +161,7 @@
#![feature(const_slice_ptr_len)]
#![feature(const_slice_split_at_mut)]
#![feature(const_str_from_utf8_unchecked_mut)]
#![feature(const_strict_overflow_ops)]
#![feature(const_swap)]
#![feature(const_try)]
#![feature(const_type_id)]

View File

@ -451,7 +451,40 @@ macro_rules! int_impl {
#[inline]
pub const fn checked_add(self, rhs: Self) -> Option<Self> {
let (a, b) = self.overflowing_add(rhs);
if unlikely!(b) {None} else {Some(a)}
if unlikely!(b) { None } else { Some(a) }
}
/// Strict integer addition. Computes `self + rhs`, panicking
/// if overflow occurred.
///
/// # Panics
///
/// ## Overflow behavior
///
/// This function will always panic on overflow, regardless of whether overflow checks are enabled.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(strict_overflow_ops)]
#[doc = concat!("assert_eq!((", stringify!($SelfT), "::MAX - 2).strict_add(1), ", stringify!($SelfT), "::MAX - 1);")]
/// ```
///
/// ```should_panic
/// #![feature(strict_overflow_ops)]
#[doc = concat!("let _ = (", stringify!($SelfT), "::MAX - 2).strict_add(3);")]
/// ```
#[unstable(feature = "strict_overflow_ops", issue = "118260")]
#[rustc_const_unstable(feature = "const_strict_overflow_ops", issue = "118260")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
#[track_caller]
pub const fn strict_add(self, rhs: Self) -> Self {
let (a, b) = self.overflowing_add(rhs);
if unlikely!(b) { overflow_panic::add() } else { a }
}
/// Unchecked integer addition. Computes `self + rhs`, assuming overflow
@ -498,7 +531,40 @@ macro_rules! int_impl {
#[inline]
pub const fn checked_add_unsigned(self, rhs: $UnsignedT) -> Option<Self> {
let (a, b) = self.overflowing_add_unsigned(rhs);
if unlikely!(b) {None} else {Some(a)}
if unlikely!(b) { None } else { Some(a) }
}
/// Strict addition with an unsigned integer. Computes `self + rhs`,
/// panicking if overflow occurred.
///
/// # Panics
///
/// ## Overflow behavior
///
/// This function will always panic on overflow, regardless of whether overflow checks are enabled.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(strict_overflow_ops)]
#[doc = concat!("assert_eq!(1", stringify!($SelfT), ".strict_add_unsigned(2), 3);")]
/// ```
///
/// ```should_panic
/// #![feature(strict_overflow_ops)]
#[doc = concat!("let _ = (", stringify!($SelfT), "::MAX - 2).strict_add_unsigned(3);")]
/// ```
#[unstable(feature = "strict_overflow_ops", issue = "118260")]
#[rustc_const_unstable(feature = "const_strict_overflow_ops", issue = "118260")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
#[track_caller]
pub const fn strict_add_unsigned(self, rhs: $UnsignedT) -> Self {
let (a, b) = self.overflowing_add_unsigned(rhs);
if unlikely!(b) { overflow_panic::add() } else { a }
}
/// Checked integer subtraction. Computes `self - rhs`, returning `None` if
@ -519,7 +585,40 @@ macro_rules! int_impl {
#[inline]
pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
let (a, b) = self.overflowing_sub(rhs);
if unlikely!(b) {None} else {Some(a)}
if unlikely!(b) { None } else { Some(a) }
}
/// Strict integer subtraction. Computes `self - rhs`, panicking if
/// overflow occurred.
///
/// # Panics
///
/// ## Overflow behavior
///
/// This function will always panic on overflow, regardless of whether overflow checks are enabled.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(strict_overflow_ops)]
#[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 2).strict_sub(1), ", stringify!($SelfT), "::MIN + 1);")]
/// ```
///
/// ```should_panic
/// #![feature(strict_overflow_ops)]
#[doc = concat!("let _ = (", stringify!($SelfT), "::MIN + 2).strict_sub(3);")]
/// ```
#[unstable(feature = "strict_overflow_ops", issue = "118260")]
#[rustc_const_unstable(feature = "const_strict_overflow_ops", issue = "118260")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
#[track_caller]
pub const fn strict_sub(self, rhs: Self) -> Self {
let (a, b) = self.overflowing_sub(rhs);
if unlikely!(b) { overflow_panic::sub() } else { a }
}
/// Unchecked integer subtraction. Computes `self - rhs`, assuming overflow
@ -566,7 +665,40 @@ macro_rules! int_impl {
#[inline]
pub const fn checked_sub_unsigned(self, rhs: $UnsignedT) -> Option<Self> {
let (a, b) = self.overflowing_sub_unsigned(rhs);
if unlikely!(b) {None} else {Some(a)}
if unlikely!(b) { None } else { Some(a) }
}
/// Strict subtraction with an unsigned integer. Computes `self - rhs`,
/// panicking if overflow occurred.
///
/// # Panics
///
/// ## Overflow behavior
///
/// This function will always panic on overflow, regardless of whether overflow checks are enabled.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(strict_overflow_ops)]
#[doc = concat!("assert_eq!(1", stringify!($SelfT), ".strict_sub_unsigned(2), -1);")]
/// ```
///
/// ```should_panic
/// #![feature(strict_overflow_ops)]
#[doc = concat!("let _ = (", stringify!($SelfT), "::MIN + 2).strict_sub_unsigned(3);")]
/// ```
#[unstable(feature = "strict_overflow_ops", issue = "118260")]
#[rustc_const_unstable(feature = "const_strict_overflow_ops", issue = "118260")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
#[track_caller]
pub const fn strict_sub_unsigned(self, rhs: $UnsignedT) -> Self {
let (a, b) = self.overflowing_sub_unsigned(rhs);
if unlikely!(b) { overflow_panic::sub() } else { a }
}
/// Checked integer multiplication. Computes `self * rhs`, returning `None` if
@ -587,7 +719,40 @@ macro_rules! int_impl {
#[inline]
pub const fn checked_mul(self, rhs: Self) -> Option<Self> {
let (a, b) = self.overflowing_mul(rhs);
if unlikely!(b) {None} else {Some(a)}
if unlikely!(b) { None } else { Some(a) }
}
/// Strict integer multiplication. Computes `self * rhs`, panicking if
/// overflow occurred.
///
/// # Panics
///
/// ## Overflow behavior
///
/// This function will always panic on overflow, regardless of whether overflow checks are enabled.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(strict_overflow_ops)]
#[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.strict_mul(1), ", stringify!($SelfT), "::MAX);")]
/// ```
///
/// ``` should_panic
/// #![feature(strict_overflow_ops)]
#[doc = concat!("let _ = ", stringify!($SelfT), "::MAX.strict_mul(2);")]
/// ```
#[unstable(feature = "strict_overflow_ops", issue = "118260")]
#[rustc_const_unstable(feature = "const_strict_overflow_ops", issue = "118260")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
#[track_caller]
pub const fn strict_mul(self, rhs: Self) -> Self {
let (a, b) = self.overflowing_mul(rhs);
if unlikely!(b) { overflow_panic::mul() } else { a }
}
/// Unchecked integer multiplication. Computes `self * rhs`, assuming overflow
@ -642,6 +807,50 @@ macro_rules! int_impl {
}
}
/// Strict integer division. Computes `self / rhs`, panicking
/// if overflow occurred.
///
/// # Panics
///
/// This function will panic if `rhs` is zero.
///
/// ## Overflow behavior
///
/// This function will always panic on overflow, regardless of whether overflow checks are enabled.
///
/// The only case where such an overflow can occur is when one divides `MIN / -1` on a signed type (where
/// `MIN` is the negative minimal value for the type); this is equivalent to `-MIN`, a positive value
/// that is too large to represent in the type.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(strict_overflow_ops)]
#[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 1).strict_div(-1), ", stringify!($Max), ");")]
/// ```
///
/// ```should_panic
/// #![feature(strict_overflow_ops)]
#[doc = concat!("let _ = ", stringify!($SelfT), "::MIN.strict_div(-1);")]
/// ```
///
/// ```should_panic
/// #![feature(strict_overflow_ops)]
#[doc = concat!("let _ = (1", stringify!($SelfT), ").strict_div(0);")]
/// ```
#[unstable(feature = "strict_overflow_ops", issue = "118260")]
#[rustc_const_unstable(feature = "const_strict_overflow_ops", issue = "118260")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
#[track_caller]
pub const fn strict_div(self, rhs: Self) -> Self {
let (a, b) = self.overflowing_div(rhs);
if unlikely!(b) { overflow_panic::div() } else { a }
}
/// Checked Euclidean division. Computes `self.div_euclid(rhs)`,
/// returning `None` if `rhs == 0` or the division results in overflow.
///
@ -668,6 +877,50 @@ macro_rules! int_impl {
}
}
/// Strict Euclidean division. Computes `self.div_euclid(rhs)`, panicking
/// if overflow occurred.
///
/// # Panics
///
/// This function will panic if `rhs` is zero.
///
/// ## Overflow behavior
///
/// This function will always panic on overflow, regardless of whether overflow checks are enabled.
///
/// The only case where such an overflow can occur is when one divides `MIN / -1` on a signed type (where
/// `MIN` is the negative minimal value for the type); this is equivalent to `-MIN`, a positive value
/// that is too large to represent in the type.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(strict_overflow_ops)]
#[doc = concat!("assert_eq!((", stringify!($SelfT), "::MIN + 1).strict_div_euclid(-1), ", stringify!($Max), ");")]
/// ```
///
/// ```should_panic
/// #![feature(strict_overflow_ops)]
#[doc = concat!("let _ = ", stringify!($SelfT), "::MIN.strict_div_euclid(-1);")]
/// ```
///
/// ```should_panic
/// #![feature(strict_overflow_ops)]
#[doc = concat!("let _ = (1", stringify!($SelfT), ").strict_div_euclid(0);")]
/// ```
#[unstable(feature = "strict_overflow_ops", issue = "118260")]
#[rustc_const_unstable(feature = "const_strict_overflow_ops", issue = "118260")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
#[track_caller]
pub const fn strict_div_euclid(self, rhs: Self) -> Self {
let (a, b) = self.overflowing_div_euclid(rhs);
if unlikely!(b) { overflow_panic::div() } else { a }
}
/// Checked integer remainder. Computes `self % rhs`, returning `None` if
/// `rhs == 0` or the division results in overflow.
///
@ -694,6 +947,49 @@ macro_rules! int_impl {
}
}
/// Strict integer remainder. Computes `self % rhs`, panicking if
/// the division results in overflow.
///
/// # Panics
///
/// This function will panic if `rhs` is zero.
///
/// ## Overflow behavior
///
/// This function will always panic on overflow, regardless of whether overflow checks are enabled.
///
/// The only case where such an overflow can occur is `x % y` for `MIN / -1` on a
/// signed type (where `MIN` is the negative minimal value), which is invalid due to implementation artifacts.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(strict_overflow_ops)]
#[doc = concat!("assert_eq!(5", stringify!($SelfT), ".strict_rem(2), 1);")]
/// ```
///
/// ```should_panic
/// #![feature(strict_overflow_ops)]
#[doc = concat!("let _ = 5", stringify!($SelfT), ".strict_rem(0);")]
/// ```
///
/// ```should_panic
/// #![feature(strict_overflow_ops)]
#[doc = concat!("let _ = ", stringify!($SelfT), "::MIN.strict_rem(-1);")]
/// ```
#[unstable(feature = "strict_overflow_ops", issue = "118260")]
#[rustc_const_unstable(feature = "const_strict_overflow_ops", issue = "118260")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
#[track_caller]
pub const fn strict_rem(self, rhs: Self) -> Self {
let (a, b) = self.overflowing_rem(rhs);
if unlikely!(b) { overflow_panic::rem() } else { a }
}
/// Checked Euclidean remainder. Computes `self.rem_euclid(rhs)`, returning `None`
/// if `rhs == 0` or the division results in overflow.
///
@ -720,6 +1016,49 @@ macro_rules! int_impl {
}
}
/// Strict Euclidean remainder. Computes `self.rem_euclid(rhs)`, panicking if
/// the division results in overflow.
///
/// # Panics
///
/// This function will panic if `rhs` is zero.
///
/// ## Overflow behavior
///
/// This function will always panic on overflow, regardless of whether overflow checks are enabled.
///
/// The only case where such an overflow can occur is `x % y` for `MIN / -1` on a
/// signed type (where `MIN` is the negative minimal value), which is invalid due to implementation artifacts.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(strict_overflow_ops)]
#[doc = concat!("assert_eq!(5", stringify!($SelfT), ".strict_rem_euclid(2), 1);")]
/// ```
///
/// ```should_panic
/// #![feature(strict_overflow_ops)]
#[doc = concat!("let _ = 5", stringify!($SelfT), ".strict_rem_euclid(0);")]
/// ```
///
/// ```should_panic
/// #![feature(strict_overflow_ops)]
#[doc = concat!("let _ = ", stringify!($SelfT), "::MIN.strict_rem_euclid(-1);")]
/// ```
#[unstable(feature = "strict_overflow_ops", issue = "118260")]
#[rustc_const_unstable(feature = "const_strict_overflow_ops", issue = "118260")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
#[track_caller]
pub const fn strict_rem_euclid(self, rhs: Self) -> Self {
let (a, b) = self.overflowing_rem_euclid(rhs);
if unlikely!(b) { overflow_panic::rem() } else { a }
}
/// Checked negation. Computes `-self`, returning `None` if `self == MIN`.
///
/// # Examples
@ -737,7 +1076,7 @@ macro_rules! int_impl {
#[inline]
pub const fn checked_neg(self) -> Option<Self> {
let (a, b) = self.overflowing_neg();
if unlikely!(b) {None} else {Some(a)}
if unlikely!(b) { None } else { Some(a) }
}
/// Unchecked negation. Computes `-self`, assuming overflow cannot occur.
@ -765,6 +1104,38 @@ macro_rules! int_impl {
unsafe { intrinsics::unchecked_sub(0, self) }
}
/// Strict negation. Computes `-self`, panicking if `self == MIN`.
///
/// # Panics
///
/// ## Overflow behavior
///
/// This function will always panic on overflow, regardless of whether overflow checks are enabled.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(strict_overflow_ops)]
#[doc = concat!("assert_eq!(5", stringify!($SelfT), ".strict_neg(), -5);")]
/// ```
///
/// ```should_panic
/// #![feature(strict_overflow_ops)]
#[doc = concat!("let _ = ", stringify!($SelfT), "::MIN.strict_neg();")]
///
#[unstable(feature = "strict_overflow_ops", issue = "118260")]
#[rustc_const_unstable(feature = "const_strict_overflow_ops", issue = "118260")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
#[track_caller]
pub const fn strict_neg(self) -> Self {
let (a, b) = self.overflowing_neg();
if unlikely!(b) { overflow_panic::neg() } else { a }
}
/// Checked shift left. Computes `self << rhs`, returning `None` if `rhs` is larger
/// than or equal to the number of bits in `self`.
///
@ -783,7 +1154,40 @@ macro_rules! int_impl {
#[inline]
pub const fn checked_shl(self, rhs: u32) -> Option<Self> {
let (a, b) = self.overflowing_shl(rhs);
if unlikely!(b) {None} else {Some(a)}
if unlikely!(b) { None } else { Some(a) }
}
/// Strict shift left. Computes `self << rhs`, panicking if `rhs` is larger
/// than or equal to the number of bits in `self`.
///
/// # Panics
///
/// ## Overflow behavior
///
/// This function will always panic on overflow, regardless of whether overflow checks are enabled.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(strict_overflow_ops)]
#[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".strict_shl(4), 0x10);")]
/// ```
///
/// ```should_panic
/// #![feature(strict_overflow_ops)]
#[doc = concat!("let _ = 0x1", stringify!($SelfT), ".strict_shl(129);")]
/// ```
#[unstable(feature = "strict_overflow_ops", issue = "118260")]
#[rustc_const_unstable(feature = "const_strict_overflow_ops", issue = "118260")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
#[track_caller]
pub const fn strict_shl(self, rhs: u32) -> Self {
let (a, b) = self.overflowing_shl(rhs);
if unlikely!(b) { overflow_panic::shl() } else { a }
}
/// Unchecked shift left. Computes `self << rhs`, assuming that
@ -831,7 +1235,40 @@ macro_rules! int_impl {
#[inline]
pub const fn checked_shr(self, rhs: u32) -> Option<Self> {
let (a, b) = self.overflowing_shr(rhs);
if unlikely!(b) {None} else {Some(a)}
if unlikely!(b) { None } else { Some(a) }
}
/// Strict shift right. Computes `self >> rhs`, panicking `rhs` is
/// larger than or equal to the number of bits in `self`.
///
/// # Panics
///
/// ## Overflow behavior
///
/// This function will always panic on overflow, regardless of whether overflow checks are enabled.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(strict_overflow_ops)]
#[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".strict_shr(4), 0x1);")]
/// ```
///
/// ```should_panic
/// #![feature(strict_overflow_ops)]
#[doc = concat!("let _ = 0x10", stringify!($SelfT), ".strict_shr(128);")]
/// ```
#[unstable(feature = "strict_overflow_ops", issue = "118260")]
#[rustc_const_unstable(feature = "const_strict_overflow_ops", issue = "118260")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
#[track_caller]
pub const fn strict_shr(self, rhs: u32) -> Self {
let (a, b) = self.overflowing_shr(rhs);
if unlikely!(b) { overflow_panic::shr() } else { a }
}
/// Unchecked shift right. Computes `self >> rhs`, assuming that
@ -885,6 +1322,42 @@ macro_rules! int_impl {
}
}
/// Strict absolute value. Computes `self.abs()`, panicking if
/// `self == MIN`.
///
/// # Panics
///
/// ## Overflow behavior
///
/// This function will always panic on overflow, regardless of whether overflow checks are enabled.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(strict_overflow_ops)]
#[doc = concat!("assert_eq!((-5", stringify!($SelfT), ").strict_abs(), 5);")]
/// ```
///
/// ```should_panic
/// #![feature(strict_overflow_ops)]
#[doc = concat!("let _ = ", stringify!($SelfT), "::MIN.strict_abs();")]
/// ```
#[unstable(feature = "strict_overflow_ops", issue = "118260")]
#[rustc_const_unstable(feature = "const_strict_overflow_ops", issue = "118260")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
#[track_caller]
pub const fn strict_abs(self) -> Self {
if self.is_negative() {
self.strict_neg()
} else {
self
}
}
/// Checked exponentiation. Computes `self.pow(exp)`, returning `None` if
/// overflow occurred.
///
@ -923,6 +1396,55 @@ macro_rules! int_impl {
acc.checked_mul(base)
}
/// Strict exponentiation. Computes `self.pow(exp)`, panicking if
/// overflow occurred.
///
/// # Panics
///
/// ## Overflow behavior
///
/// This function will always panic on overflow, regardless of whether overflow checks are enabled.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(strict_overflow_ops)]
#[doc = concat!("assert_eq!(8", stringify!($SelfT), ".strict_pow(2), 64);")]
/// ```
///
/// ```should_panic
/// #![feature(strict_overflow_ops)]
#[doc = concat!("let _ = ", stringify!($SelfT), "::MAX.strict_pow(2);")]
/// ```
#[unstable(feature = "strict_overflow_ops", issue = "118260")]
#[rustc_const_unstable(feature = "const_strict_overflow_ops", issue = "118260")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
#[track_caller]
pub const fn strict_pow(self, mut exp: u32) -> Self {
if exp == 0 {
return 1;
}
let mut base = self;
let mut acc: Self = 1;
while exp > 1 {
if (exp & 1) == 1 {
acc = acc.strict_mul(base);
}
exp /= 2;
base = base.strict_mul(base);
}
// since exp!=0, finally the exp must be 1.
// Deal with the final bit of the exponent separately, since
// squaring the base afterwards is not necessary and may cause a
// needless overflow.
acc.strict_mul(base)
}
/// Returns the square root of the number, rounded down.
///
/// Returns `None` if `self` is negative.

View File

@ -44,6 +44,7 @@ mod uint_macros; // import uint_impl!
mod error;
mod int_log10;
mod nonzero;
mod overflow_panic;
mod saturating;
mod wrapping;

View File

@ -0,0 +1,51 @@
//! Functions for panicking on overflow.
//!
//! In particular, these are used by the `strict_` methods on integers.
#[cold]
#[track_caller]
pub const fn add() -> ! {
panic!("attempt to add with overflow")
}
#[cold]
#[track_caller]
pub const fn sub() -> ! {
panic!("attempt to subtract with overflow")
}
#[cold]
#[track_caller]
pub const fn mul() -> ! {
panic!("attempt to multiply with overflow")
}
#[cold]
#[track_caller]
pub const fn div() -> ! {
panic!("attempt to divide with overflow")
}
#[cold]
#[track_caller]
pub const fn rem() -> ! {
panic!("attempt to calculate the remainder with overflow")
}
#[cold]
#[track_caller]
pub const fn neg() -> ! {
panic!("attempt to negate with overflow")
}
#[cold]
#[track_caller]
pub const fn shr() -> ! {
panic!("attempt to shift right with overflow")
}
#[cold]
#[track_caller]
pub const fn shl() -> ! {
panic!("attempt to shift left with overflow")
}

View File

@ -459,9 +459,42 @@ macro_rules! uint_impl {
#[inline]
pub const fn checked_add(self, rhs: Self) -> Option<Self> {
let (a, b) = self.overflowing_add(rhs);
if unlikely!(b) {None} else {Some(a)}
if unlikely!(b) { None } else { Some(a) }
}
/// Strict integer addition. Computes `self + rhs`, panicking
/// if overflow occurred.
///
/// # Panics
///
/// ## Overflow behavior
///
/// This function will always panic on overflow, regardless of whether overflow checks are enabled.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(strict_overflow_ops)]
#[doc = concat!("assert_eq!((", stringify!($SelfT), "::MAX - 2).strict_add(1), ", stringify!($SelfT), "::MAX - 1);")]
/// ```
///
/// ```should_panic
/// #![feature(strict_overflow_ops)]
#[doc = concat!("let _ = (", stringify!($SelfT), "::MAX - 2).strict_add(3);")]
/// ```
#[unstable(feature = "strict_overflow_ops", issue = "118260")]
#[rustc_const_unstable(feature = "const_strict_overflow_ops", issue = "118260")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
#[track_caller]
pub const fn strict_add(self, rhs: Self) -> Self {
let (a, b) = self.overflowing_add(rhs);
if unlikely!(b) { overflow_panic ::add()} else {a}
}
/// Unchecked integer addition. Computes `self + rhs`, assuming overflow
/// cannot occur.
///
@ -507,9 +540,47 @@ macro_rules! uint_impl {
#[inline]
pub const fn checked_add_signed(self, rhs: $SignedT) -> Option<Self> {
let (a, b) = self.overflowing_add_signed(rhs);
if unlikely!(b) {None} else {Some(a)}
if unlikely!(b) { None } else { Some(a) }
}
/// Strict addition with a signed integer. Computes `self + rhs`,
/// panicking if overflow occurred.
///
/// # Panics
///
/// ## Overflow behavior
///
/// This function will always panic on overflow, regardless of whether overflow checks are enabled.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(strict_overflow_ops)]
#[doc = concat!("assert_eq!(1", stringify!($SelfT), ".strict_add_signed(2), 3);")]
/// ```
///
/// ```should_panic
/// #![feature(strict_overflow_ops)]
#[doc = concat!("let _ = 1", stringify!($SelfT), ".strict_add_signed(-2);")]
/// ```
///
/// ```should_panic
/// #![feature(strict_overflow_ops)]
#[doc = concat!("let _ = (", stringify!($SelfT), "::MAX - 2).strict_add_signed(3);")]
/// ```
#[unstable(feature = "strict_overflow_ops", issue = "118260")]
#[rustc_const_unstable(feature = "const_strict_overflow_ops", issue = "118260")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
#[track_caller]
pub const fn strict_add_signed(self, rhs: $SignedT) -> Self {
let (a, b) = self.overflowing_add_signed(rhs);
if unlikely!(b) { overflow_panic ::add()} else {a}
}
/// Checked integer subtraction. Computes `self - rhs`, returning
/// `None` if overflow occurred.
///
@ -528,9 +599,42 @@ macro_rules! uint_impl {
#[inline]
pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
let (a, b) = self.overflowing_sub(rhs);
if unlikely!(b) {None} else {Some(a)}
if unlikely!(b) { None } else { Some(a) }
}
/// Strict integer subtraction. Computes `self - rhs`, panicking if
/// overflow occurred.
///
/// # Panics
///
/// ## Overflow behavior
///
/// This function will always panic on overflow, regardless of whether overflow checks are enabled.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(strict_overflow_ops)]
#[doc = concat!("assert_eq!(1", stringify!($SelfT), ".strict_sub(1), 0);")]
/// ```
///
/// ```should_panic
/// #![feature(strict_overflow_ops)]
#[doc = concat!("let _ = 0", stringify!($SelfT), ".strict_sub(1);")]
/// ```
#[unstable(feature = "strict_overflow_ops", issue = "118260")]
#[rustc_const_unstable(feature = "const_strict_overflow_ops", issue = "118260")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
#[track_caller]
pub const fn strict_sub(self, rhs: Self) -> Self {
let (a, b) = self.overflowing_sub(rhs);
if unlikely!(b) { overflow_panic ::sub()} else {a}
}
/// Unchecked integer subtraction. Computes `self - rhs`, assuming overflow
/// cannot occur.
///
@ -575,9 +679,42 @@ macro_rules! uint_impl {
#[inline]
pub const fn checked_mul(self, rhs: Self) -> Option<Self> {
let (a, b) = self.overflowing_mul(rhs);
if unlikely!(b) {None} else {Some(a)}
if unlikely!(b) { None } else { Some(a) }
}
/// Strict integer multiplication. Computes `self * rhs`, panicking if
/// overflow occurred.
///
/// # Panics
///
/// ## Overflow behavior
///
/// This function will always panic on overflow, regardless of whether overflow checks are enabled.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(strict_overflow_ops)]
#[doc = concat!("assert_eq!(5", stringify!($SelfT), ".strict_mul(1), 5);")]
/// ```
///
/// ``` should_panic
/// #![feature(strict_overflow_ops)]
#[doc = concat!("let _ = ", stringify!($SelfT), "::MAX.strict_mul(2);")]
/// ```
#[unstable(feature = "strict_overflow_ops", issue = "118260")]
#[rustc_const_unstable(feature = "const_strict_overflow_ops", issue = "118260")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
#[track_caller]
pub const fn strict_mul(self, rhs: Self) -> Self {
let (a, b) = self.overflowing_mul(rhs);
if unlikely!(b) { overflow_panic ::mul()} else {a}
}
/// Unchecked integer multiplication. Computes `self * rhs`, assuming overflow
/// cannot occur.
///
@ -630,6 +767,34 @@ macro_rules! uint_impl {
}
}
/// Strict integer division. Computes `self / rhs`.
/// Strict division on unsigned types is just normal division.
/// There's no way overflow could ever happen.
/// This function exists, so that all operations
/// are accounted for in the strict operations.
///
/// # Panics
///
/// This function will panic if `rhs` is zero.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(strict_overflow_ops)]
#[doc = concat!("assert_eq!(100", stringify!($SelfT), ".strict_div(10), 10);")]
/// ```
#[unstable(feature = "strict_overflow_ops", issue = "118260")]
#[rustc_const_unstable(feature = "const_strict_overflow_ops", issue = "118260")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline(always)]
#[track_caller]
pub const fn strict_div(self, rhs: Self) -> Self {
self / rhs
}
/// Checked Euclidean division. Computes `self.div_euclid(rhs)`, returning `None`
/// if `rhs == 0`.
///
@ -654,6 +819,36 @@ macro_rules! uint_impl {
}
}
/// Strict Euclidean division. Computes `self.div_euclid(rhs)`.
/// Strict division on unsigned types is just normal division.
/// There's no way overflow could ever happen.
/// This function exists, so that all operations
/// are accounted for in the strict operations.
/// Since, for the positive integers, all common
/// definitions of division are equal, this
/// is exactly equal to `self.strict_div(rhs)`.
///
/// # Panics
///
/// This function will panic if `rhs` is zero.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(strict_overflow_ops)]
#[doc = concat!("assert_eq!(100", stringify!($SelfT), ".strict_div_euclid(10), 10);")]
/// ```
#[unstable(feature = "strict_overflow_ops", issue = "118260")]
#[rustc_const_unstable(feature = "const_strict_overflow_ops", issue = "118260")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline(always)]
#[track_caller]
pub const fn strict_div_euclid(self, rhs: Self) -> Self {
self / rhs
}
/// Checked integer remainder. Computes `self % rhs`, returning `None`
/// if `rhs == 0`.
@ -681,6 +876,35 @@ macro_rules! uint_impl {
}
}
/// Strict integer remainder. Computes `self % rhs`.
/// Strict remainder calculation on unsigned types is
/// just the regular remainder calculation.
/// There's no way overflow could ever happen.
/// This function exists, so that all operations
/// are accounted for in the strict operations.
///
/// # Panics
///
/// This function will panic if `rhs` is zero.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(strict_overflow_ops)]
#[doc = concat!("assert_eq!(100", stringify!($SelfT), ".strict_rem(10), 0);")]
/// ```
#[unstable(feature = "strict_overflow_ops", issue = "118260")]
#[rustc_const_unstable(feature = "const_strict_overflow_ops", issue = "118260")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline(always)]
#[track_caller]
pub const fn strict_rem(self, rhs: Self) -> Self {
self % rhs
}
/// Checked Euclidean modulo. Computes `self.rem_euclid(rhs)`, returning `None`
/// if `rhs == 0`.
///
@ -705,6 +929,38 @@ macro_rules! uint_impl {
}
}
/// Strict Euclidean modulo. Computes `self.rem_euclid(rhs)`.
/// Strict modulo calculation on unsigned types is
/// just the regular remainder calculation.
/// There's no way overflow could ever happen.
/// This function exists, so that all operations
/// are accounted for in the strict operations.
/// Since, for the positive integers, all common
/// definitions of division are equal, this
/// is exactly equal to `self.strict_rem(rhs)`.
///
/// # Panics
///
/// This function will panic if `rhs` is zero.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(strict_overflow_ops)]
#[doc = concat!("assert_eq!(100", stringify!($SelfT), ".strict_rem_euclid(10), 0);")]
/// ```
#[unstable(feature = "strict_overflow_ops", issue = "118260")]
#[rustc_const_unstable(feature = "const_strict_overflow_ops", issue = "118260")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline(always)]
#[track_caller]
pub const fn strict_rem_euclid(self, rhs: Self) -> Self {
self % rhs
}
/// Returns the logarithm of the number with respect to an arbitrary base,
/// rounded down.
///
@ -893,7 +1149,42 @@ macro_rules! uint_impl {
#[inline]
pub const fn checked_neg(self) -> Option<Self> {
let (a, b) = self.overflowing_neg();
if unlikely!(b) {None} else {Some(a)}
if unlikely!(b) { None } else { Some(a) }
}
/// Strict negation. Computes `-self`, panicking unless `self ==
/// 0`.
///
/// Note that negating any positive integer will overflow.
///
/// # Panics
///
/// ## Overflow behavior
///
/// This function will always panic on overflow, regardless of whether overflow checks are enabled.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(strict_overflow_ops)]
#[doc = concat!("assert_eq!(0", stringify!($SelfT), ".strict_neg(), 0);")]
/// ```
///
/// ```should_panic
/// #![feature(strict_overflow_ops)]
#[doc = concat!("let _ = 1", stringify!($SelfT), ".strict_neg();")]
///
#[unstable(feature = "strict_overflow_ops", issue = "118260")]
#[rustc_const_unstable(feature = "const_strict_overflow_ops", issue = "118260")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
#[track_caller]
pub const fn strict_neg(self) -> Self {
let (a, b) = self.overflowing_neg();
if unlikely!(b) { overflow_panic::neg() } else { a }
}
/// Checked shift left. Computes `self << rhs`, returning `None`
@ -914,7 +1205,40 @@ macro_rules! uint_impl {
#[inline]
pub const fn checked_shl(self, rhs: u32) -> Option<Self> {
let (a, b) = self.overflowing_shl(rhs);
if unlikely!(b) {None} else {Some(a)}
if unlikely!(b) { None } else { Some(a) }
}
/// Strict shift left. Computes `self << rhs`, panicking if `rhs` is larger
/// than or equal to the number of bits in `self`.
///
/// # Panics
///
/// ## Overflow behavior
///
/// This function will always panic on overflow, regardless of whether overflow checks are enabled.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(strict_overflow_ops)]
#[doc = concat!("assert_eq!(0x1", stringify!($SelfT), ".strict_shl(4), 0x10);")]
/// ```
///
/// ```should_panic
/// #![feature(strict_overflow_ops)]
#[doc = concat!("let _ = 0x10", stringify!($SelfT), ".strict_shl(129);")]
/// ```
#[unstable(feature = "strict_overflow_ops", issue = "118260")]
#[rustc_const_unstable(feature = "const_strict_overflow_ops", issue = "118260")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
#[track_caller]
pub const fn strict_shl(self, rhs: u32) -> Self {
let (a, b) = self.overflowing_shl(rhs);
if unlikely!(b) { overflow_panic::shl() } else { a }
}
/// Unchecked shift left. Computes `self << rhs`, assuming that
@ -962,7 +1286,40 @@ macro_rules! uint_impl {
#[inline]
pub const fn checked_shr(self, rhs: u32) -> Option<Self> {
let (a, b) = self.overflowing_shr(rhs);
if unlikely!(b) {None} else {Some(a)}
if unlikely!(b) { None } else { Some(a) }
}
/// Strict shift right. Computes `self >> rhs`, panicking `rhs` is
/// larger than or equal to the number of bits in `self`.
///
/// # Panics
///
/// ## Overflow behavior
///
/// This function will always panic on overflow, regardless of whether overflow checks are enabled.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(strict_overflow_ops)]
#[doc = concat!("assert_eq!(0x10", stringify!($SelfT), ".strict_shr(4), 0x1);")]
/// ```
///
/// ```should_panic
/// #![feature(strict_overflow_ops)]
#[doc = concat!("let _ = 0x10", stringify!($SelfT), ".strict_shr(129);")]
/// ```
#[unstable(feature = "strict_overflow_ops", issue = "118260")]
#[rustc_const_unstable(feature = "const_strict_overflow_ops", issue = "118260")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
#[track_caller]
pub const fn strict_shr(self, rhs: u32) -> Self {
let (a, b) = self.overflowing_shr(rhs);
if unlikely!(b) { overflow_panic::shr() } else { a }
}
/// Unchecked shift right. Computes `self >> rhs`, assuming that
@ -1031,6 +1388,55 @@ macro_rules! uint_impl {
acc.checked_mul(base)
}
/// Strict exponentiation. Computes `self.pow(exp)`, panicking if
/// overflow occurred.
///
/// # Panics
///
/// ## Overflow behavior
///
/// This function will always panic on overflow, regardless of whether overflow checks are enabled.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// #![feature(strict_overflow_ops)]
#[doc = concat!("assert_eq!(2", stringify!($SelfT), ".strict_pow(5), 32);")]
/// ```
///
/// ```should_panic
/// #![feature(strict_overflow_ops)]
#[doc = concat!("let _ = ", stringify!($SelfT), "::MAX.strict_pow(2);")]
/// ```
#[unstable(feature = "strict_overflow_ops", issue = "118260")]
#[rustc_const_unstable(feature = "const_strict_overflow_ops", issue = "118260")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
#[track_caller]
pub const fn strict_pow(self, mut exp: u32) -> Self {
if exp == 0 {
return 1;
}
let mut base = self;
let mut acc: Self = 1;
while exp > 1 {
if (exp & 1) == 1 {
acc = acc.strict_mul(base);
}
exp /= 2;
base = base.strict_mul(base);
}
// since exp!=0, finally the exp must be 1.
// Deal with the final bit of the exponent separately, since
// squaring the base afterwards is not necessary and may cause a
// needless overflow.
acc.strict_mul(base)
}
/// Saturating integer addition. Computes `self + rhs`, saturating at
/// the numeric bounds instead of overflowing.
///