Skip to content

Implement EII for statics#154193

Open
JonathanBrouwer wants to merge 4 commits intorust-lang:mainfrom
JonathanBrouwer:external-static
Open

Implement EII for statics#154193
JonathanBrouwer wants to merge 4 commits intorust-lang:mainfrom
JonathanBrouwer:external-static

Conversation

@JonathanBrouwer
Copy link
Copy Markdown
Contributor

@JonathanBrouwer JonathanBrouwer commented Mar 22, 2026

View all comments

This PR implements EII for statics. I've tried to mirror the implementation for functions in a few places, this causes some duplicate code but I'm also not really sure whether there's a clean way to merge the implementations.

This does not implement defaults for static EIIs yet, I will do that in a followup PR

@rustbot rustbot added A-attributes Area: Attributes (`#[…]`, `#![…]`) A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Mar 22, 2026
@JonathanBrouwer JonathanBrouwer added the F-extern_item_impls `#![feature(extern_item_impls)]` label Mar 22, 2026
@rust-log-analyzer

This comment has been minimized.

@rustbot rustbot added the T-clippy Relevant to the Clippy team. label Mar 22, 2026
@JonathanBrouwer JonathanBrouwer force-pushed the external-static branch 5 times, most recently from c7aa3cf to d9d3b13 Compare March 26, 2026 15:48

// Finally, resolve all regions. This catches wily misuses of
// lifetime parameters.
let errors = infcx.resolve_regions(external_impl, param_env, []);
Copy link
Copy Markdown
Contributor Author

@JonathanBrouwer JonathanBrouwer Mar 26, 2026

Choose a reason for hiding this comment

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

I pass an empty array for the assumed_wf_tys here, I can't really find what this argument is for, is this correct?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Passing an empty array in the resolve_regions call inside compare_eii_function_types causes no tests to fail, so would also like to add a test for that once I understand what it does

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

seems to be related to the "implied bounds hack" which seems to be part of how we resolve GATs in traits. Since this code is mostly adapted from method comparison in traits that makes sense to me, but I don't think we need it for EIIs. @lcnr could you confirm that?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

relevant for static FOO<'a, 'b>: &'a &'b () = &&(); to add an assumption that 'b: 'a holds.

Given that statics don't have any where-bounds/generics anyways, this shouldn't matter

Copy link
Copy Markdown
Contributor Author

@JonathanBrouwer JonathanBrouwer left a comment

Choose a reason for hiding this comment

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

r? @jdonszelmann

This code touches quite a few areas of the compiler I'm not super familiar yet with, so please review carefully whether I'm doing any stupid things :3

View changes since this review

@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Mar 26, 2026

jdonszelmann is currently at their maximum review capacity.
They may take a while to respond.

@JonathanBrouwer JonathanBrouwer changed the title [VERY WIP] Implement EII for statics Implement EII for statics Mar 26, 2026
@JonathanBrouwer JonathanBrouwer marked this pull request as ready for review March 26, 2026 15:51
@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Mar 26, 2026

Some changes occurred in compiler/rustc_passes/src/check_attr.rs

cc @jdonszelmann

Some changes occurred in compiler/rustc_attr_parsing

cc @jdonszelmann

The parser was modified, potentially altering the grammar of (stable) Rust
which would be a breaking change.

cc @fmease

Some changes occurred in src/tools/clippy

cc @rust-lang/clippy

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Mar 26, 2026
@rust-log-analyzer

This comment has been minimized.

assert!(
self.instances.borrow_mut().insert(instance, lldecl).is_none(),
"An instance was already present for {instance:?}"
);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not sure I understand when this can fail

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

or why this sanity check is necessary. I believe it may be, but the comment doesn't explain what scenario this matters in or what bug we previously had with this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I've added explanation to the comment, the assert is indeed simply a sanity check

@bjorn3
Copy link
Copy Markdown
Member

bjorn3 commented Mar 30, 2026

The fact that eii_impls is a list seems to imply that a single item can define multiple EIIs at the same time. Wouldn't that result in multiple statics aliasing each other with this PR? Especially with static mut that would be rather dangerous. But even without, I'm not sure that is not UB.

@JonathanBrouwer
Copy link
Copy Markdown
Contributor Author

JonathanBrouwer commented Apr 4, 2026

The fact that eii_impls is a list seems to imply that a single static can define multiple EIIs at the same time. Wouldn't that result in multiple statics aliasing each other with this PR? Especially with static mut that would be rather dangerous. But even without, I'm not sure that is not UB.

