Skip to content

const checks for lifetime-extended temporaries: avoid 'top-level scope' terminology #143092

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 28, 2025
Merged
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
28 changes: 10 additions & 18 deletions compiler/rustc_const_eval/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -125,16 +125,11 @@ const_eval_incompatible_types =
calling a function with argument of type {$callee_ty} passing data of type {$caller_ty}

const_eval_interior_mutable_borrow_escaping =
interior mutable shared borrows of lifetime-extended temporaries in the top-level scope of a {const_eval_const_context} are not allowed
.label = this borrow of an interior mutable value refers to a lifetime-extended temporary
.help = to fix this, the value can be extracted to a separate `static` item and then referenced
.teach_note =
This creates a raw pointer to a temporary that has its lifetime extended to last for the entire program.
Lifetime-extended temporaries in constants and statics must be immutable.
This is to avoid accidentally creating shared mutable state.


If you really want global mutable state, try using an interior mutable `static` or a `static mut`.
interior mutable shared borrows of temporaries that have their lifetime extended until the end of the program are not allowed
.label = 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
.note2 = 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`

const_eval_intern_kind = {$kind ->
[static] static
Expand Down Expand Up @@ -215,14 +210,11 @@ const_eval_modified_global =
modifying a static's initial value from another static's initializer

const_eval_mutable_borrow_escaping =
mutable borrows of lifetime-extended temporaries in the top-level scope of a {const_eval_const_context} are not allowed
.teach_note =
This creates a reference to a temporary that has its lifetime extended to last for the entire program.
Lifetime-extended temporaries in constants and statics must be immutable.
This is to avoid accidentally creating shared mutable state.


If you really want global mutable state, try using an interior mutable `static` or a `static mut`.
mutable borrows of temporaries that have their lifetime extended until the end of the program are not allowed
.label = this mutable borrow refers to such a temporary
.note = Temporaries in constants and statics can have their lifetime extended until the end of the program
.note2 = 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`

const_eval_mutable_ptr_in_final = encountered mutable pointer in final value of {const_eval_intern_kind}

Expand Down
13 changes: 2 additions & 11 deletions compiler/rustc_const_eval/src/check_consts/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -567,12 +567,7 @@ impl<'tcx> NonConstOp<'tcx> for EscapingCellBorrow {
DiagImportance::Secondary
}
fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
ccx.dcx().create_err(errors::InteriorMutableBorrowEscaping {
span,
opt_help: matches!(ccx.const_kind(), hir::ConstContext::Static(_)),
kind: ccx.const_kind(),
teach: ccx.tcx.sess.teach(E0492),
})
ccx.dcx().create_err(errors::InteriorMutableBorrowEscaping { span, kind: ccx.const_kind() })
}
}

Expand All @@ -594,11 +589,7 @@ impl<'tcx> NonConstOp<'tcx> for EscapingMutBorrow {
}

fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> {
ccx.dcx().create_err(errors::MutableBorrowEscaping {
span,
kind: ccx.const_kind(),
teach: ccx.tcx.sess.teach(E0764),
})
ccx.dcx().create_err(errors::MutableBorrowEscaping { span, kind: ccx.const_kind() })
}
}

