diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index da472a08e7e40..6564729d3621d 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -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, @@ -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(..) diff --git a/compiler/rustc_const_eval/src/errors.rs b/compiler/rustc_const_eval/src/errors.rs index 9faf9a59fc22a..bde84f0430eaf 100644 --- a/compiler/rustc_const_eval/src/errors.rs +++ b/compiler/rustc_const_eval/src/errors.rs @@ -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( diff --git a/tests/ui/inline-const/interior-mutable-borrow-issue-154382.rs b/tests/ui/inline-const/interior-mutable-borrow-issue-154382.rs new file mode 100644 index 0000000000000..639d7d3b3edf8 --- /dev/null +++ b/tests/ui/inline-const/interior-mutable-borrow-issue-154382.rs @@ -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); +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 +} diff --git a/tests/ui/inline-const/interior-mutable-borrow-issue-154382.stderr b/tests/ui/inline-const/interior-mutable-borrow-issue-154382.stderr new file mode 100644 index 0000000000000..d33eeb87b4095 --- /dev/null +++ b/tests/ui/inline-const/interior-mutable-borrow-issue-154382.stderr @@ -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`.