Skip to content

Commit 7556d6f

Browse files
committed
Add hint::assert_unchecked
1 parent 5c97719 commit 7556d6f

File tree

4 files changed

+68
-1
lines changed

4 files changed

+68
-1
lines changed

library/core/src/hint.rs

+48
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,54 @@ pub const unsafe fn unreachable_unchecked() -> ! {
106106
}
107107
}
108108

109+
/// Makes a *soundness* promise to the compiler that `cond` holds.
110+
///
111+
/// This may allow the optimizer to simplify things,
112+
/// but it might also make the generated code slower.
113+
/// Either way, calling it will most likely make compilation take longer.
114+
///
115+
/// This is a situational tool for micro-optimization, and is allowed to do nothing.
116+
/// Any use should come with a repeatable benchmark to show the value
117+
/// and allow removing it later should the optimizer get smarter and no longer need it.
118+
///
119+
/// The more complicated the condition the less likely this is to be fruitful.
120+
/// For example, `assert_unchecked(foo.is_sorted())` is a complex enough value
121+
/// that the compiler is unlikely to be able to take advantage of it.
122+
///
123+
/// There's also no need to `assert_unchecked` basic properties of things. For
124+
/// example, the compiler already knows the range of `count_ones`, so there's no
125+
/// benefit to `let n = u32::count_ones(x); assert_unchecked(n <= u32::BITS);`.
126+
///
127+
/// If ever you're tempted to write `assert_unchecked(false)`, then you're
128+
/// actually looking for [`unreachable_unchecked()`].
129+
///
130+
/// You may know this from other places
131+
/// as [`llvm.assume`](https://llvm.org/docs/LangRef.html#llvm-assume-intrinsic)
132+
/// or [`__builtin_assume`](https://clang.llvm.org/docs/LanguageExtensions.html#builtin-assume).
133+
///
134+
/// This promotes a correctness requirement to a soundness requirement.
135+
/// Don't do that without very good reason.
136+
///
137+
/// # Safety
138+
///
139+
/// `cond` must be `true`. It's immediate UB to call this with `false`.
140+
///
141+
#[inline(always)]
142+
#[doc(alias = "assume")]
143+
#[track_caller]
144+
#[unstable(feature = "hint_assert_unchecked", issue = "119131")]
145+
#[rustc_const_unstable(feature = "const_hint_assert_unchecked", issue = "119131")]
146+
pub const unsafe fn assert_unchecked(cond: bool) {
147+
// SAFETY: The caller promised `cond` is true.
148+
unsafe {
149+
intrinsics::assert_unsafe_precondition!(
150+
"hint::assert_unchecked must never be called when the condition is false",
151+
(cond: bool) => cond,
152+
);
153+
crate::intrinsics::assume(cond);
154+
}
155+
}
156+
109157
/// Emits a machine instruction to signal the processor that it is running in
110158
/// a busy-wait spin-loop ("spin lock").
111159
///

library/core/src/intrinsics.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2516,7 +2516,7 @@ extern "rust-intrinsic" {
25162516
/// the occasional mistake, and this check should help them figure things out.
25172517
#[allow_internal_unstable(const_eval_select)] // permit this to be called in stably-const fn
25182518
macro_rules! assert_unsafe_precondition {
2519-
($name:expr, $([$($tt:tt)*])?($($i:ident:$ty:ty),*$(,)?) => $e:expr) => {
2519+
($name:expr, $([$($tt:tt)*])?($($i:ident:$ty:ty),*$(,)?) => $e:expr $(,)?) => {
25202520
if cfg!(debug_assertions) {
25212521
// allow non_snake_case to allow capturing const generics
25222522
#[allow(non_snake_case)]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
#![feature(hint_assert_unchecked)]
2+
#![feature(const_hint_assert_unchecked)]
3+
4+
const _: () = unsafe {
5+
let n = u32::MAX.count_ones();
6+
std::hint::assert_unchecked(n < 32); //~ ERROR evaluation of constant value failed
7+
};
8+
9+
fn main() {
10+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0080]: evaluation of constant value failed
2+
--> $DIR/const-assert-unchecked-ub.rs:6:5
3+
|
4+
LL | std::hint::assert_unchecked(n < 32);
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `assume` called with `false`
6+
7+
error: aborting due to 1 previous error
8+
9+
For more information about this error, try `rustc --explain E0080`.

0 commit comments

Comments
 (0)