diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs index ff177c70d39..4a73f160b65 100644 --- a/library/core/src/hint.rs +++ b/library/core/src/hint.rs @@ -106,6 +106,54 @@ } } +/// Makes a *soundness* promise to the compiler that `cond` holds. +/// +/// This may allow the optimizer to simplify things, +/// but it might also make the generated code slower. +/// Either way, calling it will most likely make compilation take longer. +/// +/// This is a situational tool for micro-optimization, and is allowed to do nothing. +/// Any use should come with a repeatable benchmark to show the value +/// and allow removing it later should the optimizer get smarter and no longer need it. +/// +/// The more complicated the condition the less likely this is to be fruitful. +/// For example, `assert_unchecked(foo.is_sorted())` is a complex enough value +/// that the compiler is unlikely to be able to take advantage of it. +/// +/// There's also no need to `assert_unchecked` basic properties of things. For +/// example, the compiler already knows the range of `count_ones`, so there's no +/// benefit to `let n = u32::count_ones(x); assert_unchecked(n <= u32::BITS);`. +/// +/// If ever you're tempted to write `assert_unchecked(false)`, then you're +/// actually looking for [`unreachable_unchecked()`]. +/// +/// You may know this from other places +/// as [`llvm.assume`](https://llvm.org/docs/LangRef.html#llvm-assume-intrinsic) +/// or [`__builtin_assume`](https://clang.llvm.org/docs/LanguageExtensions.html#builtin-assume). +/// +/// This promotes a correctness requirement to a soundness requirement. +/// Don't do that without very good reason. +/// +/// # Safety +/// +/// `cond` must be `true`. It's immediate UB to call this with `false`. +/// +#[inline(always)] +#[doc(alias = "assume")] +#[track_caller] +#[unstable(feature = "hint_assert_unchecked", issue = "119131")] +#[rustc_const_unstable(feature = "const_hint_assert_unchecked", issue = "119131")] +pub const unsafe fn assert_unchecked(cond: bool) { + // SAFETY: The caller promised `cond` is true. + unsafe { + intrinsics::assert_unsafe_precondition!( + "hint::assert_unchecked must never be called when the condition is false", + (cond: bool) => cond, + ); + crate::intrinsics::assume(cond); + } +} + /// Emits a machine instruction to signal the processor that it is running in /// a busy-wait spin-loop ("spin lock"). /// diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index f25ca9e2b18..67db289dd47 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -2516,7 +2516,7 @@ pub fn const_eval_select( /// the occasional mistake, and this check should help them figure things out. #[allow_internal_unstable(const_eval_select)] // permit this to be called in stably-const fn macro_rules! assert_unsafe_precondition { - ($name:expr, $([$($tt:tt)*])?($($i:ident:$ty:ty),*$(,)?) => $e:expr) => { + ($name:expr, $([$($tt:tt)*])?($($i:ident:$ty:ty),*$(,)?) => $e:expr $(,)?) => { if cfg!(debug_assertions) { // allow non_snake_case to allow capturing const generics #[allow(non_snake_case)] diff --git a/tests/ui/consts/const-assert-unchecked-ub.rs b/tests/ui/consts/const-assert-unchecked-ub.rs new file mode 100644 index 00000000000..5c05b813048 --- /dev/null +++ b/tests/ui/consts/const-assert-unchecked-ub.rs @@ -0,0 +1,10 @@ +#![feature(hint_assert_unchecked)] +#![feature(const_hint_assert_unchecked)] + +const _: () = unsafe { + let n = u32::MAX.count_ones(); + std::hint::assert_unchecked(n < 32); //~ ERROR evaluation of constant value failed +}; + +fn main() { +} diff --git a/tests/ui/consts/const-assert-unchecked-ub.stderr b/tests/ui/consts/const-assert-unchecked-ub.stderr new file mode 100644 index 00000000000..3957a3b1c24 --- /dev/null +++ b/tests/ui/consts/const-assert-unchecked-ub.stderr @@ -0,0 +1,9 @@ +error[E0080]: evaluation of constant value failed + --> $DIR/const-assert-unchecked-ub.rs:6:5 + | +LL | std::hint::assert_unchecked(n < 32); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `assume` called with `false` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0080`.