Auto merge of #119133 - scottmcm:assert-unchecked, r=thomcc
Add `hint::assert_unchecked` Libs-API expressed interest, modulo bikeshedding, in https://github.com/rust-lang/libs-team/issues/315#issuecomment-1863159430 I think that means this is good for nightly, since we can always rename it before stabilization. Tracking issue: https://github.com/rust-lang/rust/issues/119131
This commit is contained in:
commit
e1fadb2c35
4 changed files with 68 additions and 1 deletions
|
@ -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()`].
|
||||||
|
///
|
||||||
|
/// 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
|
/// Emits a machine instruction to signal the processor that it is running in
|
||||||
/// a busy-wait spin-loop ("spin lock").
|
/// a busy-wait spin-loop ("spin lock").
|
||||||
///
|
///
|
||||||
|
|
|
@ -2534,7 +2534,7 @@ extern "rust-intrinsic" {
|
||||||
/// the occasional mistake, and this check should help them figure things out.
|
/// 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
|
#[allow_internal_unstable(const_eval_select)] // permit this to be called in stably-const fn
|
||||||
macro_rules! assert_unsafe_precondition {
|
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) {
|
if cfg!(debug_assertions) {
|
||||||
// allow non_snake_case to allow capturing const generics
|
// allow non_snake_case to allow capturing const generics
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
|
10
tests/ui/consts/const-assert-unchecked-ub.rs
Normal file
10
tests/ui/consts/const-assert-unchecked-ub.rs
Normal file
|
@ -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
tests/ui/consts/const-assert-unchecked-ub.stderr
Normal file
9
tests/ui/consts/const-assert-unchecked-ub.stderr
Normal file
|
@ -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`.
|
Loading…
Add table
Add a link
Reference in a new issue