Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit b184a84

Browse files
authoredJul 3, 2024
Rollup merge of rust-lang#123588 - tgross35:stabilize-assert_unchecked, r=dtolnay
Stabilize `hint::assert_unchecked` Make the following API stable, including const: ```rust // core::hint, std::hint pub const unsafe fn assert_unchecked(p: bool); ``` This PR also reworks some of the documentation and adds an example. Tracking issue: rust-lang#119131 FCP: rust-lang#119131 (comment). The docs update should resolve the remaining concern.
2 parents a8b6d0a + 1ba2fa4 commit b184a84

File tree

5 files changed

+75
-27
lines changed

5 files changed

+75
-27
lines changed
 

‎alloc/src/lib.rs

-1
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,6 @@
127127
#![feature(fmt_internals)]
128128
#![feature(fn_traits)]
129129
#![feature(hasher_prefixfree_extras)]
130-
#![feature(hint_assert_unchecked)]
131130
#![feature(inplace_iteration)]
132131
#![feature(iter_advance_by)]
133132
#![feature(iter_next_chunk)]

‎core/src/hint.rs

+74-23
Original file line numberDiff line numberDiff line change
@@ -111,41 +111,92 @@ pub const unsafe fn unreachable_unchecked() -> ! {
111111

112112
/// Makes a *soundness* promise to the compiler that `cond` holds.
113113
///
114-
/// This may allow the optimizer to simplify things,
115-
/// but it might also make the generated code slower.
116-
/// Either way, calling it will most likely make compilation take longer.
114+
/// This may allow the optimizer to simplify things, but it might also make the generated code
115+
/// slower. Either way, calling it will most likely make compilation take longer.
117116
///
118-
/// This is a situational tool for micro-optimization, and is allowed to do nothing.
119-
/// Any use should come with a repeatable benchmark to show the value
120-
/// and allow removing it later should the optimizer get smarter and no longer need it.
117+
/// You may know this from other places as
118+
/// [`llvm.assume`](https://llvm.org/docs/LangRef.html#llvm-assume-intrinsic) or, in C,
119+
/// [`__builtin_assume`](https://clang.llvm.org/docs/LanguageExtensions.html#builtin-assume).
121120
///
122-
/// The more complicated the condition the less likely this is to be fruitful.
123-
/// For example, `assert_unchecked(foo.is_sorted())` is a complex enough value
124-
/// that the compiler is unlikely to be able to take advantage of it.
121+
/// This promotes a correctness requirement to a soundness requirement. Don't do that without
122+
/// very good reason.
125123
///
126-
/// There's also no need to `assert_unchecked` basic properties of things. For
127-
/// example, the compiler already knows the range of `count_ones`, so there's no
128-
/// benefit to `let n = u32::count_ones(x); assert_unchecked(n <= u32::BITS);`.
124+
/// # Usage
129125
///
130-
/// If ever you're tempted to write `assert_unchecked(false)`, then you're
131-
/// actually looking for [`unreachable_unchecked()`].
126+
/// This is a situational tool for micro-optimization, and is allowed to do nothing. Any use
127+
/// should come with a repeatable benchmark to show the value, with the expectation to drop it
128+
/// later should the optimizer get smarter and no longer need it.
132129
///
133-
/// You may know this from other places
134-
/// as [`llvm.assume`](https://llvm.org/docs/LangRef.html#llvm-assume-intrinsic)
135-
/// or [`__builtin_assume`](https://clang.llvm.org/docs/LanguageExtensions.html#builtin-assume).
130+
/// The more complicated the condition, the less likely this is to be useful. For example,
131+
/// `assert_unchecked(foo.is_sorted())` is a complex enough value that the compiler is unlikely
132+
/// to be able to take advantage of it.
136133
///
137-
/// This promotes a correctness requirement to a soundness requirement.
138-
/// Don't do that without very good reason.
134+
/// There's also no need to `assert_unchecked` basic properties of things. For example, the
135+
/// compiler already knows the range of `count_ones`, so there is no benefit to
136+
/// `let n = u32::count_ones(x); assert_unchecked(n <= u32::BITS);`.
137+
///
138+
/// `assert_unchecked` is logically equivalent to `if !cond { unreachable_unchecked(); }`. If
139+
/// ever you are tempted to write `assert_unchecked(false)`, you should instead use
140+
/// [`unreachable_unchecked()`] directly.
139141
///
140142
/// # Safety
141143
///
142-
/// `cond` must be `true`. It's immediate UB to call this with `false`.
144+
/// `cond` must be `true`. It is immediate UB to call this with `false`.
145+
///
146+
/// # Example
147+
///
148+
/// ```
149+
/// use core::hint;
143150
///
151+
/// /// # Safety
152+
/// ///
153+
/// /// `p` must be nonnull and valid
154+
/// pub unsafe fn next_value(p: *const i32) -> i32 {
155+
/// // SAFETY: caller invariants guarantee that `p` is not null
156+
/// unsafe { hint::assert_unchecked(!p.is_null()) }
157+
///
158+
/// if p.is_null() {
159+
/// return -1;
160+
/// } else {
161+
/// // SAFETY: caller invariants guarantee that `p` is valid
162+
/// unsafe { *p + 1 }
163+
/// }
164+
/// }
165+
/// ```
166+
///
167+
/// Without the `assert_unchecked`, the above function produces the following with optimizations
168+
/// enabled:
169+
///
170+
/// ```asm
171+
/// next_value:
172+
/// test rdi, rdi
173+
/// je .LBB0_1
174+
/// mov eax, dword ptr [rdi]
175+
/// inc eax
176+
/// ret
177+
/// .LBB0_1:
178+
/// mov eax, -1
179+
/// ret
180+
/// ```
181+
///
182+
/// Adding the assertion allows the optimizer to remove the extra check:
183+
///
184+
/// ```asm
185+
/// next_value:
186+
/// mov eax, dword ptr [rdi]
187+
/// inc eax
188+
/// ret
189+
/// ```
190+
///
191+
/// This example is quite unlike anything that would be used in the real world: it is redundant
192+
/// to put an an assertion right next to code that checks the same thing, and dereferencing a
193+
/// pointer already has the builtin assumption that it is nonnull. However, it illustrates the
194+
/// kind of changes the optimizer can make even when the behavior is less obviously related.
195+
#[track_caller]
144196
#[inline(always)]
145197
#[doc(alias = "assume")]
146-
#[track_caller]
147-
#[unstable(feature = "hint_assert_unchecked", issue = "119131")]
148-
#[rustc_const_unstable(feature = "const_hint_assert_unchecked", issue = "119131")]
198+
#[stable(feature = "hint_assert_unchecked", since = "CURRENT_RUSTC_VERSION")]
199+
#[rustc_const_stable(feature = "hint_assert_unchecked", since = "CURRENT_RUSTC_VERSION")]
149200
pub const unsafe fn assert_unchecked(cond: bool) {
150201
// SAFETY: The caller promised `cond` is true.
151202
unsafe {

‎core/src/intrinsics.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -959,7 +959,7 @@ extern "rust-intrinsic" {
959959
/// not be used if the invariant can be discovered by the optimizer on its
960960
/// own, or if it does not enable any significant optimizations.
961961
///
962-
/// This intrinsic does not have a stable counterpart.
962+
/// The stabilized version of this intrinsic is [`core::hint::assert_unchecked`].
963963
#[rustc_const_stable(feature = "const_assume", since = "1.77.0")]
964964
#[rustc_nounwind]
965965
#[unstable(feature = "core_intrinsics", issue = "none")]

‎core/src/lib.rs

-1
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,6 @@
127127
#![feature(const_fmt_arguments_new)]
128128
#![feature(const_hash)]
129129
#![feature(const_heap)]
130-
#![feature(const_hint_assert_unchecked)]
131130
#![feature(const_index_range_slice_index)]
132131
#![feature(const_int_from_str)]
133132
#![feature(const_intrinsic_copy)]

‎std/src/lib.rs

-1
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,6 @@
334334
#![feature(fmt_internals)]
335335
#![feature(hasher_prefixfree_extras)]
336336
#![feature(hashmap_internals)]
337-
#![feature(hint_assert_unchecked)]
338337
#![feature(ip)]
339338
#![feature(maybe_uninit_slice)]
340339
#![feature(maybe_uninit_write_slice)]

0 commit comments

Comments
 (0)
Failed to load comments.