Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 66 additions & 5 deletions compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3352,8 +3352,35 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
proper_span: Span,
explanation: BorrowExplanation<'tcx>,
) -> Diag<'infcx> {
if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
explanation
// Emit E0492 for a shared `&const { expr }` borrow when `expr` has
// interior mutability, since that's what actually prevents promotion.
let interior_mut_const_span = if let Some(expr) = self.find_expr(proper_span)
&& let hir::ExprKind::ConstBlock(const_block) = expr.kind
&& matches!(borrow.kind(), BorrowKind::Shared)
{
let borrowed_ty = self.body.local_decls[borrow.borrowed_place.local].ty;
let typing_env = self.infcx.typing_env(self.infcx.param_env);
let tcx = self.infcx.tcx;
if !borrowed_ty.is_freeze(tcx, typing_env) {
let body_expr = tcx.hir_body(const_block.body).value;
let inner_span = if let hir::ExprKind::Block(block, _) = body_expr.kind
&& let Some(tail_expr) = block.expr
{
tail_expr.span
} else {
body_expr.span
};
Some(inner_span)
} else {
None
}
} else {
None
};

if interior_mut_const_span.is_none()
&& let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
explanation
{
if let Err(diag) = self.try_report_cannot_return_reference_to_local(
borrow,
Expand All @@ -3366,9 +3393,43 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
}
}

let mut err = self.temporary_value_borrowed_for_too_long(proper_span);
err.span_label(proper_span, "creates a temporary value which is freed while still in use");
err.span_label(drop_span, "temporary value is freed at the end of this statement");
let mut err = if let Some(inner_span) = interior_mut_const_span {
// FIXME(#154810): this message is duplicated from
// `rustc_const_eval::errors::InteriorMutableBorrowEscaping`. Deduplicate once the
// diagnostic struct can be shared across the borrowck/const_eval crate boundary.
let mut err = struct_span_code_err!(
self.dcx(),
inner_span,
E0492,
"interior mutable shared borrows of temporaries that have their \
lifetime extended until the end of the program are not allowed"
);
err.span_label(
inner_span,
"this borrow of an interior mutable value refers to such a temporary",
);
err.note(
"temporaries in constants and statics can have their lifetime \
extended until the end of the program",
);
err.note(
"to avoid accidentally creating global mutable state, such \
temporaries must be immutable",
);
err.help(
"if you really want global mutable state, try replacing the \
temporary by an interior mutable `static` or a `static mut`",
);
err
} else {
let mut err = self.temporary_value_borrowed_for_too_long(proper_span);
err.span_label(
proper_span,
"creates a temporary value which is freed while still in use",
);
err.span_label(drop_span, "temporary value is freed at the end of this statement");
err
};

match explanation {
BorrowExplanation::UsedLater(..)
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_const_eval/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,10 @@ pub(crate) struct UnallowedInlineAsm {
pub kind: ConstContext,
}

// FIXME(#154810): this message is duplicated in
// `rustc_borrowck::diagnostics::conflict_errors`
// (`report_temporary_value_does_not_live_long_enough`). Keep the two in sync until the
// diagnostic can be shared across the crate boundary.
#[derive(Diagnostic)]
#[diag("interior mutable shared borrows of temporaries that have their lifetime extended until the end of the program are not allowed", code = E0492)]
#[note(
Expand Down
64 changes: 64 additions & 0 deletions tests/ui/inline-const/interior-mutable-borrow-issue-154382.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Verify that `&const { expr }` where `expr` has interior mutability
// gets E0492 instead of E0716.
// https://github.com/rust-lang/rust/issues/154382

use std::cell::Cell;
use std::sync::atomic::AtomicUsize;

struct Mutable(Cell<u32>);
impl Mutable {
const fn new(a: u32) -> Self { Self(Cell::new(a)) }
}

fn foo() -> &'static Mutable {
&const { Mutable::new(0) }
//~^ ERROR interior mutable shared borrows of temporaries
}

struct Holder {
val: &'static Mutable,
}

