Skip to content

Conversation

@ChayimFriedman2
Copy link
Contributor

@ChayimFriedman2 ChayimFriedman2 commented Jul 27, 2025

Basically, we switch to expanding cfg_attr in AST form, filter irrelevant attributes from the item tree, and move hir-def attributes (non-item-tree) to be flag-based.

The main motivation is memory usage, although this also simplifies the code, and fixes some bugs around handling of cfg_attrs.

This saves 120mb on analysis-stats . 🎉

Fixes #10110 and maybe some other issues too.
Fixes #8434.

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jul 27, 2025
@ChayimFriedman2 ChayimFriedman2 force-pushed the better-attrs branch 2 times, most recently from e69ea8b to b2ecf39 Compare July 28, 2025 19:22
Copy link
Member

@Veykril Veykril left a comment

Choose a reason for hiding this comment

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

You're killing me with these big PRs 😬 ..

I have some thought to note down on the EditionedFileId stuff but that will have to wait for later

ModPath { kind, segments: SmallVec::new_const() }
}

pub fn from_tokens(
Copy link
Member

Choose a reason for hiding this comment

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

Ugh now we have 3 ways to parse these ...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

And all subtly different... Yeah I agree.

Comment on lines +971 to +964
// FIXME: We probably should use this in more places.
/// This is used to avoid interning the whole `AttrDefId`, so we intern just modules and not everything.
#[salsa_macros::interned(debug, no_lifetime)]
pub struct InternedModuleId {
pub loc: ModuleId,
}
Copy link
Member

Choose a reason for hiding this comment

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

Sounds like #20001

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, except using interneds and not tracked structs, and being localized to attrs. At least currently, analysis doesn't create this type, only IDE needs to resolve attributes on modules, so the memory impact is low.

#20001 is that right path forward though.

/// This can be empty if the path is not of 1 or 2 segments exactly.
pub segments: ArrayVec<SyntaxToken, 2>,
pub range: TextRange,
pub is_test: bool,
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't really make sense, we cannot know whether this is the test attribute at this layer, we have no name resolution here after all?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have a FIXME for this in hir-def/src/attrs.rs. The general point is that yes, we need proper name resolution for these, but it's hard to do. The reason I put this in Meta is because macros often expand to the full name ::core::prelude::vX::test, which is longer than 2 segments. I could make segments 4-length, but that may somewhat hurt perf. I don't feel strongly about that though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added a FIXME here too.

@ChayimFriedman2
Copy link
Contributor Author

You're killing me with these big PRs 😬 ..

Sorry! This one could probably be broken (at least to a rewrite of ast-based expand_cfg_attr then to flags-based attrs), but I didn't thought about that until it was too late...

@Veykril
Copy link
Member

Veykril commented Aug 3, 2025

Should we wait with this until the trait solver lands? It looks like it will cause a lot of conflicts

@ChayimFriedman2
Copy link
Contributor Author

The changes in hir-ty here are minimal, but I can wait until the switch to the next trait solver lands.

@ChayimFriedman2
Copy link
Contributor Author

Rebased. This should be unblocked now.

@ChayimFriedman2
Copy link
Contributor Author

Rebased. @Veykril I think this should be ready for review now.

@Veykril
Copy link
Member

Veykril commented Oct 21, 2025

Will take a look tomorrow 👍

Copy link
Member

Choose a reason for hiding this comment

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

I see I never got back to my follow up on this part. I really do not like this, for one it feels wrong to bundle crates into this. And secondly, the crate being ignored for comparison and such gives me the fear that we might rely on the crate somewhere where we suddenly get a different editioned file id that compares the same but has the a different crate. Is there no way for us to go ab out things differently where we need this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I dislike that too. In fact I doubted it is the best idea, and I am still not sure it is, and if there is an alternative I will eagerly adopt it. I did it anyway, because this simplifies the code a lot.

Basically, the (almost) only place we use this is to retrieve the cfg when lowering the item tree, to eager-expand #[cfg] and #[cfg_attr]. The alternative is that the item tree will not be crate-aware, and we will instead lazily expand cfgs on the def map. The problems with this approach are:

  • It requires us to maintain two codes for cfg expansion, both at AST-level and at tt level (item tree attrs)
  • Worse, they are extremely tightly coupled. Basically, they are required to match the behavior of each other precisely, including when they error. This is because of AttrId - we use the attribute index to refer to the attribute, and this is post cfg-expansion (it must be, since cfg_attr can expand to multiple attrs). That means that any mismatch between the two parsing codes will create very hard to detect bugs.

Also note that the crate is not ignored for comparisons. This is important. We ignore the crate in two places:

  • In hash checking, because we support ignoring the crate in comparisons, as per the next bullet. This shouldn't impact interning perf since usually each file belongs to only one crate.
  • In some handlers (those that use from_span_guess_origin()), at the following conditions:
    • The hasher is latency-sensitive (e.g. on enter), so it cannot afford checking the crate from the def map.
    • The hasher operates on AST only (this correlates with the previous point), so the crate doesn't matter

Comment on lines -261 to -270
let mut process = true;

// Process other crate-level attributes.
for attr in &*attrs {
if let Some(cfg) = attr.cfg()
&& self.cfg_options.check(&cfg) == Some(false)
{
process = false;
break;
}
Copy link
Member

Choose a reason for hiding this comment

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

This still needs to be reverted #20316 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No? When hitting CfgDisabled we still process the attrs.

Copy link
Member

Choose a reason for hiding this comment

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

we process too many, once we hit the cfg we need to stop. That is

#![attr1]
#![cfg(false)]
#![attr2]

in this setup, rust only processes attr1, not attr2

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The attributes in the CfgDisabled is only up to the cfg, it's even documented in the variant. This is because collect_item_tree_attrs() early-breaks on #[cfg].

Basically, we switch to expanding cfg_attr in AST form, filter irrelevant attributes from the item tree, and move hir-def attributes (non-item-tree) to be flag-based.

The main motivation is memory usage, although this also simplifies the code, and fixes some bugs around handling of `cfg_attr`s.
@ChayimFriedman2
Copy link
Contributor Author

@Veykril do you want to merge this, or do you still want to review this?

@Veykril
Copy link
Member

Veykril commented Oct 22, 2025

good to go, I don;t have a better idea for the editioned file id stuff and I don't want to block it on that either.

@ChayimFriedman2 ChayimFriedman2 added this pull request to the merge queue Oct 22, 2025
Merged via the queue into rust-lang:master with commit 08c0dc6 Oct 22, 2025
15 checks passed
@ChayimFriedman2 ChayimFriedman2 deleted the better-attrs branch October 22, 2025 12:56
@rustbot rustbot removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Oct 22, 2025
@Veykril
Copy link
Member

Veykril commented Oct 22, 2025

This horribly broke something in the type system https://rust-analyzer.github.io/metrics/?start=2025-10-15&end=

@ChayimFriedman2
Copy link
Contributor Author

Both speed and type mismatches regression. Type mismatches is probably some thing I forgot - I'll try to investigate, speed is weirder as this didn't have any measurable effect on speed when I benchmarked, although that was before the trait solver migration. Will check that too.

@ChayimFriedman2
Copy link
Contributor Author

Okay, I identified the problem for both: for some reason, attribute macros are run twice. This is likely related to the def map changes - I'm reviewing them carefully now.

@ChayimFriedman2
Copy link
Contributor Author

Okay, I found the problem. The old behavior was subtly wrong, the new behavior is also wrong. I need to look at rustc now to see what it does.

@ShoyuVanilla
Copy link
Member

ShoyuVanilla commented Oct 23, 2025

FWIW rustc's attribute handling has been rewritten recently. cc rust-lang/rust#146389

@lnicola
Copy link
Member

lnicola commented Oct 24, 2025

changelog skip

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

proc-macro applied with cfg_attr doesn’t see associated attributes Custom derive attribute stripping is insufficient

5 participants