Skip to content

Do not promote extern statics#157641

Open
sjwang05 wants to merge 2 commits into
rust-lang:mainfrom
sjwang05:fix-143174-extern-static-promotion
Open

Do not promote extern statics#157641
sjwang05 wants to merge 2 commits into
rust-lang:mainfrom
sjwang05:fix-143174-extern-static-promotion

Conversation

@sjwang05

@sjwang05 sjwang05 commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

View all comments

We already reject thread-local statics during promotion, so extend that check to extern statics as well.

fixes #143174

@rustbot

rustbot commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

Some changes occurred to MIR optimizations

cc @rust-lang/wg-mir-opt

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jun 9, 2026
@rustbot

rustbot commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

r? @oli-obk

rustbot has assigned @oli-obk.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: compiler
  • compiler expanded to 73 candidates
  • Random selection from 20 candidates

@rust-log-analyzer

This comment has been minimized.

@sjwang05 sjwang05 force-pushed the fix-143174-extern-static-promotion branch from 6257168 to 80ac224 Compare June 9, 2026 07:39
@rust-log-analyzer

This comment has been minimized.

@sjwang05 sjwang05 force-pushed the fix-143174-extern-static-promotion branch from 80ac224 to 8877f4c Compare June 9, 2026 08:01
Comment thread compiler/rustc_mir_transform/src/promote_consts.rs Outdated
@rust-log-analyzer

This comment has been minimized.

@oli-obk oli-obk added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jun 9, 2026
@rustbot

rustbot commented Jun 10, 2026

Copy link
Copy Markdown
Collaborator

Some changes occurred to the CTFE machinery

cc @RalfJung, @oli-obk, @lcnr

Some changes occurred to constck

cc @fee1-dead

@sjwang05 sjwang05 changed the title promotion: don't promote extern statics reject by-value reads of extern atatics during constck Jun 10, 2026
@sjwang05 sjwang05 changed the title reject by-value reads of extern atatics during constck reject by-value reads of extern statics during constck Jun 10, 2026
@oli-obk

oli-obk commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

So... This is technically a breaking change, as we now have a new const check, which previously only errored if actually evaluated. So an associated const could now error, if it previously was unused in the current crate.

A different oddity is that we don't check this in const fns, as those can be fine at runtime, as long as the extern static is never read at compile time.

Thoughts @rust-lang/wg-const-eval?

@lcnr

lcnr commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

crater than FCP merge in that case?

@RalfJung

RalfJung commented Jun 11, 2026

Copy link
Copy Markdown
Member

Even though we're allowed to reference extern statics, it's impossible to know the value of an extern static during CTFE when it's used by-value, so we shouldn't try promoting them.

The PR changes const-checking, not promotion. Something seems wrong, what is this actually trying to fix?

