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
44 changes: 44 additions & 0 deletions compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3293,6 +3293,50 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
proper_span: Span,
explanation: BorrowExplanation<'tcx>,
) -> Diag<'infcx> {
// Emit E0492 for `&const { expr }` when `expr` has
// interior mutability, since that's what actually prevents promotion.
if let Some(expr) = self.find_expr(proper_span)
Copy link
Copy Markdown
Member

@JohnTitor JohnTitor Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

View changes since the review

I guess we could expand the fix for a similar case where promotion failed because the value is not Freeze, like:

use std::cell::Cell;

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

fn foo() -> &'static Mutable {
  &const { Mutable::new(0) }
}

fn main() {}

The current only addresses the case issue mentioned, I guess it's kinda ad hoc. These cases should have the same diagnostics for consistency.

&& let hir::ExprKind::ConstBlock(const_block) = expr.kind
{
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
};
let mut err = struct_span_code_err!(
self.dcx(),
inner_span,
E0492,
"interior mutable shared borrows of temporaries that have their \
Copy link
Copy Markdown
Member

@JohnTitor JohnTitor Apr 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

View changes since the review

I don't think it's a good idea to duplicate the message with

#[diag("interior mutable shared borrows of temporaries that have their lifetime extended until the end of the program are not allowed", code = E0492)]

Copy link
Copy Markdown
Author

@Hiryxx Hiryxx Apr 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I totally agree. Though the borrow checker does not depend on rust_const_eval, what do you do in these situations? Should I move that under rustc_middle so that both can see that?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JohnTitor I was trying with that but rustc_middle has some errors definitions that are only used inside that module, so I am not sure it would be a good fit. I could also make the borrowchecker depend on const_evals but it seems an overkill for a single diagnostic structure, what do you think? Do you have a place in mind where you would put that struct without needing an "invasive" change.

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`",
);
return err;
}
}

if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
explanation
{
Expand Down
50 changes: 50 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,50 @@
// 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
}

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
}
73 changes: 73 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,73 @@
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
|
= 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
|
= 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
|
= 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
|
= 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:45: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:42:34
|
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:48:34
|
LL | let _: &'static _ = &const { AtomicUsize::new(0) };
| ^^^^^^^^^^^^^^^^^^^ 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: aborting due to 7 previous errors

For more information about this error, try `rustc --explain E0492`.
Loading