Expand Down
13 changes: 7 additions & 6 deletions compiler/rustc_const_eval/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -151,12 +151,14 @@ pub(crate) struct UnmarkedIntrinsicExposed {

#[derive(Diagnostic)]
#[diag(const_eval_mutable_borrow_escaping, code = E0764)]
#[note]
#[note(const_eval_note2)]
#[help]
pub(crate) struct MutableBorrowEscaping {
#[primary_span]
#[label]
pub span: Span,
pub kind: ConstContext,
#[note(const_eval_teach_note)]
pub teach: bool,
}

#[derive(Diagnostic)]
Expand Down Expand Up @@ -217,15 +219,14 @@ pub(crate) struct UnallowedInlineAsm {

#[derive(Diagnostic)]
#[diag(const_eval_interior_mutable_borrow_escaping, code = E0492)]
#[note]
#[note(const_eval_note2)]
#[help]
pub(crate) struct InteriorMutableBorrowEscaping {
#[primary_span]
#[label]
pub span: Span,
#[help]
pub opt_help: bool,
pub kind: ConstContext,
#[note(const_eval_teach_note)]
pub teach: bool,
}

#[derive(LintDiagnostic)]
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/consts/const-mut-refs/issue-76510.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::mem::{transmute, ManuallyDrop};

const S: &'static mut str = &mut " hello ";
//~^ ERROR: mutable borrows of lifetime-extended temporaries
//~^ ERROR: mutable borrows of temporaries

const fn trigger() -> [(); unsafe {
let s = transmute::<(*const u8, usize), &ManuallyDrop<str>>((S.as_ptr(), 3));
Expand Down
8 changes: 6 additions & 2 deletions tests/ui/consts/const-mut-refs/issue-76510.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
error[E0764]: mutable borrows of lifetime-extended temporaries in the top-level scope of a constant are not allowed
error[E0764]: mutable borrows of temporaries that have their lifetime extended until the end of the program are not allowed
--> $DIR/issue-76510.rs:3:29
|
LL | const S: &'static mut str = &mut " hello ";
| ^^^^^^^^^^^^^^
| ^^^^^^^^^^^^^^ this mutable borrow 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 1 previous error

Expand Down
12 changes: 6 additions & 6 deletions tests/ui/consts/const-mut-refs/mut_ref_in_final.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ const A: *const i32 = &4;
// It could be made sound to allow it to compile,
// but we do not want to allow this to compile,
// as that would be an enormous footgun in oli-obk's opinion.
const B: *mut i32 = &mut 4; //~ ERROR mutable borrows of lifetime-extended temporaries
const B: *mut i32 = &mut 4; //~ ERROR mutable borrows of temporaries

// Ok, no actual mutable allocation exists
const B2: Option<&mut i32> = None;

// Not ok, can't prove that no mutable allocation ends up in final value
const B3: Option<&mut i32> = Some(&mut 42); //~ ERROR mutable borrows of lifetime-extended temporaries
const B3: Option<&mut i32> = Some(&mut 42); //~ ERROR mutable borrows of temporaries

const fn helper(x: &mut i32) -> Option<&mut i32> { Some(x) }
const B4: Option<&mut i32> = helper(&mut 42); //~ ERROR temporary value dropped while borrowed
Expand Down Expand Up @@ -69,13 +69,13 @@ unsafe impl<T> Sync for SyncPtr<T> {}
// (This relies on `SyncPtr` being a curly brace struct.)
// However, we intern the inner memory as read-only, so this must be rejected.
static RAW_MUT_CAST_S: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
//~^ ERROR mutable borrows of lifetime-extended temporaries
//~^ ERROR mutable borrows of temporaries
static RAW_MUT_COERCE_S: SyncPtr<i32> = SyncPtr { x: &mut 0 };
//~^ ERROR mutable borrows of lifetime-extended temporaries
//~^ ERROR mutable borrows of temporaries
const RAW_MUT_CAST_C: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
//~^ ERROR mutable borrows of lifetime-extended temporaries
//~^ ERROR mutable borrows of temporaries
const RAW_MUT_COERCE_C: SyncPtr<i32> = SyncPtr { x: &mut 0 };
//~^ ERROR mutable borrows of lifetime-extended temporaries
//~^ ERROR mutable borrows of temporaries

fn main() {
println!("{}", unsafe { *A });
Expand Down
48 changes: 36 additions & 12 deletions tests/ui/consts/const-mut-refs/mut_ref_in_final.stderr
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
error[E0764]: mutable borrows of lifetime-extended temporaries in the top-level scope of a constant are not allowed
error[E0764]: mutable borrows of temporaries that have their lifetime extended until the end of the program are not allowed
--> $DIR/mut_ref_in_final.rs:15:21
|
LL | const B: *mut i32 = &mut 4;
| ^^^^^^
| ^^^^^^ this mutable borrow 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[E0764]: mutable borrows of lifetime-extended temporaries in the top-level scope of a constant are not allowed
error[E0764]: mutable borrows of temporaries that have their lifetime extended until the end of the program are not allowed
--> $DIR/mut_ref_in_final.rs:21:35
|
LL | const B3: Option<&mut i32> = Some(&mut 42);
| ^^^^^^^
| ^^^^^^^ this mutable borrow 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[E0716]: temporary value dropped while borrowed
--> $DIR/mut_ref_in_final.rs:24:42
Expand Down Expand Up @@ -72,29 +80,45 @@ LL | static mut FOO3: NotAMutex<&mut i32> = NotAMutex(UnsafeCell::new(&mut 42));
| | creates a temporary value which is freed while still in use
| using this value as a static requires that borrow lasts for `'static`

error[E0764]: mutable borrows of lifetime-extended temporaries in the top-level scope of a static are not allowed
error[E0764]: mutable borrows of temporaries that have their lifetime extended until the end of the program are not allowed
--> $DIR/mut_ref_in_final.rs:71:53
|
LL | static RAW_MUT_CAST_S: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
| ^^^^^^^
| ^^^^^^^ this mutable borrow 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[E0764]: mutable borrows of lifetime-extended temporaries in the top-level scope of a static are not allowed
error[E0764]: mutable borrows of temporaries that have their lifetime extended until the end of the program are not allowed
--> $DIR/mut_ref_in_final.rs:73:54
|
LL | static RAW_MUT_COERCE_S: SyncPtr<i32> = SyncPtr { x: &mut 0 };
| ^^^^^^
| ^^^^^^ this mutable borrow 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[E0764]: mutable borrows of lifetime-extended temporaries in the top-level scope of a constant are not allowed
error[E0764]: mutable borrows of temporaries that have their lifetime extended until the end of the program are not allowed
--> $DIR/mut_ref_in_final.rs:75:52
|
LL | const RAW_MUT_CAST_C: SyncPtr<i32> = SyncPtr { x : &mut 42 as *mut _ as *const _ };
| ^^^^^^^
| ^^^^^^^ this mutable borrow 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[E0764]: mutable borrows of lifetime-extended temporaries in the top-level scope of a constant are not allowed
error[E0764]: mutable borrows of temporaries that have their lifetime extended until the end of the program are not allowed
--> $DIR/mut_ref_in_final.rs:77:53
|
LL | const RAW_MUT_COERCE_C: SyncPtr<i32> = SyncPtr { x: &mut 0 };
| ^^^^^^
| ^^^^^^ this mutable borrow 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 12 previous errors

Expand Down
8 changes: 6 additions & 2 deletions tests/ui/consts/const-promoted-opaque.atomic.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@ LL |
LL | };
| - value is dropped here

error[E0492]: interior mutable shared borrows of lifetime-extended temporaries in the top-level scope of a constant are not allowed
error[E0492]: interior mutable shared borrows of temporaries that have their lifetime extended until the end of the program are not allowed
--> $DIR/const-promoted-opaque.rs:36:19
|
LL | const BAZ: &Foo = &FOO;
| ^^^^ this borrow of an interior mutable value refers to a lifetime-extended temporary
| ^^^^ 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[E0716]: temporary value dropped while borrowed
--> $DIR/const-promoted-opaque.rs:40:26
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/consts/const-promoted-opaque.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const BAR: () = {
};

const BAZ: &Foo = &FOO;
//[atomic]~^ ERROR: interior mutable shared borrows of lifetime-extended temporaries
//[atomic]~^ ERROR: interior mutable shared borrows of temporaries

fn main() {
let _: &'static _ = &FOO;
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/consts/issue-17718-const-bad-values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#![allow(static_mut_refs)]

const C1: &'static mut [usize] = &mut [];
//~^ ERROR: mutable borrows of lifetime-extended temporaries
//~^ ERROR: mutable borrows of temporaries

static mut S: i32 = 3;
const C2: &'static mut i32 = unsafe { &mut S };
Expand Down
8 changes: 6 additions & 2 deletions tests/ui/consts/issue-17718-const-bad-values.stderr
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
error[E0764]: mutable borrows of lifetime-extended temporaries in the top-level scope of a constant are not allowed
error[E0764]: mutable borrows of temporaries that have their lifetime extended until the end of the program are not allowed
--> $DIR/issue-17718-const-bad-values.rs:7:34
|
LL | const C1: &'static mut [usize] = &mut [];
| ^^^^^^^
| ^^^^^^^ this mutable borrow 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[E0080]: constructing invalid value: encountered mutable reference in `const` value
--> $DIR/issue-17718-const-bad-values.rs:11:1
Expand Down
6 changes: 3 additions & 3 deletions tests/ui/consts/issue-17718-const-borrow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ use std::cell::UnsafeCell;

const A: UnsafeCell<usize> = UnsafeCell::new(1);
const B: &'static UnsafeCell<usize> = &A;
//~^ ERROR: interior mutable shared borrows of lifetime-extended temporaries
//~^ ERROR: interior mutable shared borrows of temporaries

struct C { a: UnsafeCell<usize> }
const D: C = C { a: UnsafeCell::new(1) };
const E: &'static UnsafeCell<usize> = &D.a;
//~^ ERROR: interior mutable shared borrows of lifetime-extended temporaries
//~^ ERROR: interior mutable shared borrows of temporaries
const F: &'static C = &D;
//~^ ERROR: interior mutable shared borrows of lifetime-extended temporaries
//~^ ERROR: interior mutable shared borrows of temporaries

fn main() {}
24 changes: 18 additions & 6 deletions tests/ui/consts/issue-17718-const-borrow.stderr
Original file line number Diff line number Diff line change
@@ -1,20 +1,32 @@
error[E0492]: interior mutable shared borrows of lifetime-extended temporaries in the top-level scope of a constant are not allowed
error[E0492]: interior mutable shared borrows of temporaries that have their lifetime extended until the end of the program are not allowed
--> $DIR/issue-17718-const-borrow.rs:4:39
|
LL | const B: &'static UnsafeCell<usize> = &A;
| ^^ this borrow of an interior mutable value refers to a lifetime-extended temporary
| ^^ 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 lifetime-extended temporaries in the top-level scope of a constant are not allowed
error[E0492]: interior mutable shared borrows of temporaries that have their lifetime extended until the end of the program are not allowed
--> $DIR/issue-17718-const-borrow.rs:9:39
|
LL | const E: &'static UnsafeCell<usize> = &D.a;
| ^^^^ this borrow of an interior mutable value refers to a lifetime-extended temporary
| ^^^^ 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 lifetime-extended temporaries in the top-level scope of a constant are not allowed
error[E0492]: interior mutable shared borrows of temporaries that have their lifetime extended until the end of the program are not allowed
--> $DIR/issue-17718-const-borrow.rs:11:23
|
LL | const F: &'static C = &D;
| ^^ this borrow of an interior mutable value refers to a lifetime-extended temporary
| ^^ 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 3 previous errors

Expand Down
2 changes: 1 addition & 1 deletion tests/ui/consts/partial_qualif.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::cell::Cell;
const FOO: &(Cell<usize>, bool) = {
let mut a = (Cell::new(0), false);
a.1 = true; // sets `qualif(a)` to `qualif(a) | qualif(true)`
&{a} //~ ERROR interior mutable shared borrows of lifetime-extended temporaries
&{a} //~ ERROR interior mutable shared borrows of temporaries
};

fn main() {}
Loading
Loading