This looks like an ad-hoc special case in const checking; I am not sure what this buys us since it can be trivially bypassed (by creating a pointer and then deref'ing it).

To fix #143174, I would have expected this to change promotion (compiler/rustc_mir_transform/src/promote_consts.rs), not const checking.

@RalfJung

Copy link
Copy Markdown
Member

We already avoid promotion in this:

fn main() { unsafe {
let f: &Foo = &Foo(BAR);
} }

Why do we promote in the other case?

@oli-obk

oli-obk commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

This PR originally had a promotion time check. Preventing it in const check covers exactly the problematic use case, as everything else doesn't get promoted.

@RalfJung

RalfJung commented Jun 11, 2026

Copy link
Copy Markdown
Member

I don't think we should fix a promotion bug by changing const-checking. That's a very non-local invariant relying on the subtle interplay of which exact code gets rejected where.

Promotion on its own is supposed to only accept code that cannot fail to evaluate. It apparently does not do this job properly.

@sjwang05

sjwang05 commented Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

To fix #143174, I would have expected this to change promotion (compiler/rustc_mir_transform/src/promote_consts.rs), not const checking.

The original version of this PR actually did modify promotion; the diff is available here: 8877f4c. We decided to change it because the original logic felt too messy: #157641 (comment), and I just forgot to change the PR description after making the change.

Why do we promote in the other case?

Promotion is gated behind let Some(hir::ConstContext::Static(..)) = self.const_kind:

&& let Some(hir::ConstContext::Static(..)) = self.const_kind
so the fn main case never gets promoted, even though both desugar to "deref of a static addr."

@RalfJung

RalfJung commented Jun 12, 2026

Copy link
Copy Markdown
Member

That check already special-cases thread-local statics here:

&& !self.tcx.is_thread_local_static(did)

It is very common in the interpreter to treat thread-local statics and extern statics the same, so what I would have expected is that we add && !self.tcx.is_foreign_item(did) there. Does that not work? This is just promotion, so I think excluding any use of extern statics is fine (we'll have to check with crater though).

@sjwang05

sjwang05 commented Jun 12, 2026

Copy link
Copy Markdown
Contributor Author

I actually did try that at first, just adding !self.tcx.is_foreign_item(did) at the end of the if-statement; the problem was that this ended up rejecting creating refs/ptrs to extern statics as well, like this line in tests/mir-opt/const_promotion_extern_static.rs:

static mut FOO: *const &i32 = [unsafe { &X }].as_ptr();

since &X still desugars to &(*X_REF), which is the same "deref of a static ref" pattern that's problematic for by-value reads of extern statics, which the original check wrongly failed to compile.

For instance, in the non-problematic case,

extern "C" {
    static X: i32;
}

static mut FOO: *const &i32 = [unsafe { &X }].as_ptr();

gets lowered to

const FOO::promoted[0]: &[&i32; 1] = {
    bb0: {
        _3 = const {alloc1: *const i32}; // addr of X
        _2 = &(*_3);
        _1 = [move _2];
        _0 = &_1;
        return;
    }
}

alloc1 (extern static: X)

after promotion, and since (*_3) is only ever reborrowed and never read by value, CTFE never tries to evaluate X — the promoted just bakes the address in. On the other hand, in the problematic case,

extern "C" {
    static BAR: i32;
}

static FOO: &(i32,) = unsafe { &(BAR,) };

gets lowered to

const FOO::promoted[0]: &(i32,) = {
    bb0: {
        _3 = const {alloc1: *const i32}; // addr of BAR
        _2 = copy (*_3);
        _1 = (move _2,);
        _0 = &_1;
        return;
    }
}

alloc1 (extern static: BAR)

where the copy (*_3) forces CTFE to try to find the value of BAR, causing the ICE. The !self.tcx.is_foreign_item(did) check rejects both instances of *_3, though, regardless of whether or not the value is ever actually read or if we're just taking a ref to it.

@RalfJung

Copy link
Copy Markdown
Member

Yeah I think we should reject such promotion (unless crater disagrees). People can use const blocks if they need this.

@oli-obk

oli-obk commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

Cool, let's try that then and crater it. Seems very niche

@sjwang05 sjwang05 changed the title reject by-value reads of extern statics during constck Do not promote extern statics Jun 13, 2026
@sjwang05 sjwang05 force-pushed the fix-143174-extern-static-promotion branch from 168eebd to 3e8d36a Compare June 13, 2026 05:04
@oli-obk oli-obk added T-lang Relevant to the language team WG-const-eval Working group: Const evaluation A-constant-promotion Area: constant promotion and removed T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jun 22, 2026
@oli-obk

oli-obk commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Please also add a test for the snippet that used to compile:

unsafe extern "C" {
    static X: i32;
}

static mut FOO: *const &i32 = [unsafe { &X }].as_ptr();

I think this is a fairly straight forward "make promotion simpler" change.

@rfcbot fcp merge lang

"Stabilization" report

This PR, intended as an ICE fix for

type Fun = unsafe extern "C" fn();

struct Foo(Fun);

static FOO: &Foo = &Foo(BAR); // ICE, reading from an extern static

unsafe extern "C" {
    static BAR: Fun;
}

also breaks the more benign

unsafe extern "C" {
    static X: i32;
}

static mut FOO: *const &i32 = [unsafe { &X }].as_ptr();

instead requiring it to be written as

unsafe extern "C" {
    static X: i32;
}

static HELPER: &i32 = unsafe { &X };
static mut FOO: *const &i32 = [HELPER].as_ptr();

We encountered a single regression on crater (#157641 (comment)), which only exists in an old version of a crate, all newer ones have fixed it. The regression always turns into a compilation failure, unfortunately not in a way that we can make any suggestion for improving it. The diagnostic itself is a generic diagnostic we already emit today and which can get some improvements, but that's orthogonal to this change.

This also claws back one of the accidental capabilities we gave promotion.

While it may be fixable in a way that doesn't break such code, all other ways would just add hacks to the compiler to avoid this really rare case that is easily worked around in code

@rust-rfcbot

rust-rfcbot commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

@oli-obk has proposed to merge this. The next step is review by the rest of the tagged team members:

No concerns currently listed.

Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

cc @rust-lang/lang-advisors: FCP proposed for lang, please feel free to register concerns.
See this document for info about what commands tagged team members can give me.

@rust-rfcbot rust-rfcbot added proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. labels Jun 22, 2026
@traviscross traviscross added I-lang-nominated Nominated for discussion during a lang team meeting. P-lang-drag-1 Lang team prioritization drag level 1. https://rust-lang.zulipchat.com/#narrow/channel/410516-t-lang needs-reference-pr This language change needs an approved Reference PR to proceed. labels Jun 22, 2026
@traviscross

Copy link
Copy Markdown
Contributor

Makes sense to me. The rule is that we want to reject promotion of code that might fail evaluation. Here, we're doing that by rejecting derefs of extern statics. We could reject less and allow reborrows, but not doing that helps keep promotion simple, which it also should be.

Thanks @sjwang05 for your efforts on this (and to @oli-obk and @RalfJung for the reviews and support).

It's my understanding that these more-local rewrites would also be valid:

unsafe extern "C" { static X: i32; }
static mut FOO: *const &i32 = [const { unsafe { &X } }].as_ptr();
unsafe extern "C" { static X: i32; }
static mut FOO: *const &i32 = const { [unsafe { &X }] }.as_ptr();

@rfcbot reviewed

@oli-obk oli-obk removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jun 23, 2026
@rust-rfcbot rust-rfcbot added the final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. label Jun 24, 2026
@scottmcm

Copy link
Copy Markdown
Member

I certainly like moving the tricky cases to requiring const { … } where we can get away with it 👍 Happy to follow the const folks' recommendation here.

@rfcbot reviewed

@rust-rfcbot rust-rfcbot removed the proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. label Jun 24, 2026
@rust-rfcbot

Copy link
Copy Markdown
Collaborator

🔔 This is now entering its final comment period, as per the review above. 🔔

@traviscross traviscross added proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. I-lang-radar Items that are on lang's radar and will need eventual work or consideration. and removed final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. I-lang-nominated Nominated for discussion during a lang team meeting. P-lang-drag-1 Lang team prioritization drag level 1. https://rust-lang.zulipchat.com/#narrow/channel/410516-t-lang labels Jun 24, 2026
@nikomatsakis

Copy link
Copy Markdown
Contributor

@rfcbot reviewed

@sjwang05 sjwang05 force-pushed the fix-143174-extern-static-promotion branch from 3e8d36a to 9ac2606 Compare June 24, 2026 19:20
@sjwang05

Copy link
Copy Markdown
Contributor Author

Please also add a test for the snippet that used to compile

Done, added a new uitest for it.

Thanks @oli-obk @RalfJung for helping me through this.

sjwang05 added a commit to sjwang05/reference that referenced this pull request Jun 30, 2026
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.
@sjwang05

Copy link
Copy Markdown
Contributor Author

Reference PR is here: rust-lang/reference#2302

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-constant-promotion Area: constant promotion disposition-merge This issue / PR is in PFCP or FCP with a disposition to merge it. I-lang-radar Items that are on lang's radar and will need eventual work or consideration. needs-reference-pr This language change needs an approved Reference PR to proceed. proposed-final-comment-period Proposed to merge/close by relevant subteam, see T-<team> label. Will enter FCP once signed off. T-lang Relevant to the language team WG-const-eval Working group: Const evaluation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ICE when trying to create a static reference to a structure enclosing an external static value