Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add hint::assert_unchecked #119133

Merged
merged 1 commit into from Dec 26, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
48 changes: 48 additions & 0 deletions library/core/src/hint.rs
Expand Up @@ -106,6 +106,54 @@ pub const unsafe fn unreachable_unchecked() -> ! {
}
}

/// 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()`].
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth mentioning that this is logically entirely equivalent to if !cond { 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").
///
Expand Down
2 changes: 1 addition & 1 deletion library/core/src/intrinsics.rs
Expand Up @@ -2516,7 +2516,7 @@ extern "rust-intrinsic" {
/// 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)]
Expand Down
10 changes: 10 additions & 0 deletions 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() {
}
9 changes: 9 additions & 0 deletions 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`.