Skip to content

Commit

Permalink
Auto merge of #97013 - matthiaskrgr:rollup-c1pc6pc, r=matthiaskrgr
Browse files Browse the repository at this point in the history
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
  • Loading branch information
bors committed May 13, 2022
2 parents 9ad4bde + cc357bd commit f1f721e
Show file tree
Hide file tree
Showing 11 changed files with 140 additions and 26 deletions.
4 changes: 3 additions & 1 deletion compiler/rustc_const_eval/src/const_eval/valtrees.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,9 @@ fn slice_branches<'tcx>(
ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
place: &MPlaceTy<'tcx>,
) -> Option<ty::ValTree<'tcx>> {
let n = place.len(&ecx.tcx.tcx).expect(&format!("expected to use len of place {:?}", place));
let n = place
.len(&ecx.tcx.tcx)
.unwrap_or_else(|_| panic!("expected to use len of place {:?}", place));
let branches = (0..n).map(|i| {
let place_elem = ecx.mplace_index(place, i).unwrap();
const_to_valtree_inner(ecx, &place_elem)
Expand Down
1 change: 0 additions & 1 deletion library/alloc/src/raw_vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ impl<T, A: Allocator> RawVec<T, A> {

/// Like `new`, but parameterized over the choice of allocator for
/// the returned `RawVec`.
#[rustc_allow_const_fn_unstable(const_fn)]
pub const fn new_in(alloc: A) -> Self {
// `cap: 0` means "unallocated". zero-sized types are ignored.
Self { ptr: Unique::dangling(), cap: 0, alloc }
Expand Down
81 changes: 66 additions & 15 deletions library/core/src/hint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,78 @@

use crate::intrinsics;

/// Informs the compiler that this point in the code is not reachable, enabling
/// further optimizations.
/// Informs the compiler that the site which is calling this function is not
/// reachable, possibly enabling further optimizations.
///
/// # Safety
///
/// Reaching this function is completely *undefined behavior* (UB). In
/// particular, the compiler assumes that all UB must never happen, and
/// therefore will eliminate all branches that reach to a call to
/// `unreachable_unchecked()`.
/// Reaching this function is *Undefined Behavior*.
///
/// Like all instances of UB, if this assumption turns out to be wrong, i.e., the
/// `unreachable_unchecked()` call is actually reachable among all possible
/// control flow, the compiler will apply the wrong optimization strategy, and
/// may sometimes even corrupt seemingly unrelated code, causing
/// difficult-to-debug problems.
/// As the compiler assumes that all forms of Undefined Behavior can never
/// happen, it will eliminate all branches in the surrounding code that it can
/// determine will invariably lead to a call to `unreachable_unchecked()`.
///
/// Use this function only when you can prove that the code will never call it.
/// Otherwise, consider using the [`unreachable!`] macro, which does not allow
/// optimizations but will panic when executed.
/// If the assumptions embedded in using this function turn out to be wrong -
/// that is, if the site which is calling `unreachable_unchecked()` is actually
/// reachable at runtime - the compiler may have generated nonsensical machine
/// instructions for this situation, including in seemingly unrelated code,
/// causing difficult-to-debug problems.
///
/// # Example
/// Use this function sparingly. Consider using the [`unreachable!`] macro,
/// which may prevent some optimizations but will safely panic in case it is
/// actually reached at runtime. Benchmark your code to find out if using
/// `unreachable_unchecked()` comes with a performance benefit.
///
/// # Examples
///
/// `unreachable_unchecked()` can be used in situations where the compiler
/// can't prove invariants that were previously established. Such situations
/// have a higher chance of occuring if those invariants are upheld by
/// external code that the compiler can't analyze.
/// ```
/// fn prepare_inputs(divisors: &mut Vec<u32>) {
/// // Note to future-self when making changes: The invariant established
/// // here is NOT checked in `do_computation()`; if this changes, you HAVE
/// // to change `do_computation()`.
/// divisors.retain(|divisor| *divisor != 0)
/// }
///
/// /// # Safety
/// /// All elements of `divisor` must be non-zero.
/// unsafe fn do_computation(i: u32, divisors: &[u32]) -> u32 {
/// divisors.iter().fold(i, |acc, divisor| {
/// // Convince the compiler that a division by zero can't happen here
/// // and a check is not needed below.
/// if *divisor == 0 {
/// // Safety: `divisor` can't be zero because of `prepare_inputs`,
/// // but the compiler does not know about this. We *promise*
/// // that we always call `prepare_inputs`.
/// std::hint::unreachable_unchecked()
/// }
/// // The compiler would normally introduce a check here that prevents
/// // a division by zero. However, if `divisor` was zero, the branch
/// // above would reach what we explicitly marked as unreachable.
/// // The compiler concludes that `divisor` can't be zero at this point
/// // and removes the - now proven useless - check.
/// acc / divisor
/// })
/// }
///
/// let mut divisors = vec![2, 0, 4];
/// prepare_inputs(&mut divisors);
/// let result = unsafe {
/// // Safety: prepare_inputs() guarantees that divisors is non-zero
/// do_computation(100, &divisors)
/// };
/// assert_eq!(result, 12);
///
/// ```
///
/// While using `unreachable_unchecked()` is perfectly sound in the following
/// example, the compiler is able to prove that a division by zero is not
/// possible. Benchmarking reveals that `unreachable_unchecked()` provides
/// no benefit over using [`unreachable!`], while the latter does not introduce
/// the possibility of Undefined Behavior.
///
/// ```
/// fn div_1(a: u32, b: u32) -> u32 {
Expand Down
1 change: 0 additions & 1 deletion library/core/src/num/nonzero.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ macro_rules! nonzero_integers {
#[$const_new_unchecked_stability]
#[must_use]
#[inline]
#[rustc_allow_const_fn_unstable(const_fn_fn_ptr_basics)] // required by assert_unsafe_precondition
pub const unsafe fn new_unchecked(n: $Int) -> Self {
// SAFETY: this is guaranteed to be safe by the caller.
unsafe {
Expand Down
1 change: 0 additions & 1 deletion library/core/src/option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1486,7 +1486,6 @@ impl<T> Option<T> {
where
T: ~const Default,
{
#[rustc_allow_const_fn_unstable(const_fn_trait_bound)]
const fn default<T: ~const Default>() -> T {
T::default()
}
Expand Down
1 change: 0 additions & 1 deletion library/core/src/task/wake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,6 @@ impl RawWakerVTable {
#[rustc_promotable]
#[stable(feature = "futures_api", since = "1.36.0")]
#[rustc_const_stable(feature = "futures_api", since = "1.36.0")]
#[rustc_allow_const_fn_unstable(const_fn_fn_ptr_basics)]
pub const fn new(
clone: unsafe fn(*const ()) -> RawWaker,
wake: unsafe fn(*const ()),
Expand Down
5 changes: 0 additions & 5 deletions library/proc_macro/src/bridge/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,6 @@ fn run_client<A: for<'a, 's> DecodeMut<'a, 's, ()>, R: Encode<()>>(
}

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

impl Client<fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream> {
#[rustc_allow_const_fn_unstable(const_fn)]
pub const fn expand2(
f: fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream,
) -> Self {
Expand Down Expand Up @@ -474,7 +472,6 @@ impl ProcMacro {
}
}

#[rustc_allow_const_fn_unstable(const_fn)]
pub const fn custom_derive(
trait_name: &'static str,
attributes: &'static [&'static str],
Expand All @@ -483,15 +480,13 @@ impl ProcMacro {
ProcMacro::CustomDerive { trait_name, attributes, client: Client::expand1(expand) }
}

#[rustc_allow_const_fn_unstable(const_fn)]
pub const fn attr(
name: &'static str,
expand: fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream,
) -> Self {
ProcMacro::Attr { name, client: Client::expand2(expand) }
}

#[rustc_allow_const_fn_unstable(const_fn)]
pub const fn bang(
name: &'static str,
expand: fn(crate::TokenStream) -> crate::TokenStream,
Expand Down
1 change: 0 additions & 1 deletion library/proc_macro/src/bridge/scoped_cell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ impl<'a, 'b, T: LambdaL> DerefMut for RefMutL<'a, 'b, T> {
pub struct ScopedCell<T: LambdaL>(Cell<<T as ApplyL<'static>>::Out>);

impl<T: LambdaL> ScopedCell<T> {
#[rustc_allow_const_fn_unstable(const_fn)]
pub const fn new(value: <T as ApplyL<'static>>::Out) -> Self {
ScopedCell(Cell::new(value))
}
Expand Down
9 changes: 9 additions & 0 deletions src/test/ui/inference/issue-28935.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// check-pass

use std::cell::RefCell;

pub fn f(v: Vec<RefCell<u8>>) {
let _t = &mut *v[0].borrow_mut();
}

fn main() {}
51 changes: 51 additions & 0 deletions src/test/ui/nll/issue-54779-anon-static-lifetime.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Regression test for #54779, checks if the diagnostics are confusing.

#![feature(nll)]

trait DebugWith<Cx: ?Sized> {
fn debug_with<'me>(&'me self, cx: &'me Cx) -> DebugCxPair<'me, Self, Cx> {
DebugCxPair { value: self, cx }
}

fn fmt_with(&self, cx: &Cx, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result;
}

struct DebugCxPair<'me, Value: ?Sized, Cx: ?Sized>
where
Value: DebugWith<Cx>,
{
value: &'me Value,
cx: &'me Cx,
}

trait DebugContext {}

struct Foo {
bar: Bar,
}

impl DebugWith<dyn DebugContext> for Foo {
fn fmt_with(
&self,
cx: &dyn DebugContext,
fmt: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
let Foo { bar } = self;
bar.debug_with(cx); //~ ERROR: lifetime may not live long enough
Ok(())
}
}

struct Bar {}

impl DebugWith<dyn DebugContext> for Bar {
fn fmt_with(
&self,
cx: &dyn DebugContext,
fmt: &mut std::fmt::Formatter<'_>,
) -> std::fmt::Result {
Ok(())
}
}

fn main() {}
11 changes: 11 additions & 0 deletions src/test/ui/nll/issue-54779-anon-static-lifetime.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
error: lifetime may not live long enough
--> $DIR/issue-54779-anon-static-lifetime.rs:34:24
|
LL | cx: &dyn DebugContext,
| - let's call the lifetime of this reference `'1`
...
LL | bar.debug_with(cx);
| ^^ cast requires that `'1` must outlive `'static`

error: aborting due to previous error

0 comments on commit f1f721e

Please sign in to comment.