diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 4fbd3c6dc2142..eaea2d39eaca8 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -96,11 +96,13 @@ #![feature(core_intrinsics)] #![feature(coverage_attribute)] #![feature(disjoint_bitor)] +#![feature(funnel_shifts)] #![feature(io_const_error)] #![feature(offset_of_enum)] #![feature(panic_internals)] #![feature(pattern_type_macro)] #![feature(ub_checks)] +#![feature(wrapping_funnel_shifts)] // tidy-alphabetical-end // // Language features: @@ -131,7 +133,6 @@ #![feature(final_associated_functions)] #![feature(freeze_impls)] #![feature(fundamental)] -#![feature(funnel_shifts)] #![feature(impl_restriction)] #![feature(intra_doc_pointers)] #![feature(intrinsics)] diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index a98ef99b9d7bb..d8281944cb3db 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -568,11 +568,8 @@ macro_rules! uint_impl { if intrinsics::overflow_checks() { assert!(n < Self::BITS, "attempt to funnel shift left with overflow"); } - // SAFETY: `n` is wrapped to within range - unsafe { - let n = n & (Self::BITS - 1); - self.unchecked_funnel_shl(right, n) - } + + self.wrapping_funnel_shl(right, n) } /// Performs a right funnel shift. @@ -639,10 +636,141 @@ macro_rules! uint_impl { if intrinsics::overflow_checks() { assert!(n < Self::BITS, "attempt to funnel shift right with overflow"); } + + self.wrapping_funnel_shr(right, n) + } + + /// Performs a left funnel shift. + /// + /// This function will return `None` if `n` is greater than or equal to the number of + /// bits in `self`, i.e., when [`funnel_shl`](Self::funnel_shl) might panic or wrap. + #[rustc_const_unstable(feature = "wrapping_funnel_shifts", issue = "161798")] + #[unstable(feature = "wrapping_funnel_shifts", issue = "161798")] + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline(always)] + pub const fn checked_funnel_shl(self, right: Self, n: u32) -> Option { + if n < Self::BITS { + // SAFETY: just checked that `n` is in-range + Some(unsafe { self.unchecked_funnel_shl(right, n) }) + } else { + None + } + } + + /// Performs a right funnel shift. + /// + /// This function will return `None` if `n` is greater than or equal to the number of + /// bits in `self`, i.e., when [`funnel_shr`](Self::funnel_shr) might panic or wrap. + #[rustc_const_unstable(feature = "wrapping_funnel_shifts", issue = "161798")] + #[unstable(feature = "wrapping_funnel_shifts", issue = "161798")] + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline(always)] + pub const fn checked_funnel_shr(self, right: Self, n: u32) -> Option { + if n < Self::BITS { + // SAFETY: just checked that `n` is in-range + Some(unsafe { self.unchecked_funnel_shr(right, n) }) + } else { + None + } + } + + /// Performs a left funnel shift. + /// + /// This function shifts by `mask(n)`, where `mask` removes any high-order bits of `n` + /// that would cause the shift to exceed the bitwidth of the type. As a result, this + /// function never panics, unlike [`funnel_shl`](Self::funnel_shl). + #[rustc_const_unstable(feature = "wrapping_funnel_shifts", issue = "161798")] + #[unstable(feature = "wrapping_funnel_shifts", issue = "161798")] + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline(always)] + pub const fn wrapping_funnel_shl(self, right: Self, n: u32) -> Self { + let n = n & (Self::BITS - 1); // SAFETY: `n` is wrapped to within range - unsafe { - let n = n & (Self::BITS - 1); - self.unchecked_funnel_shr(right, n) + unsafe { self.unchecked_funnel_shl(right, n) } + } + + /// Performs a right funnel shift. + /// + /// This function shifts by `mask(n)`, where `mask` removes any high-order bits of `n` + /// that would cause the shift to exceed the bitwidth of the type. As a result, this + /// function never panics, unlike [`funnel_shr`](Self::funnel_shr). + #[rustc_const_unstable(feature = "wrapping_funnel_shifts", issue = "161798")] + #[unstable(feature = "wrapping_funnel_shifts", issue = "161798")] + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline(always)] + pub const fn wrapping_funnel_shr(self, right: Self, n: u32) -> Self { + let n = n & (Self::BITS - 1); + // SAFETY: `n` is wrapped to within range + unsafe { self.unchecked_funnel_shr(right, n) } + } + + /// Performs a left funnel shift. + /// + /// # Panics + /// + /// This function will always panic if `n` is greater than or equal to the number of + /// bits in `self`, i.e., when [`funnel_shr`](Self::funnel_shr) might panic or wrap. + #[rustc_const_unstable(feature = "wrapping_funnel_shifts", issue = "161798")] + #[unstable(feature = "wrapping_funnel_shifts", issue = "161798")] + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline(always)] + pub const fn strict_funnel_shl(self, right: Self, n: u32) -> Self { + assert!(n < Self::BITS, "attempt to funnel shift left with overflow"); + // SAFETY: `n` is checked to be within range + unsafe { self.unchecked_funnel_shl(right, n) } + } + + /// Performs a right funnel shift. + /// + /// # Panics + /// + /// This function will always panic if `n` is greater than or equal to the number of + /// bits in `self`, i.e., when [`funnel_shr`](Self::funnel_shr) might panic or wrap. + #[rustc_const_unstable(feature = "wrapping_funnel_shifts", issue = "161798")] + #[unstable(feature = "wrapping_funnel_shifts", issue = "161798")] + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline(always)] + pub const fn strict_funnel_shr(self, right: Self, n: u32) -> Self { + assert!(n < Self::BITS, "attempt to funnel shift right with overflow"); + // SAFETY: `n` is checked to be within range + unsafe { self.unchecked_funnel_shr(right, n) } + } + + /// Performs a left funnel shift. + /// + /// This function will act like [`unbounded_shl`](Self::unbounded_shl) on a type with + /// twice the bitwidth of `Self` where the high-order half comes from `self` and the + /// low-order half comes from `right`, regardless of whether such a type exists, and + /// will return the high-order half of the result. + #[rustc_const_unstable(feature = "wrapping_funnel_shifts", issue = "161798")] + #[unstable(feature = "wrapping_funnel_shifts", issue = "161798")] + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline(always)] + pub const fn unbounded_funnel_shl(self, right: Self, n: u32) -> Self { + if let Some(r) = self.checked_funnel_shl(right, n) { + r + } else { + // This subtraction will never underflow. + right.unbounded_shl(n - Self::BITS) + } + } + + /// Performs a right funnel shift. + /// + /// This function will act like [`unbounded_shr`](Self::unbounded_shr) on a type with + /// twice the bitwidth of `Self` where the high-order half comes from `self` and the + /// low-order half comes from `right`, regardless of whether such a type exists, and + /// will return the low-order half of the result. + #[rustc_const_unstable(feature = "wrapping_funnel_shifts", issue = "161798")] + #[unstable(feature = "wrapping_funnel_shifts", issue = "161798")] + #[must_use = "this returns the result of the operation, without modifying the original"] + #[inline(always)] + pub const fn unbounded_funnel_shr(self, right: Self, n: u32) -> Self { + if let Some(r) = self.checked_funnel_shr(right, n) { + r + } else { + // This subtraction will never underflow. + self.unbounded_shr(n - Self::BITS) } } @@ -652,7 +780,7 @@ macro_rules! uint_impl { /// /// This results in undefined behavior if `n` is greater than or equal to #[doc = concat!("`", stringify!($SelfT) , "::BITS`,")] - /// i.e. when [`funnel_shl`](Self::funnel_shl) would panic. + /// i.e. when [`funnel_shl`](Self::funnel_shl) might panic or wrap. /// #[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")] #[unstable(feature = "funnel_shifts", issue = "145686")] @@ -678,7 +806,7 @@ macro_rules! uint_impl { /// /// This results in undefined behavior if `n` is greater than or equal to #[doc = concat!("`", stringify!($SelfT) , "::BITS`,")] - /// i.e. when [`funnel_shr`](Self::funnel_shr) would panic. + /// i.e. when [`funnel_shr`](Self::funnel_shr) might panic or wrap. /// #[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")] #[unstable(feature = "funnel_shifts", issue = "145686")] diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index c993c947929cd..2fdb02231a613 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -128,6 +128,7 @@ #![feature(unicode_internals)] #![feature(unsize)] #![feature(unwrap_infallible)] +#![feature(wrapping_funnel_shifts)] // tidy-alphabetical-end #![allow(internal_features)] #![deny(implicit_provenance_casts)] diff --git a/library/coretests/tests/num/uint_macros.rs b/library/coretests/tests/num/uint_macros.rs index 4dfcb1b8b4688..7f68a8a2aae98 100644 --- a/library/coretests/tests/num/uint_macros.rs +++ b/library/coretests/tests/num/uint_macros.rs @@ -241,6 +241,28 @@ macro_rules! uint_module { let _ = <$T>::funnel_shr(A, B, $T::BITS); } + #[test] + #[should_panic = "attempt to funnel shift left with overflow"] + fn test_strict_funnel_shl_overflow() { + let _ = <$T>::strict_funnel_shl(A, B, $T::BITS); + } + + #[test] + #[should_panic = "attempt to funnel shift right with overflow"] + fn test_strict_funnel_shr_overflow() { + let _ = <$T>::strict_funnel_shr(A, B, $T::BITS); + } + + #[test] + fn test_wrapping_funnel_shl_overflow() { + let _ = <$T>::wrapping_funnel_shl(A, B, $T::BITS); + } + + #[test] + fn test_wrapping_funnel_shr_overflow() { + let _ = <$T>::wrapping_funnel_shr(A, B, $T::BITS); + } + #[test] fn test_funnel_shifts_runtime() { for i in 0..$T::BITS - 1 { diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 92eccc27ee05d..3b8851506dee1 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -393,6 +393,7 @@ #![feature(ub_checks)] #![feature(uint_carryless_mul)] #![feature(used_with_arg)] +#![feature(wrapping_funnel_shifts)] #![feature(write_all_vectored)] // tidy-alphabetical-end //