Skip to content

Commit 944f352

Browse files
committed
Auto merge of #97013 - matthiaskrgr:rollup-c1pc6pc, r=matthiaskrgr
Rollup of 5 pull requests Successful merges: - #96154 (Expand core::hint::unreachable_unchecked() docs) - #96615 (Add a regression test for #54779) - #96982 (fix clippy expect_fun_call) - #97003 (Remove some unnecessary `rustc_allow_const_fn_unstable` attributes.) - #97011 (Add regression test for #28935) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
2 parents fcb518d + 552f2d4 commit 944f352

File tree

7 files changed

+66
-25
lines changed

7 files changed

+66
-25
lines changed

alloc/src/raw_vec.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,6 @@ impl<T, A: Allocator> RawVec<T, A> {
118118

119119
/// Like `new`, but parameterized over the choice of allocator for
120120
/// the returned `RawVec`.
121-
#[rustc_allow_const_fn_unstable(const_fn)]
122121
pub const fn new_in(alloc: A) -> Self {
123122
// `cap: 0` means "unallocated". zero-sized types are ignored.
124123
Self { ptr: Unique::dangling(), cap: 0, alloc }

core/src/hint.rs

Lines changed: 66 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,78 @@
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
16-
/// `unreachable_unchecked()`.
13+
/// Reaching this function is *Undefined Behavior*.
1714
///
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()`.
2318
///
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.
2724
///
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.
2980
///
3081
/// ```
3182
/// fn div_1(a: u32, b: u32) -> u32 {

core/src/num/nonzero.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ macro_rules! nonzero_integers {
5252
#[$const_new_unchecked_stability]
5353
#[must_use]
5454
#[inline]
55-
#[rustc_allow_const_fn_unstable(const_fn_fn_ptr_basics)] // required by assert_unsafe_precondition
5655
pub const unsafe fn new_unchecked(n: $Int) -> Self {
5756
// SAFETY: this is guaranteed to be safe by the caller.
5857
unsafe {

core/src/option.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1486,7 +1486,6 @@ impl<T> Option<T> {
14861486
where
14871487
T: ~const Default,
14881488
{
1489-
#[rustc_allow_const_fn_unstable(const_fn_trait_bound)]
14901489
const fn default<T: ~const Default>() -> T {
14911490
T::default()
14921491
}

core/src/task/wake.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,6 @@ impl RawWakerVTable {
147147
#[rustc_promotable]
148148
#[stable(feature = "futures_api", since = "1.36.0")]
149149
#[rustc_const_stable(feature = "futures_api", since = "1.36.0")]
150-
#[rustc_allow_const_fn_unstable(const_fn_fn_ptr_basics)]
151150
pub const fn new(
152151
clone: unsafe fn(*const ()) -> RawWaker,
153152
wake: unsafe fn(*const ()),

proc_macro/src/bridge/client.rs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,6 @@ fn run_client<A: for<'a, 's> DecodeMut<'a, 's, ()>, R: Encode<()>>(
416416
}
417417

418418
impl Client<fn(crate::TokenStream) -> crate::TokenStream> {
419-
#[rustc_allow_const_fn_unstable(const_fn)]
420419
pub const fn expand1(f: fn(crate::TokenStream) -> crate::TokenStream) -> Self {
421420
extern "C" fn run(
422421
bridge: Bridge<'_>,
@@ -429,7 +428,6 @@ impl Client<fn(crate::TokenStream) -> crate::TokenStream> {
429428
}
430429

431430
impl Client<fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream> {
432-
#[rustc_allow_const_fn_unstable(const_fn)]
433431
pub const fn expand2(
434432
f: fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream,
435433
) -> Self {
@@ -474,7 +472,6 @@ impl ProcMacro {
474472
}
475473
}
476474

477-
#[rustc_allow_const_fn_unstable(const_fn)]
478475
pub const fn custom_derive(
479476
trait_name: &'static str,
480477
attributes: &'static [&'static str],
@@ -483,15 +480,13 @@ impl ProcMacro {
483480
ProcMacro::CustomDerive { trait_name, attributes, client: Client::expand1(expand) }
484481
}
485482

486-
#[rustc_allow_const_fn_unstable(const_fn)]
487483
pub const fn attr(
488484
name: &'static str,
489485
expand: fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream,
490486
) -> Self {
491487
ProcMacro::Attr { name, client: Client::expand2(expand) }
492488
}
493489

494-
#[rustc_allow_const_fn_unstable(const_fn)]
495490
pub const fn bang(
496491
name: &'static str,
497492
expand: fn(crate::TokenStream) -> crate::TokenStream,

proc_macro/src/bridge/scoped_cell.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ impl<'a, 'b, T: LambdaL> DerefMut for RefMutL<'a, 'b, T> {
3535
pub struct ScopedCell<T: LambdaL>(Cell<<T as ApplyL<'static>>::Out>);
3636

3737
impl<T: LambdaL> ScopedCell<T> {
38-
#[rustc_allow_const_fn_unstable(const_fn)]
3938
pub const fn new(value: <T as ApplyL<'static>>::Out) -> Self {
4039
ScopedCell(Cell::new(value))
4140
}

0 commit comments

Comments
 (0)