about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJacob Pratt <jacob@jhpratt.dev>2024-07-03 03:03:13 -0400
committerGitHub <noreply@github.com>2024-07-03 03:03:13 -0400
commitdb592253a673a57ce0d1fa10b74c2a69c0e62bf9 (patch)
tree7cdffee79c30f93cf6c072d500fd115e2aad24f0
parentd163e5e515d470a713f7d6907852904622a85d4e (diff)
parent5745c220e65a83436bb0bf855dcf83aebffe8c73 (diff)
downloadrust-db592253a673a57ce0d1fa10b74c2a69c0e62bf9.tar.gz
rust-db592253a673a57ce0d1fa10b74c2a69c0e62bf9.zip
Rollup merge of #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: https://github.com/rust-lang/rust/issues/119131
FCP: https://github.com/rust-lang/rust/issues/119131#issuecomment-1906394087. The docs update should resolve the remaining concern.
-rw-r--r--library/alloc/src/lib.rs1
-rw-r--r--library/core/src/hint.rs97
-rw-r--r--library/core/src/intrinsics.rs2
-rw-r--r--library/core/src/lib.rs1
-rw-r--r--library/std/src/lib.rs1
-rw-r--r--tests/ui/consts/const-assert-unchecked-ub.rs6
-rw-r--r--tests/ui/consts/const-assert-unchecked-ub.stderr2
7 files changed, 77 insertions, 33 deletions
diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs
index a1a2404a2ee..703538d0f35 100644
--- a/library/alloc/src/lib.rs
+++ b/library/alloc/src/lib.rs
@@ -127,7 +127,6 @@
 #![feature(fmt_internals)]
 #![feature(fn_traits)]
 #![feature(hasher_prefixfree_extras)]
-#![feature(hint_assert_unchecked)]
 #![feature(inplace_iteration)]
 #![feature(iter_advance_by)]
 #![feature(iter_next_chunk)]
diff --git a/library/core/src/hint.rs b/library/core/src/hint.rs
index c3b16c34293..976a6c04ca6 100644
--- a/library/core/src/hint.rs
+++ b/library/core/src/hint.rs
@@ -111,41 +111,92 @@ 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 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.
+/// You may know this from other places as
+/// [`llvm.assume`](https://llvm.org/docs/LangRef.html#llvm-assume-intrinsic) or, in C,
+/// [`__builtin_assume`](https://clang.llvm.org/docs/LanguageExtensions.html#builtin-assume).
 ///
-/// 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.
+/// This promotes a correctness requirement to a soundness requirement. Don't do that without
+/// very good reason.
 ///
-/// 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);`.
+/// # Usage
 ///
-/// If ever you're tempted to write `assert_unchecked(false)`, then you're
-/// actually looking for [`unreachable_unchecked()`].
+/// 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, with the expectation to drop it
+/// later should the optimizer get smarter and no longer need it.
 ///
-/// 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).
+/// The more complicated the condition, the less likely this is to be useful. 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.
 ///
-/// This promotes a correctness requirement to a soundness requirement.
-/// Don't do that without very good reason.
+/// 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 is no benefit to
+/// `let n = u32::count_ones(x); assert_unchecked(n <= u32::BITS);`.
+///
+/// `assert_unchecked` is logically equivalent to `if !cond { unreachable_unchecked(); }`. If
+/// ever you are tempted to write `assert_unchecked(false)`, you should instead use
+/// [`unreachable_unchecked()`] directly.
 ///
 /// # Safety
 ///
-/// `cond` must be `true`.  It's immediate UB to call this with `false`.
+/// `cond` must be `true`. It is immediate UB to call this with `false`.
+///
+/// # Example
+///
+/// ```
+/// use core::hint;
 ///