It is the other way around, a single item can implement multiple EIIs.

The definition of the EII is an extern static that "aliases" the static that implements the EII, I am far from an expert of rusts memory model so please correct me if I'm wrong, but I believe that should be fine?
Also if both the definition and implementation are static mut this should also be fine? Again far from an expert, please correct me

TLDR: It is exactly as sound as external statics are, since it's basically just an external static

@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Apr 4, 2026

This PR was rebased onto a different main commit. Here's a range-diff highlighting what actually changed.

Rebasing is a normal part of keeping PRs up to date, so no action is needed—this note is just to help reviewers.

@JonathanBrouwer
Copy link
Copy Markdown
Contributor Author

@rustbot ready

@jdonszelmann
Copy link
Copy Markdown
Contributor

it's correct that with EIIs you can get multiple statics aliasing eachother. We might want to disallow static mut EIIs completely?

@jdonszelmann
Copy link
Copy Markdown
Contributor

jdonszelmann commented Apr 5, 2026

even with a single EII,

#[eii(a)]
static FOO: usize;

#[a]
static BAR: usize = 3;

fn main() {
    assert_eq!(
        &BAR as *const _ as usize, 
        &FOO as *const _ as usize
    );
}

@JonathanBrouwer
Copy link
Copy Markdown
Contributor Author

JonathanBrouwer commented Apr 5, 2026

The thing is that FOO is not a static, it's desugared to an extern static, is it still a problem then?

unsafe extern "Rust" {
    safe static FOO: usize;
}

mod imp {
    #[unsafe(no_mangle)]
    pub static FOO: usize = 3;
}

fn main() {
    assert_eq!(
        &FOO as *const _ as usize,
        &imp::FOO as *const _ as usize
    );
}

i.e. Is this code unsound?

@JonathanBrouwer
Copy link
Copy Markdown
Contributor Author

JonathanBrouwer commented Apr 5, 2026

Disallowing static mut EIIs makes sense to me, but more because they're hard to use correctly than that they're inherently unsound

@bjorn3
Copy link
Copy Markdown
Member

bjorn3 commented Apr 5, 2026

Currently if you have two extern statics with different symbol names, they would never alias. I can very much imagine that there will be crates which break if two EIIs defined by said crate alias. Breaking both as in causing UB and as in misbehaving without UB. Also it requires having support for aliasing at the object file level, while if each static can define only a single EII, then we can get away without any aliasing support at the object file level. The definition can just have the same symbol name as the EII. And if the EII has a default, this default can either be put in a separate object file only linked when the default is used or be defined as weak symbol.

@JonathanBrouwer
Copy link
Copy Markdown
Contributor Author

JonathanBrouwer commented Apr 5, 2026

Right, I agree this is also undesirable
So what I will do is:

  • Disallow multiple eii impls on 1 static
  • Disallow eii on mut statics

@JonathanBrouwer
Copy link
Copy Markdown
Contributor Author

@rustbot author

@rustbot rustbot removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Apr 5, 2026
@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Apr 5, 2026

Reminder, once the PR becomes ready for a review, use @rustbot ready.

@rustbot rustbot added the S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. label Apr 5, 2026
@jdonszelmann
Copy link
Copy Markdown
Contributor

jdonszelmann commented Apr 5, 2026

That doesn't help though, EIIs inherently generate two symbol names for one static. Even if one is extern. And the definition can't get the same name. It should be available under the name given to the implementation as well imo. The same is true for functions atm.

@jdonszelmann
Copy link
Copy Markdown
Contributor

multiple impls does make that worse, but it's already a problem to begin with

@jdonszelmann
Copy link
Copy Markdown
Contributor

also, could you add a test that both have the same address?

@bjorn3
Copy link
Copy Markdown
Member

bjorn3 commented Apr 5, 2026

That doesn't help though, EIIs inherently generate two symbol names for one static. Even if one is extern.

There is no inherent reason why the EII declaration and the impl have to use different symbol names afaik. If you only defining a single EII per static, then tcx.symbol_name() on the impl can return the symbol name of the declaration.

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

Labels

A-attributes Area: Attributes (`#[…]`, `#![…]`) A-LLVM Area: Code generation parts specific to LLVM. Both correctness bugs and optimization-related issues. F-extern_item_impls `#![feature(extern_item_impls)]` S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-clippy Relevant to the Clippy team. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants