Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 42 additions & 12 deletions library/core/src/num/uint_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -515,8 +515,13 @@ macro_rules! uint_impl {
///
/// # Panics
///
/// This function will panic if `n` is greater than or equal to the number of
/// bits in `self`.
/// ## Overflow behavior
///
/// If overflow checks are enabled (default in debug mode), this function will panic if `n`
/// is greater than or equal to the number of bits in `self`. If overflow checks are
/// disabled (default in release mode), there is no panic; instead, the value is shifted
/// by `n % Self::BITS`.
// FIXME(wrapping_funnel_shifts): link to `wrapping_funnel_shl` when stable.
///
/// # Examples
///
Expand All @@ -543,21 +548,31 @@ macro_rules! uint_impl {
///
/// ```should_panic
/// #![feature(funnel_shifts)]
/// # #![feature(cfg_overflow_checks)]
/// # #[cfg(overflow_checks)] {
///
#[doc = concat!("let a = ", stringify!($SelfT), "::MAX;")]
/// // Okay
#[doc = concat!("let _ = a.rotate_left(", stringify!($SelfT), "::BITS);")]
/// // Panics
/// // Panics (only when overflow checks are enabled)
#[doc = concat!("let _ = a.funnel_shl(a, ", stringify!($SelfT), "::BITS);")]
/// # }
/// # #[cfg(not(overflow_checks))] panic!("fulfill should_panic");
/// ```
#[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")]
#[unstable(feature = "funnel_shifts", issue = "145686")]
#[must_use = "this returns the result of the operation, without modifying the original"]
#[inline(always)]
#[rustc_inherit_overflow_checks]
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) }
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)
}
}

/// Performs a right funnel shift.
Expand All @@ -571,8 +586,13 @@ macro_rules! uint_impl {
///
/// # Panics
///
/// This function will panic if `n` is greater than or equal to the number of
/// bits in `self`.
/// ## Overflow behavior
///
/// If overflow checks are enabled (default in debug mode), this function will panic if `n`
/// is greater than or equal to the number of bits in `self`. If overflow checks are
/// disabled (default in release mode), there is no panic; instead, the value is shifted
/// by `n % Self::BITS`.
// FIXME(wrapping_funnel_shifts): link to `wrapping_funnel_shr` when stable.
///
/// # Examples
///
Expand All @@ -599,21 +619,31 @@ macro_rules! uint_impl {
///
/// ```should_panic
/// #![feature(funnel_shifts)]
/// # #![feature(cfg_overflow_checks)]
/// # #[cfg(overflow_checks)] {
///
#[doc = concat!("let a = ", stringify!($SelfT), "::MAX;")]
/// // Okay
#[doc = concat!("let _ = a.rotate_right(", stringify!($SelfT), "::BITS);")]
/// // Panics
/// // Panics (only when overflow checks are enabled)
#[doc = concat!("let _ = a.funnel_shr(a, ", stringify!($SelfT), "::BITS);")]
/// # }
/// # #[cfg(not(overflow_checks))] panic!("fulfill should_panic");
/// ```
#[rustc_const_unstable(feature = "funnel_shifts", issue = "145686")]
#[unstable(feature = "funnel_shifts", issue = "145686")]
#[must_use = "this returns the result of the operation, without modifying the original"]
#[inline(always)]
#[rustc_inherit_overflow_checks]
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
unsafe { self.unchecked_funnel_shr(right, n) }
if intrinsics::overflow_checks() {
assert!(n < Self::BITS, "attempt to funnel shift right with overflow");
}
// SAFETY: `n` is wrapped to within range
unsafe {
let n = n & (Self::BITS - 1);
self.unchecked_funnel_shr(right, n)
}
}

/// Unchecked funnel shift left.
Expand Down
1 change: 1 addition & 0 deletions library/coretests/tests/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#![feature(borrowed_buf_init)]
#![feature(bstr)]
#![feature(casefold)]
#![feature(cfg_overflow_checks)]
#![feature(cfg_target_has_reliable_f16_f128)]
#![feature(char_internals)]
#![feature(clone_to_uninit)]
Expand Down
14 changes: 14 additions & 0 deletions library/coretests/tests/num/uint_macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,17 +216,31 @@ macro_rules! uint_module {
}

#[test]
#[cfg(overflow_checks)]
#[should_panic = "attempt to funnel shift left with overflow"]
fn test_funnel_shl_overflow() {
let _ = <$T>::funnel_shl(A, B, $T::BITS);
}

#[test]
#[cfg(overflow_checks)]
#[should_panic = "attempt to funnel shift right with overflow"]
fn test_funnel_shr_overflow() {
let _ = <$T>::funnel_shr(A, B, $T::BITS);
}

#[test]
#[cfg(not(overflow_checks))]
fn test_funnel_shl_overflow() {
let _ = <$T>::funnel_shl(A, B, $T::BITS);
}

#[test]
#[cfg(not(overflow_checks))]
fn test_funnel_shr_overflow() {
let _ = <$T>::funnel_shr(A, B, $T::BITS);
}

#[test]
fn test_funnel_shifts_runtime() {
for i in 0..$T::BITS - 1 {
Expand Down
45 changes: 45 additions & 0 deletions tests/ui/std/overflow-check-ops.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
//! Verify the behavior differences between enabling and disabling overflow checks.

//@ run-pass
//@ needs-unwind
//@ revisions: ERROR WRAP
//@[ERROR] compile-flags: -C overflow-checks=true
//@[WRAP] compile-flags: -C overflow-checks=false

#![feature(cfg_overflow_checks)]
#![feature(funnel_shifts)]

use std::hint::black_box as bb;
use std::{assert_matches, fmt, panic};

#[track_caller]
fn check<T: fmt::Debug + PartialEq>(func: fn() -> T, wrapping_res: T, name: &str) {
let type_name = std::any::type_name::<T>();
let res = panic::catch_unwind(func);
if cfg!(overflow_checks) {
assert_matches!(res, Err(_), "{type_name} {name}");
} else {
assert_eq!(res.unwrap(), wrapping_res, "{type_name} {name}");
}
}

fn main() {
check(|| bb(u32::MAX) + bb(1), 0, "add");
check(|| bb(0u32) - bb(1), u32::MAX, "sub");
check(|| bb(u32::MAX) * bb(2), u32::MAX << 1, "mul");
check(|| bb(1u32) << bb(32), 1, "shl");
check(|| bb(u32::MAX) >> bb(32), u32::MAX, "shr");
check(|| bb(1234u32).funnel_shl(4567, bb(32)), 1234, "funnel_shl");
check(|| bb(1234u32).funnel_shr(4567, bb(32)), 4567, "funnel_shr");
check(|| bb(u32::MAX).pow(bb(2)), 1, "pow");
check(|| bb(u32::MAX).next_power_of_two(), 0, "next_power_of_two");

check(|| bb(i32::MAX) + bb(1), i32::MIN, "add");
check(|| bb(i32::MIN) - bb(1), i32::MAX, "sub");
check(|| bb(i32::MAX) * bb(2), i32::MAX << 1, "mul");
check(|| -bb(i32::MIN), i32::MIN, "neg");
check(|| bb(i32::MIN).abs(), i32::MIN, "abs");
check(|| bb(1) << bb(32), 1, "shl");
check(|| bb(i32::MAX) >> bb(32), i32::MAX, "shr");
check(|| bb(i32::MAX).pow(bb(2)), 1, "pow");
}
Loading