|
5 | 5 |
|
6 | 6 | use crate::intrinsics;
|
7 | 7 |
|
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. |
10 | 10 | ///
|
11 | 11 | /// # Safety
|
12 | 12 | ///
|
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 |
16 |
| -/// `unreachable_unchecked()`. |
| 13 | +/// Reaching this function is *Undefined Behavior*. |
17 | 14 | ///
|
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. |
| 15 | +/// As the compiler assumes that all forms of Undefined Behavior can never |
| 16 | +/// happen, it will eliminate all branches in the surrounding code that it can |
| 17 | +/// determine will invariably lead to a call to `unreachable_unchecked()`. |
23 | 18 | ///
|
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. |
| 19 | +/// If the assumptions embedded in using this function turn out to be wrong - |
| 20 | +/// that is, if the site which is calling `unreachable_unchecked()` is actually |
| 21 | +/// reachable at runtime - the compiler may have generated nonsensical machine |
| 22 | +/// instructions for this situation, including in seemingly unrelated code, |
| 23 | +/// causing difficult-to-debug problems. |
27 | 24 | ///
|
28 |
| -/// # Example |
| 25 | +/// Use this function sparingly. Consider using the [`unreachable!`] macro, |
| 26 | +/// which may prevent some optimizations but will safely panic in case it is |
| 27 | +/// actually reached at runtime. Benchmark your code to find out if using |
| 28 | +/// `unreachable_unchecked()` comes with a performance benefit. |
| 29 | +/// |
| 30 | +/// # Examples |
| 31 | +/// |
| 32 | +/// `unreachable_unchecked()` can be used in situations where the compiler |
| 33 | +/// can't prove invariants that were previously established. Such situations |
| 34 | +/// have a higher chance of occuring if those invariants are upheld by |
| 35 | +/// external code that the compiler can't analyze. |
| 36 | +/// ``` |
| 37 | +/// fn prepare_inputs(divisors: &mut Vec<u32>) { |
| 38 | +/// // Note to future-self when making changes: The invariant established |
| 39 | +/// // here is NOT checked in `do_computation()`; if this changes, you HAVE |
| 40 | +/// // to change `do_computation()`. |
| 41 | +/// divisors.retain(|divisor| *divisor != 0) |
| 42 | +/// } |
| 43 | +/// |
| 44 | +/// /// # Safety |
| 45 | +/// /// All elements of `divisor` must be non-zero. |
| 46 | +/// unsafe fn do_computation(i: u32, divisors: &[u32]) -> u32 { |
| 47 | +/// divisors.iter().fold(i, |acc, divisor| { |
| 48 | +/// // Convince the compiler that a division by zero can't happen here |
| 49 | +/// // and a check is not needed below. |
| 50 | +/// if *divisor == 0 { |
| 51 | +/// // Safety: `divisor` can't be zero because of `prepare_inputs`, |
| 52 | +/// // but the compiler does not know about this. We *promise* |
| 53 | +/// // that we always call `prepare_inputs`. |
| 54 | +/// std::hint::unreachable_unchecked() |
| 55 | +/// } |
| 56 | +/// // The compiler would normally introduce a check here that prevents |
| 57 | +/// // a division by zero. However, if `divisor` was zero, the branch |
| 58 | +/// // above would reach what we explicitly marked as unreachable. |
| 59 | +/// // The compiler concludes that `divisor` can't be zero at this point |
| 60 | +/// // and removes the - now proven useless - check. |
| 61 | +/// acc / divisor |
| 62 | +/// }) |
| 63 | +/// } |
| 64 | +/// |
| 65 | +/// let mut divisors = vec![2, 0, 4]; |
| 66 | +/// prepare_inputs(&mut divisors); |
| 67 | +/// let result = unsafe { |
| 68 | +/// // Safety: prepare_inputs() guarantees that divisors is non-zero |
| 69 | +/// do_computation(100, &divisors) |
| 70 | +/// }; |
| 71 | +/// assert_eq!(result, 12); |
| 72 | +/// |
| 73 | +/// ``` |
| 74 | +/// |
| 75 | +/// While using `unreachable_unchecked()` is perfectly sound in the following |
| 76 | +/// example, the compiler is able to prove that a division by zero is not |
| 77 | +/// possible. Benchmarking reveals that `unreachable_unchecked()` provides |
| 78 | +/// no benefit over using [`unreachable!`], while the latter does not introduce |
| 79 | +/// the possibility of Undefined Behavior. |
29 | 80 | ///
|
30 | 81 | /// ```
|
31 | 82 | /// fn div_1(a: u32, b: u32) -> u32 {
|
|
0 commit comments