From a87322def5a36a19ccfa0efc994fb32991127201 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Sat, 8 Nov 2025 06:04:25 +0000 Subject: [PATCH 1/3] Clarify const item text WRT mutable statics The rule is that "mutable references contained within a mutable static may be referenced in the final value of a constant." However, the key here is that "mutable static" means either a `static mut` item or a `static` item with an interior mutable type. We had made that clear over in `const-eval.const-expr.path-static` but not in our rules about const item validity, and our link for "mutable static" had implied it may mean `static mut`. Let's fix that and add an example to demonstrate the point. --- src/items/constant-items.md | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/items/constant-items.md b/src/items/constant-items.md index 321894a944..3d9d044ce4 100644 --- a/src/items/constant-items.md +++ b/src/items/constant-items.md @@ -133,9 +133,9 @@ const _: U = unsafe { U { f: &mut S }}; // OK. // This is treated as a sequence of untyped bytes. ``` -Mutable references contained within a [mutable static] may be referenced in the final value of a constant. +Mutable references contained within a mutable `static` may be referenced in the final value of a constant. A mutable `static` is a [`static mut`] item or a [`static`] item with an [interior mutable] type. -```rust +```rust,no_run # #![allow(static_mut_refs)] static mut S: &mut u8 = unsafe { static mut I: u8 = 0; &mut I }; const _: &&mut u8 = unsafe { &S }; // OK. @@ -143,6 +143,21 @@ const _: &&mut u8 = unsafe { &S }; // OK. // This mutable reference comes from a `static mut`. ``` +```rust,no_run +# #![allow(static_mut_refs)] +# use core::cell::UnsafeCell; +struct PhantomCell { _m: UnsafeCell<()> } +unsafe impl Sync for PhantomCell {} +static S: (&mut u8, Option) = { + static mut I: u8 = 0; + (unsafe { &mut I }, None) +}; +const _: &&mut u8 = &S.0; +// ^^^^^^^ +// This mutable reference comes from a `static` with an +// interior mutable type. +``` + > [!NOTE] > This is allowed as it's separately not allowed to read from a mutable static during constant evaluation. See [const-eval.const-expr.path-static]. @@ -251,17 +266,19 @@ fn unused_generic_function() { } ``` +[`static mut`]: items.static.mut +[`static`]: items.static [const_eval]: ../const_eval.md [associated constant]: ../items/associated-items.md#associated-constants [constant value]: ../const_eval.md#constant-expressions [external static]: items.extern.static [free]: ../glossary.md#free-item +[interior mutable]: interior-mut [static lifetime elision]: ../lifetime-elision.md#const-and-static-elision [trait definition]: traits.md [underscore imports]: use-declarations.md#underscore-imports [`Copy`]: ../special-types-and-traits.md#copy [value namespace]: ../names/namespaces.md -[mutable static]: items.static.mut [promoted]: destructors.scope.const-promotion [promotion]: destructors.scope.const-promotion [union type]: type.union From 95974736943751458ff8e98e94e03ab43646e807 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 9 Nov 2025 14:41:03 +0100 Subject: [PATCH 2/3] get rid of const.no-mut-refs --- src/items/constant-items.md | 146 ------------------------------------ 1 file changed, 146 deletions(-) diff --git a/src/items/constant-items.md b/src/items/constant-items.md index 3d9d044ce4..a151847146 100644 --- a/src/items/constant-items.md +++ b/src/items/constant-items.md @@ -48,152 +48,6 @@ const BITS_N_STRINGS: BitsNStrings<'static> = BitsNStrings { }; ``` -r[items.const.no-mut-refs] -The final value of a `const` item, after the initializer is evaluated to a value that has the declared type of the constant, cannot contain any mutable references except as described below. - -```rust -# #![allow(static_mut_refs)] -static mut S: u8 = 0; -const _: &u8 = unsafe { &mut S }; // OK. -// ^^^^^^ -// Allowed since this is coerced to `&S`. -``` - -```rust -# use core::sync::atomic::AtomicU8; -static S: AtomicU8 = AtomicU8::new(0); -const _: &AtomicU8 = &S; // OK. -// ^^ -// Allowed even though the shared reference is to an interior -// mutable value. -``` - -```rust,compile_fail,E0080 -# #![allow(static_mut_refs)] -static mut S: u8 = 0; -const _: &mut u8 = unsafe { &mut S }; // ERROR. -// ^^^^^^ -// Not allowed as the mutable reference appears in the final value. -``` - -> [!NOTE] -> Constant initializers can be thought of, in most cases, as being inlined wherever the constant appears. If a constant whose value contains a mutable reference to a mutable static were to appear twice, and this were to be allowed, that would create two mutable references, each having `'static` lifetime, to the same place. This could produce undefined behavior. -> -> Constants that contain mutable references to temporaries whose scopes have been extended to the end of the program have that same problem and an additional one. -> -> ```rust,compile_fail,E0764 -> const _: &mut u8 = &mut 0; // ERROR. -> // ^^^^^^ -> // Not allowed as the mutable reference appears in the final value and -> // because the constant expression contains a mutable borrow of an -> // expression whose temporary scope would be extended to the end of -> // the program. -> ``` -> -> Here, the value `0` is a temporary whose scope is extended to the end of the program (see [destructors.scope.lifetime-extension.static]). Such temporaries cannot be mutably borrowed in constant expressions (see [const-eval.const-expr.borrows]). -> -> To allow this, we'd have to decide whether each use of the constant creates a new `u8` value or whether each use shares the same lifetime-extended temporary. The latter choice, though closer to how `rustc` thinks about this today, would break the conceptual model that, in most cases, the constant initializer can be thought of as being inlined wherever the constant is used. Since we haven't decided, and due to the other problem mentioned, this is not allowed. - -```rust,compile_fail,E0080 -# #![allow(static_mut_refs)] -static mut S: u8 = 0; -const _: &dyn Send = &unsafe { &mut S }; // ERROR. -// ^^^^^^ -// Not allowed as the mutable reference appears in the final value, -// even though type erasure occurs. -``` - -Mutable references where the referent is a value of a [zero-sized type] are allowed. - -```rust -# #![allow(static_mut_refs)] -static mut S: () = (); -const _: &mut () = unsafe { &mut S }; // OK. -// ^^ This is a zero-sized type. -``` - -```rust -# #![allow(static_mut_refs)] -static mut S: [u8; 0] = [0; 0]; -const _: &mut [u8; 0] = unsafe { &mut S }; // OK. -// ^^^^^^^ This is a zero-sized type. -``` - -> [!NOTE] -> This is allowed as, for a value of a zero-sized type, no bytes can actually be mutated. We must accept this as `&mut []` is [promoted]. - -Values of [union type] are not considered to contain any references; for this purpose, a value of union type is treated as a sequence of untyped bytes. - -```rust -# #![allow(static_mut_refs)] -union U { f: &'static mut u8 } -static mut S: u8 = 0; -const _: U = unsafe { U { f: &mut S }}; // OK. -// ^^^^^^^^^^^^^^^ -// This is treated as a sequence of untyped bytes. -``` - -Mutable references contained within a mutable `static` may be referenced in the final value of a constant. A mutable `static` is a [`static mut`] item or a [`static`] item with an [interior mutable] type. - -```rust,no_run -# #![allow(static_mut_refs)] -static mut S: &mut u8 = unsafe { static mut I: u8 = 0; &mut I }; -const _: &&mut u8 = unsafe { &S }; // OK. -// ^^^^^^^ -// This mutable reference comes from a `static mut`. -``` - -```rust,no_run -# #![allow(static_mut_refs)] -# use core::cell::UnsafeCell; -struct PhantomCell { _m: UnsafeCell<()> } -unsafe impl Sync for PhantomCell {} -static S: (&mut u8, Option) = { - static mut I: u8 = 0; - (unsafe { &mut I }, None) -}; -const _: &&mut u8 = &S.0; -// ^^^^^^^ -// This mutable reference comes from a `static` with an -// interior mutable type. -``` - -> [!NOTE] -> This is allowed as it's separately not allowed to read from a mutable static during constant evaluation. See [const-eval.const-expr.path-static]. - -Mutable references contained within an [external static] may be referenced in the final value of a constant. - -```rust -# #![allow(static_mut_refs)] -unsafe extern "C" { static S: &'static mut u8; } -const _: &&mut u8 = unsafe { &S }; // OK. -// ^^^^^^^ -// This mutable references comes from an extern static. -``` - -> [!NOTE] -> This is allowed as it's separately not allowed to read from an external static during constant evaluation. See [const-eval.const-expr.path-static]. - -> [!NOTE] -> As described above, we accept, in the final value of constant items, shared references to static items whose values have interior mutability. -> -> ```rust -> # use core::sync::atomic::AtomicU8; -> static S: AtomicU8 = AtomicU8::new(0); -> const _: &AtomicU8 = &S; // OK. -> ``` -> -> However, we disallow similar code when the interior mutable value is created in the initializer. -> -> ```rust,compile_fail,E0492 -> # use core::sync::atomic::AtomicU8; -> const _: &AtomicU8 = &AtomicU8::new(0); // ERROR. -> ``` -> -> Here, the `AtomicU8` is a temporary whose scope is extended to the end of the program (see [destructors.scope.lifetime-extension.static]). Such temporaries with interior mutability cannot be borrowed in constant expressions (see [const-eval.const-expr.borrows]). -> -> To allow this, we'd have to decide whether each use of the constant creates a new `AtomicU8` or whether each use shares the same lifetime-extended temporary. The latter choice, though closer to how `rustc` thinks about this today, would break the conceptual model that, in most cases, the constant initializer can be thought of as being inlined wherever the constant is used. Since we haven't decided, this is not allowed. - r[items.const.expr-omission] The constant expression may only be omitted in a [trait definition]. From b666677ae6e6314a811dfaa30847eb7bb07c2c09 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Sun, 30 Nov 2025 02:54:29 +0000 Subject: [PATCH 3/3] Remove unused link reference definitions These link reference definitions were used in `const.no-mut-refs` and are no longer needed. --- src/items/constant-items.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/items/constant-items.md b/src/items/constant-items.md index a151847146..0b2f223c44 100644 --- a/src/items/constant-items.md +++ b/src/items/constant-items.md @@ -120,20 +120,13 @@ fn unused_generic_function() { } ``` -[`static mut`]: items.static.mut -[`static`]: items.static [const_eval]: ../const_eval.md [associated constant]: ../items/associated-items.md#associated-constants [constant value]: ../const_eval.md#constant-expressions -[external static]: items.extern.static [free]: ../glossary.md#free-item -[interior mutable]: interior-mut [static lifetime elision]: ../lifetime-elision.md#const-and-static-elision [trait definition]: traits.md [underscore imports]: use-declarations.md#underscore-imports [`Copy`]: ../special-types-and-traits.md#copy [value namespace]: ../names/namespaces.md -[promoted]: destructors.scope.const-promotion [promotion]: destructors.scope.const-promotion -[union type]: type.union -[zero-sized type]: layout.properties.size