From 3382d393156e934f6de8cb1a5930712a5ac7b821 Mon Sep 17 00:00:00 2001 From: Laine Taffin Altman Date: Fri, 14 Aug 2026 17:14:15 -0700 Subject: [PATCH] Checked/wrapping/unbounded funnel shifts --- library/core/src/lib.rs | 3 +- library/core/src/num/uint_macros.rs | 108 ++++++++++++++++++++++++++-- library/std/src/lib.rs | 1 + 3 files changed, 106 insertions(+), 6 deletions(-) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 8c85e6d1c0c5e..a4d7daf64a0c0 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 1664ce83aef72..1939ab8882e2d 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -555,9 +555,7 @@ macro_rules! uint_impl { #[must_use = "this returns the result of the operation, without modifying the original"] #[inline(always)] pub const fn funnel_shl(self, right: Self, n: u32) -> Self { - assert!(n < Self::BITS, "attempt to funnel shift left with overflow"); - // SAFETY: just checked that `shift` is in-range - unsafe { self.unchecked_funnel_shl(right, n) } + self.checked_funnel_shl(right, n).expect("attempt to funnel shift left with overflow") } /// Performs a right funnel shift. @@ -611,11 +609,111 @@ macro_rules! uint_impl { #[must_use = "this returns the result of the operation, without modifying the original"] #[inline(always)] pub const fn funnel_shr(self, right: Self, n: u32) -> Self { - assert!(n < Self::BITS, "attempt to funnel shift right with overflow"); - // SAFETY: just checked that `shift` is in-range + self.checked_funnel_shr(right, n).expect("attempt to funnel shift right with overflow") + } + + /// 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) would panic. + #[rustc_const_unstable(feature = "wrapping_funnel_shifts", issue = "none")] + #[unstable(feature = "wrapping_funnel_shifts", issue = "none")] + #[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) would panic. + #[rustc_const_unstable(feature = "wrapping_funnel_shifts", issue = "none")] + #[unstable(feature = "wrapping_funnel_shifts", issue = "none")] + #[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 = "none")] + #[unstable(feature = "wrapping_funnel_shifts", issue = "none")] + #[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 now ensured to be in-range + 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 = "none")] + #[unstable(feature = "wrapping_funnel_shifts", issue = "none")] + #[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 now ensured to be in-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 = "none")] + #[unstable(feature = "wrapping_funnel_shifts", issue = "none")] + #[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 = "none")] + #[unstable(feature = "wrapping_funnel_shifts", issue = "none")] + #[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) + } + } + /// Unchecked funnel shift left. /// /// # Safety diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 22ac7443f464b..afb759d5e16d0 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -394,6 +394,7 @@ #![feature(ub_checks)] #![feature(uint_carryless_mul)] #![feature(used_with_arg)] +#![feature(wrapping_funnel_shifts)] #![feature(write_all_vectored)] // tidy-alphabetical-end //