fn takes_static(_: &'static Mutable) {}

fn via_closure() {
let _f: fn() -> &'static Mutable = || &const { Mutable::new(0) };
//~^ ERROR interior mutable shared borrows of temporaries
}

fn via_struct() {
let _h = Holder { val: &const { Mutable::new(0) } };
//~^ ERROR interior mutable shared borrows of temporaries
}

fn via_argument() {
takes_static(&const { Mutable::new(0) });
//~^ ERROR interior mutable shared borrows of temporaries
}

// The `let` binding suggestion should still be offered alongside E0492.
fn deferred_assignment() {
let r;
r = &const { Cell::new(0) };
//~^ ERROR interior mutable shared borrows of temporaries
r.set(1);
}

// A `&mut` borrow is not a shared borrow, so it keeps the E0716 diagnostic.
fn mut_borrow() {
let _: &'static mut _ = &mut const { Cell::new(0) };
//~^ ERROR temporary value dropped while borrowed
}

fn main() {
let _: &'static _ = &const { 0u32 };

let _: &'static _ = &const { Mutable::new(0u32) };
//~^ ERROR interior mutable shared borrows of temporaries

let _: &'static _ = const { &Mutable::new(0u32) };
//~^ ERROR interior mutable shared borrows of temporaries

let _: &'static _ = &const { AtomicUsize::new(0) };
//~^ ERROR interior mutable shared borrows of temporaries
}
120 changes: 120 additions & 0 deletions tests/ui/inline-const/interior-mutable-borrow-issue-154382.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
error[E0492]: interior mutable shared borrows of temporaries that have their lifetime extended until the end of the program are not allowed
--> $DIR/interior-mutable-borrow-issue-154382.rs:14:14
|
LL | &const { Mutable::new(0) }
| ---------^^^^^^^^^^^^^^^--
| | |
| | this borrow of an interior mutable value refers to such a temporary
| returning this value requires that borrow lasts for `'static`
|
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`

error[E0492]: interior mutable shared borrows of temporaries that have their lifetime extended until the end of the program are not allowed
--> $DIR/interior-mutable-borrow-issue-154382.rs:25:52
|
LL | let _f: fn() -> &'static Mutable = || &const { Mutable::new(0) };
| - ---------^^^^^^^^^^^^^^^--
| | | |
| | | this borrow of an interior mutable value refers to such a temporary
| | returning this value requires that borrow lasts for `'1`
| return type of closure is &'1 Mutable
|
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`

error[E0492]: interior mutable shared borrows of temporaries that have their lifetime extended until the end of the program are not allowed
--> $DIR/interior-mutable-borrow-issue-154382.rs:30:37
|
LL | let _h = Holder { val: &const { Mutable::new(0) } };
| ---------^^^^^^^^^^^^^^^--
| | |
| | this borrow of an interior mutable value refers to such a temporary
| this usage requires that borrow lasts for `'static`
|
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`

error[E0492]: interior mutable shared borrows of temporaries that have their lifetime extended until the end of the program are not allowed
--> $DIR/interior-mutable-borrow-issue-154382.rs:35:27
|
LL | takes_static(&const { Mutable::new(0) });
| ----------------------^^^^^^^^^^^^^^^---
| | |
| | this borrow of an interior mutable value refers to such a temporary
| argument requires that borrow lasts for `'static`
|
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`

error[E0492]: interior mutable shared borrows of temporaries that have their lifetime extended until the end of the program are not allowed
--> $DIR/interior-mutable-borrow-issue-154382.rs:42:18
|
LL | r = &const { Cell::new(0) };
| ^^^^^^^^^^^^ this borrow of an interior mutable value refers to such a temporary
LL |
LL | r.set(1);
| - borrow later used here
|
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`
help: consider using a `let` binding to create a longer lived value
|
LL ~ let binding = const { Cell::new(0) };
LL ~ r = &binding;
|

error[E0716]: temporary value dropped while borrowed
--> $DIR/interior-mutable-borrow-issue-154382.rs:49:34
|
LL | let _: &'static mut _ = &mut const { Cell::new(0) };
| -------------- ^^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use
| |
| type annotation requires that borrow lasts for `'static`
LL |
LL | }
| - temporary value is freed at the end of this statement

error[E0492]: interior mutable shared borrows of temporaries that have their lifetime extended until the end of the program are not allowed
--> $DIR/interior-mutable-borrow-issue-154382.rs:59:33
|
LL | let _: &'static _ = const { &Mutable::new(0u32) };
| ^^^^^^^^^^^^^^^^^^^ this borrow of an interior mutable value refers to such a temporary
|
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`

error[E0492]: interior mutable shared borrows of temporaries that have their lifetime extended until the end of the program are not allowed
--> $DIR/interior-mutable-borrow-issue-154382.rs:56:34
|
LL | let _: &'static _ = &const { Mutable::new(0u32) };
| ---------- ^^^^^^^^^^^^^^^^^^ this borrow of an interior mutable value refers to such a temporary
| |
| type annotation requires that borrow lasts for `'static`
|
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`

error[E0492]: interior mutable shared borrows of temporaries that have their lifetime extended until the end of the program are not allowed
--> $DIR/interior-mutable-borrow-issue-154382.rs:62:34
|
LL | let _: &'static _ = &const { AtomicUsize::new(0) };
| ---------- ^^^^^^^^^^^^^^^^^^^ this borrow of an interior mutable value refers to such a temporary
| |
| type annotation requires that borrow lasts for `'static`
|
= note: temporaries in constants and statics can have their lifetime extended until the end of the program
= note: to avoid accidentally creating global mutable state, such temporaries must be immutable
= help: if you really want global mutable state, try replacing the temporary by an interior mutable `static` or a `static mut`

error: aborting due to 9 previous errors

Some errors have detailed explanations: E0492, E0716.
For more information about an error, try `rustc --explain E0492`.
Loading