From 68746b96e90062e6f943e93dd051bfdcb5296646 Mon Sep 17 00:00:00 2001 From: sjwang05 <63834813+sjwang05@users.noreply.github.com> Date: Tue, 30 Jun 2026 14:02:35 -0700 Subject: [PATCH 1/3] Document that extern statics are never eligible for promotion Reference update for rust-lang/rust#157641: a borrow of an extern static is never eligible for constant promotion, even when the reference itself is never read from. --- src/destructors.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/destructors.md b/src/destructors.md index aa27842622..2df34176f1 100644 --- a/src/destructors.md +++ b/src/destructors.md @@ -378,6 +378,24 @@ r[destructors.scope.const-promotion] Promotion of a value expression to a `'static` slot occurs when the expression could be written in a constant and borrowed, and that borrow could be dereferenced where the expression was originally written, without changing the runtime behavior. That is, the promoted expression can be evaluated at compile-time and the resulting value does not contain [interior mutability] or [destructors] (these properties are determined based on the value where possible, e.g. `&None` always has the type `&'static Option<_>`, as it contains nothing disallowed). +r[destructors.scope.const-promotion.extern-static] +A borrow of an [`extern` static] cannot be promoted, even if the resulting reference is never read from. + +```rust,compile_fail +unsafe extern "C" { + static X: i32; +} + +// The array is a temporary, so it would have to be promoted to a +// `'static` slot in order to be used in a static initializer. +// However, since it borrows an `extern` static, it is not eligible +// for promotion. +static mut FOO: *const &i32 = [unsafe { &X }].as_ptr(); // ERROR +``` + +> [!NOTE] +> Only expressions that cannot fail to evaluate can be promoted. The value of an `extern` static cannot be known at compile time, so any expression that uses an `extern` static is rejected, even if it never reads the static's value. To borrow an `extern` static in a const context, use a [const block] instead. + r[destructors.scope.lifetime-extension] ### Temporary lifetime extension @@ -645,9 +663,11 @@ There is one additional case to be aware of: when a panic reaches a [non-unwindi [Assignment]: expressions/operator-expr.md#assignment-expressions [binding modes]: patterns.md#binding-modes [closure]: types/closure.md +[const block]: expressions/block-expr.md#const-blocks [destructors]: destructors.md [destructuring assignment]: expr.assign.destructure [expression]: expressions.md +[`extern` static]: items/external-blocks.md#statics [guard condition operand]: expressions/match-expr.md#match-guard-chains [identifier pattern]: patterns.md#identifier-patterns [initialized]: glossary.md#initialized From 88f2cbee042afc093dbebd400bc61ee1c08b81b3 Mon Sep 17 00:00:00 2001 From: Travis Cross Date: Tue, 4 Aug 2026 19:44:06 +0000 Subject: [PATCH 2/3] Revise `...const-promotion.extern-static` rule Let's make some editorially adjustments. --- src/destructors.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/destructors.md b/src/destructors.md index 2df34176f1..27eab0dd15 100644 --- a/src/destructors.md +++ b/src/destructors.md @@ -383,18 +383,18 @@ A borrow of an [`extern` static] cannot be promoted, even if the resulting refer ```rust,compile_fail unsafe extern "C" { - static X: i32; + static X: u8; } -// The array is a temporary, so it would have to be promoted to a -// `'static` slot in order to be used in a static initializer. -// However, since it borrows an `extern` static, it is not eligible -// for promotion. -static mut FOO: *const &i32 = [unsafe { &X }].as_ptr(); // ERROR +// The array is a temporary, so it would have to be promoted to +// a `'static` slot in order to be used in a static initializer. +// But since it borrows from an `extern` static, it is not +// eligible for promotion. +static mut S: *const &u8 = [unsafe { &X }].as_ptr(); // ERROR. ``` > [!NOTE] -> Only expressions that cannot fail to evaluate can be promoted. The value of an `extern` static cannot be known at compile time, so any expression that uses an `extern` static is rejected, even if it never reads the static's value. To borrow an `extern` static in a const context, use a [const block] instead. +> Only expressions that cannot fail to evaluate can be promoted. The value of an `extern` static cannot be known at compile time, so any expression that uses an `extern` static is rejected by promotion, even if the code never reads the static's value. To borrow from an `extern` static in a const context, use a [const block] instead. r[destructors.scope.lifetime-extension] ### Temporary lifetime extension From 6acda2a998b6247cef150629d8b6e81469a157f0 Mon Sep 17 00:00:00 2001 From: sjwang05 <63834813+sjwang05@users.noreply.github.com> Date: Tue, 4 Aug 2026 18:45:38 -0700 Subject: [PATCH 3/3] Add const-block example for borrowing an `extern` static --- src/destructors.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/destructors.md b/src/destructors.md index 27eab0dd15..8be3ee2604 100644 --- a/src/destructors.md +++ b/src/destructors.md @@ -394,7 +394,17 @@ static mut S: *const &u8 = [unsafe { &X }].as_ptr(); // ERROR. ``` > [!NOTE] -> Only expressions that cannot fail to evaluate can be promoted. The value of an `extern` static cannot be known at compile time, so any expression that uses an `extern` static is rejected by promotion, even if the code never reads the static's value. To borrow from an `extern` static in a const context, use a [const block] instead. +> Only expressions that cannot fail to evaluate can be promoted. The value of an `extern` static cannot be known at compile time, so any expression that uses an `extern` static is rejected by promotion, even if the code never reads the static's value. To borrow from an `extern` static in a const context, use a [const block] instead: +> +> ```rust +> unsafe extern "C" { +> static X: u8; +> } +> +> // The array is evaluated as a constant, giving it a `'static` +> // slot without promotion. +> static mut S: *const &u8 = const { [unsafe { &X }] }.as_ptr(); +> ``` r[destructors.scope.lifetime-extension] ### Temporary lifetime extension