Rollup merge of #154664 - okaneco:integer_cast_extras, r=scottmcm

core/num: Implement feature `integer_cast_extras`

Tracking issue https://github.com/rust-lang/rust/issues/154650
Accepted ACP https://github.com/rust-lang/libs-team/issues/765#issuecomment-4164285847

Implement `saturating`, `checked`, and `strict` casting between signed and unsigned integer primitives of the same bit-width.

Add `cast_integer` panic function to `overflow_panic.rs`
This commit is contained in:
Jonathan Brouwer
2026-04-20 08:14:11 +02:00
committed by GitHub
3 changed files with 186 additions and 0 deletions
@@ -49,3 +49,9 @@ pub(in crate::num) const fn shr() -> ! {
pub(in crate::num) const fn shl() -> ! {
panic!("attempt to shift left with overflow")
}
#[cold]
#[track_caller]
pub(in crate::num) const fn cast_integer() -> ! {
panic!("attempt to cast integer with overflow")
}
+88
View File
@@ -268,6 +268,94 @@ pub const fn cast_unsigned(self) -> $UnsignedT {
self as $UnsignedT
}
/// Saturating conversion of `self` to an unsigned integer of the same size.
///
/// Negative values are clamped to `0`.
///
/// For other kinds of unsigned integer casts, see
/// [`cast_unsigned`](Self::cast_unsigned),
/// [`checked_cast_unsigned`](Self::checked_cast_unsigned),
/// or [`strict_cast_unsigned`](Self::strict_cast_unsigned).
///
/// # Examples
///
/// ```
/// #![feature(integer_cast_extras)]
#[doc = concat!("let n = ", stringify!($SelfT), "::MIN;")]
///
#[doc = concat!("assert_eq!(n.saturating_cast_unsigned(), 0", stringify!($UnsignedT), ");")]
#[doc = concat!("assert_eq!(64", stringify!($SelfT), ".saturating_cast_unsigned(), 64", stringify!($UnsignedT), ");")]
/// ```
#[rustc_const_unstable(feature = "integer_cast_extras", issue = "154650")]
#[unstable(feature = "integer_cast_extras", issue = "154650")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline(always)]
pub const fn saturating_cast_unsigned(self) -> $UnsignedT {
if self >= 0 {
self.cast_unsigned()
} else {
0
}
}
/// Checked conversion of `self` to an unsigned integer of the same size,
/// returning `None` if `self` is negative.
///
/// For other kinds of unsigned integer casts, see
/// [`cast_unsigned`](Self::cast_unsigned),
/// [`saturating_cast_unsigned`](Self::saturating_cast_unsigned),
/// or [`strict_cast_unsigned`](Self::strict_cast_unsigned).
///
/// # Examples
///
/// ```
/// #![feature(integer_cast_extras)]
#[doc = concat!("let n = ", stringify!($SelfT), "::MIN;")]
///
#[doc = concat!("assert_eq!(n.checked_cast_unsigned(), None);")]
#[doc = concat!("assert_eq!(64", stringify!($SelfT), ".checked_cast_unsigned(), Some(64", stringify!($UnsignedT), "));")]
/// ```
#[rustc_const_unstable(feature = "integer_cast_extras", issue = "154650")]
#[unstable(feature = "integer_cast_extras", issue = "154650")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline(always)]
pub const fn checked_cast_unsigned(self) -> Option<$UnsignedT> {
if self >= 0 {
Some(self.cast_unsigned())
} else {
None
}
}
/// Strict conversion of `self` to an unsigned integer of the same size,
/// which panics if `self` is negative.
///
/// For other kinds of unsigned integer casts, see
/// [`cast_unsigned`](Self::cast_unsigned),
/// [`checked_cast_unsigned`](Self::checked_cast_unsigned),
/// or [`saturating_cast_unsigned`](Self::saturating_cast_unsigned).
///
/// # Examples
///
/// ```should_panic
/// #![feature(integer_cast_extras)]
#[doc = concat!("let _ = ", stringify!($SelfT), "::MIN.strict_cast_unsigned();")]
/// ```
#[rustc_const_unstable(feature = "integer_cast_extras", issue = "154650")]
#[unstable(feature = "integer_cast_extras", issue = "154650")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
#[track_caller]
pub const fn strict_cast_unsigned(self) -> $UnsignedT {
match self.checked_cast_unsigned() {
Some(n) => n,
None => imp::overflow_panic::cast_integer(),
}
}
/// Shifts the bits to the left by a specified amount, `n`,
/// wrapping the truncated bits to the end of the resulting integer.
///
+92
View File
@@ -353,6 +353,98 @@ pub const fn cast_signed(self) -> $SignedT {
self as $SignedT
}
/// Saturating conversion of `self` to a signed integer of the same size.
///
/// The signed integer's maximum value is returned if `self` is larger
/// than the maximum positive value representable by the signed integer.
///
/// For other kinds of signed integer casts, see
/// [`cast_signed`](Self::cast_signed),
/// [`checked_cast_signed`](Self::checked_cast_signed),
/// or [`strict_cast_signed`](Self::strict_cast_signed).
///
/// # Examples
///
/// ```
/// #![feature(integer_cast_extras)]
#[doc = concat!("let n = ", stringify!($SelfT), "::MAX;")]
///
#[doc = concat!("assert_eq!(n.saturating_cast_signed(), ", stringify!($SignedT), "::MAX);")]
#[doc = concat!("assert_eq!(64", stringify!($SelfT), ".saturating_cast_signed(), 64", stringify!($SignedT), ");")]
/// ```
#[rustc_const_unstable(feature = "integer_cast_extras", issue = "154650")]
#[unstable(feature = "integer_cast_extras", issue = "154650")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline(always)]
pub const fn saturating_cast_signed(self) -> $SignedT {
// Clamp to the signed integer max size, which is ActualT::MAX >> 1.
if self <= <$SignedT>::MAX.cast_unsigned() {
self.cast_signed()
} else {
<$SignedT>::MAX
}
}
/// Checked conversion of `self` to a signed integer of the same size,
/// returning `None` if `self` is larger than the signed integer's
/// maximum value.
///
/// For other kinds of signed integer casts, see
/// [`cast_signed`](Self::cast_signed),
/// [`saturating_cast_signed`](Self::saturating_cast_signed),
/// or [`strict_cast_signed`](Self::strict_cast_signed).
///
/// # Examples
///
/// ```
/// #![feature(integer_cast_extras)]
#[doc = concat!("let n = ", stringify!($SelfT), "::MAX;")]
///
#[doc = concat!("assert_eq!(n.checked_cast_signed(), None);")]
#[doc = concat!("assert_eq!(64", stringify!($SelfT), ".checked_cast_signed(), Some(64", stringify!($SignedT), "));")]
/// ```
#[rustc_const_unstable(feature = "integer_cast_extras", issue = "154650")]
#[unstable(feature = "integer_cast_extras", issue = "154650")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline(always)]
pub const fn checked_cast_signed(self) -> Option<$SignedT> {
if self <= <$SignedT>::MAX.cast_unsigned() {
Some(self.cast_signed())
} else {
None
}
}
/// Strict conversion of `self` to a signed integer of the same size,
/// which panics if `self` is larger than the signed integer's maximum
/// value.
///
/// For other kinds of signed integer casts, see
/// [`cast_signed`](Self::cast_signed),
/// [`checked_cast_signed`](Self::checked_cast_signed),
/// or [`saturating_cast_signed`](Self::saturating_cast_signed).
///
/// # Examples
///
/// ```should_panic
/// #![feature(integer_cast_extras)]
#[doc = concat!("let _ = ", stringify!($SelfT), "::MAX.strict_cast_signed();")]
/// ```
#[rustc_const_unstable(feature = "integer_cast_extras", issue = "154650")]
#[unstable(feature = "integer_cast_extras", issue = "154650")]
#[must_use = "this returns the result of the operation, \
without modifying the original"]
#[inline]
#[track_caller]
pub const fn strict_cast_signed(self) -> $SignedT {
match self.checked_cast_signed() {
Some(n) => n,
None => imp::overflow_panic::cast_integer(),
}
}
/// Shifts the bits to the left by a specified amount, `n`,
/// wrapping the truncated bits to the end of the resulting integer.
///