Skip to content

Commit 3615cb4

Browse files
committed
Expand core::hint::unreachable_unchecked() docs
Fixes #95865
1 parent 1ec2c13 commit 3615cb4

File tree

1 file changed

+58
-14
lines changed

1 file changed

+58
-14
lines changed

library/core/src/hint.rs

+58-14
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,71 @@
55
66
use crate::intrinsics;
77

8-
/// Informs the compiler that this point in the code is not reachable, enabling
9-
/// further optimizations.
8+
/// Informs the compiler that the site which is calling this function is not
9+
/// reachable, possibly enabling further optimizations.
1010
///
1111
/// # Safety
1212
///
13-
/// Reaching this function is completely *undefined behavior* (UB). In
14-
/// particular, the compiler assumes that all UB must never happen, and
15-
/// therefore will eliminate all branches that reach to a call to
13+
/// Reaching this function is *Undefined Behavior* (UB). In particular, as the
14+
/// compiler assumes that all forms of Undefined Behavior can never happen, it
15+
/// will eliminate all branches which themselves reach a call to
1616
/// `unreachable_unchecked()`.
1717
///
18-
/// Like all instances of UB, if this assumption turns out to be wrong, i.e., the
19-
/// `unreachable_unchecked()` call is actually reachable among all possible
20-
/// control flow, the compiler will apply the wrong optimization strategy, and
21-
/// may sometimes even corrupt seemingly unrelated code, causing
22-
/// difficult-to-debug problems.
18+
/// If the assumptions embedded in using this function turn out to be wrong -
19+
/// that is, if the site which is calling `unreachable_unchecked()` is actually
20+
/// reachable at runtime - the compiler may have generated nonsensical machine
21+
/// instructions for this situation, including in seemingly unrelated code,
22+
/// causing difficult-to-debug problems.
2323
///
24-
/// Use this function only when you can prove that the code will never call it.
25-
/// Otherwise, consider using the [`unreachable!`] macro, which does not allow
26-
/// optimizations but will panic when executed.
24+
/// Use this function sparingly. Consider using the [`unreachable!`] macro,
25+
/// which may prevent some optimizations but will safely panic in case it is
26+
/// actually reached at runtime. Benchmark your code to find out if using
27+
/// `unreachable_unchecked()` comes with a performance benefit.
2728
///
28-
/// # Example
29+
/// # Examples
30+
///
31+
/// `unreachable_unchecked()` can be used in situations where the compiler
32+
/// can't prove invariants that were previously established. Such situations
33+
/// have a higher chance of occuring if those invariants are upheld by
34+
/// external code that the compiler can't analyze.
35+
/// ```
36+
/// fn prepare_inputs(divisors: &mut Vec<u32>) {
37+
/// // Note to future-self when making changes: The invariant established
38+
/// // here is NOT checked in `do_computation()`; if this changes, you HAVE
39+
/// // to change `do_computation()`.
40+
/// divisors.retain(|divisor| *divisor != 0)
41+
/// }
42+
///
43+
/// fn do_computation(i: u32, divisors: &[u32]) -> u32 {
44+
/// divisors.iter().fold(i, |acc, divisor| {
45+
/// // Convince the compiler that a division by zero can't happen here
46+
/// // and a check is not needed below.
47+
/// if *divisor == 0 {
48+
/// // SAFETY: `divisor` can't be zero because of `prepare_inputs`,
49+
/// // but the compiler does not know about this. We *promise*
50+
/// // that we always call `prepare_inputs`.
51+
/// unsafe { std::hint::unreachable_unchecked() }
52+
/// }
53+
/// // The compiler would normally introduce a check here that prevents
54+
/// // a division by zero. However, if `divisor` was zero, the branch
55+
/// // above would reach what we explicitly marked as unreachable.
56+
/// // The compiler concludes that `divisor` can't be zero at this point
57+
/// // and removes the - now proven useless - check.
58+
/// acc / divisor
59+
/// })
60+
/// }
61+
///
62+
/// let mut divisors = vec![2, 0, 4];
63+
/// prepare_inputs(&mut divisors);
64+
/// assert_eq!(do_computation(100, &divisors), 12);
65+
///
66+
/// ```
67+
///
68+
/// While using `unreachable_unchecked()` is perfectly safe in the following
69+
/// example, the compiler is able to prove that a division by zero is not
70+
/// possible. Benchmarking reveals that `unreachable_unchecked()` provides
71+
/// no benefit over using [`unreachable!`], while the latter does not introduce
72+
/// the possibility of Undefined Behavior.
2973
///
3074
/// ```
3175
/// fn div_1(a: u32, b: u32) -> u32 {

0 commit comments

Comments
 (0)