diff --git a/library/core/src/num/imp/overflow_panic.rs b/library/core/src/num/imp/overflow_panic.rs index a9b91daba954..2b699fa61f87 100644 --- a/library/core/src/num/imp/overflow_panic.rs +++ b/library/core/src/num/imp/overflow_panic.rs @@ -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") +} diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 1b1489261f62..d68a9493e9f3 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -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. /// diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index 5be1b837798f..c0ec4c87cfe0 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -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. ///