+/// /// # Safety
+/// ///
+/// /// `p` must be nonnull and valid
+/// pub unsafe fn next_value(p: *const i32) -> i32 {
+///     // SAFETY: caller invariants guarantee that `p` is not null
+///     unsafe { hint::assert_unchecked(!p.is_null()) }
+///
+///     if p.is_null() {
+///         return -1;
+///     } else {
+///         // SAFETY: caller invariants guarantee that `p` is valid
+///         unsafe { *p + 1 }
+///     }
+/// }
+/// ```
+///
+/// Without the `assert_unchecked`, the above function produces the following with optimizations
+/// enabled:
+///
+/// ```asm
+/// next_value:
+///         test    rdi, rdi
+///         je      .LBB0_1
+///         mov     eax, dword ptr [rdi]
+///         inc     eax
+///         ret
+/// .LBB0_1:
+///         mov     eax, -1
+///         ret
+/// ```
+///
+/// Adding the assertion allows the optimizer to remove the extra check:
+///
+/// ```asm
+/// next_value:
+///         mov     eax, dword ptr [rdi]
+///         inc     eax
+///         ret
+/// ```
+///
+/// This example is quite unlike anything that would be used in the real world: it is redundant
+/// to put an an assertion right next to code that checks the same thing, and dereferencing a
+/// pointer already has the builtin assumption that it is nonnull. However, it illustrates the
+/// kind of changes the optimizer can make even when the behavior is less obviously related.
+#[track_caller]
 #[inline(always)]
 #[doc(alias = "assume")]
-#[track_caller]
-#[unstable(feature = "hint_assert_unchecked", issue = "119131")]
-#[rustc_const_unstable(feature = "const_hint_assert_unchecked", issue = "119131")]
+#[stable(feature = "hint_assert_unchecked", since = "CURRENT_RUSTC_VERSION")]
+#[rustc_const_stable(feature = "hint_assert_unchecked", since = "CURRENT_RUSTC_VERSION")]
 pub const unsafe fn assert_unchecked(cond: bool) {
     // SAFETY: The caller promised `cond` is true.
     unsafe {
diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs
index 9ba1c6a4154..720da0feece 100644
--- a/library/core/src/intrinsics.rs
+++ b/library/core/src/intrinsics.rs
@@ -959,7 +959,7 @@ extern "rust-intrinsic" {
 /// not be used if the invariant can be discovered by the optimizer on its
 /// own, or if it does not enable any significant optimizations.
 ///
-/// This intrinsic does not have a stable counterpart.
+/// The stabilized version of this intrinsic is [`core::hint::assert_unchecked`].
 #[rustc_const_stable(feature = "const_assume", since = "1.77.0")]
 #[rustc_nounwind]
 #[unstable(feature = "core_intrinsics", issue = "none")]
diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs
index 128f1c81827..c5a1fca667b 100644
--- a/library/core/src/lib.rs
+++ b/library/core/src/lib.rs
@@ -127,7 +127,6 @@
 #![feature(const_fmt_arguments_new)]
 #![feature(const_hash)]
 #![feature(const_heap)]
-#![feature(const_hint_assert_unchecked)]
 #![feature(const_index_range_slice_index)]
 #![feature(const_int_from_str)]
 #![feature(const_intrinsic_copy)]
diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs
index c8691a9a609..6f9ac3d4acb 100644
--- a/library/std/src/lib.rs
+++ b/library/std/src/lib.rs
@@ -334,7 +334,6 @@
 #![feature(fmt_internals)]
 #![feature(hasher_prefixfree_extras)]
 #![feature(hashmap_internals)]
-#![feature(hint_assert_unchecked)]
 #![feature(ip)]
 #![feature(maybe_uninit_slice)]
 #![feature(maybe_uninit_write_slice)]
diff --git a/tests/ui/consts/const-assert-unchecked-ub.rs b/tests/ui/consts/const-assert-unchecked-ub.rs
index 5c05b813048..ffc02eedcb7 100644
--- a/tests/ui/consts/const-assert-unchecked-ub.rs
+++ b/tests/ui/consts/const-assert-unchecked-ub.rs
@@ -1,10 +1,6 @@
-#![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() {
-}
+fn main() {}
diff --git a/tests/ui/consts/const-assert-unchecked-ub.stderr b/tests/ui/consts/const-assert-unchecked-ub.stderr
index 3957a3b1c24..468f15f3472 100644
--- a/tests/ui/consts/const-assert-unchecked-ub.stderr
+++ b/tests/ui/consts/const-assert-unchecked-ub.stderr
@@ -1,5 +1,5 @@
 error[E0080]: evaluation of constant value failed
-  --> $DIR/const-assert-unchecked-ub.rs:6:5
+  --> $DIR/const-assert-unchecked-ub.rs:3:5
    |
 LL |     std::hint::assert_unchecked(n < 32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `assume` called with `false`