Auto merge of #151382 - JonathanBrouwer:attr-perf, r=jdonszelmann

Only run finalizers of accepted attributes

Locally this had insanely good perf, but lets see what reality thinks about this
r? @jdonszelmann 

Attribute parsing consists of two stages:

- All attribute are "accepted" by one or more parsers, which means the unparsed attribute is parsed, information about it is stored in the attr parser struct
- After all attributes are parsed, we "finalize" all parsers, producing a single parsed attribute representation from the parser struct.

This two-stage process exist so multiple attributes can get merged into one parser representation. For example if you have two repr attributes `#[repr(C)]` `#[repr(packed)]` it will only produce one parsed `Repr` attribue.

The dumb thing we did was we would "finalize" all parsers, even the ones that never accepted an attribute. This PR only calls finalize on the parsers that accepted at least one attribute.
This commit is contained in:
bors 2026-01-21 00:01:49 +00:00
commit d276646872
6 changed files with 49 additions and 47 deletions

View file

@ -103,18 +103,18 @@ type GroupType<S> = LazyLock<GroupTypeInner<S>>;
pub(super) struct GroupTypeInner<S: Stage> {
pub(super) accepters: BTreeMap<&'static [Symbol], Vec<GroupTypeInnerAccept<S>>>,
pub(super) finalizers: Vec<FinalizeFn<S>>,
}
pub(super) struct GroupTypeInnerAccept<S: Stage> {
pub(super) template: AttributeTemplate,
pub(super) accept_fn: AcceptFn<S>,
pub(super) allowed_targets: AllowedTargets,
pub(super) finalizer: FinalizeFn<S>,
}
type AcceptFn<S> =
pub(crate) type AcceptFn<S> =
Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, S>, &ArgParser) + Send + Sync>;
type FinalizeFn<S> =
pub(crate) type FinalizeFn<S> =
Box<dyn Send + Sync + Fn(&mut FinalizeContext<'_, '_, S>) -> Option<AttributeKind>>;
macro_rules! attribute_parsers {
@ -142,8 +142,7 @@ macro_rules! attribute_parsers {
@[$stage: ty] pub(crate) static $name: ident = [$($names: ty),* $(,)?];
) => {
pub(crate) static $name: GroupType<$stage> = LazyLock::new(|| {
let mut accepts = BTreeMap::<_, Vec<GroupTypeInnerAccept<$stage>>>::new();
let mut finalizes = Vec::<FinalizeFn<$stage>>::new();
let mut accepters = BTreeMap::<_, Vec<GroupTypeInnerAccept<$stage>>>::new();
$(
{
thread_local! {
@ -151,7 +150,7 @@ macro_rules! attribute_parsers {
};
for (path, template, accept_fn) in <$names>::ATTRIBUTES {
accepts.entry(*path).or_default().push(GroupTypeInnerAccept {
accepters.entry(*path).or_default().push(GroupTypeInnerAccept {
template: *template,
accept_fn: Box::new(|cx, args| {
STATE_OBJECT.with_borrow_mut(|s| {
@ -159,17 +158,16 @@ macro_rules! attribute_parsers {
})
}),
allowed_targets: <$names as crate::attributes::AttributeParser<$stage>>::ALLOWED_TARGETS,
finalizer: Box::new(|cx| {
let state = STATE_OBJECT.take();
state.finalize(cx)
}),
});
}
finalizes.push(Box::new(|cx| {
let state = STATE_OBJECT.take();
state.finalize(cx)
}));
}
)*
GroupTypeInner { accepters:accepts, finalizers:finalizes }
GroupTypeInner { accepters }
});
};
}

View file

@ -12,7 +12,7 @@ use rustc_session::Session;
use rustc_session::lint::{BuiltinLintDiag, LintId};
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
use crate::context::{AcceptContext, FinalizeContext, SharedContext, Stage};
use crate::context::{AcceptContext, FinalizeContext, FinalizeFn, SharedContext, Stage};
use crate::early_parsed::{EARLY_PARSED_ATTRIBUTES, EarlyParsedState};
use crate::parser::{ArgParser, PathParser, RefPathParser};
use crate::session_diagnostics::ParsedDescription;
@ -270,6 +270,8 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
let mut attr_paths: Vec<RefPathParser<'_>> = Vec::new();
let mut early_parsed_state = EarlyParsedState::default();
let mut finalizers: Vec<&FinalizeFn<S>> = Vec::with_capacity(attrs.len());
for attr in attrs {
// If we're only looking for a single attribute, skip all the ones we don't care about.
if let Some(expected) = self.parse_only {
@ -383,6 +385,8 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
};
(accept.accept_fn)(&mut cx, &args);
finalizers.push(&accept.finalizer);
if !matches!(cx.stage.should_emit(), ShouldEmit::Nothing) {
Self::check_target(&accept.allowed_targets, target, &mut cx);
}
@ -417,7 +421,7 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> {
}
early_parsed_state.finalize_early_parsed_attributes(&mut attributes);
for f in &S::parsers().finalizers {
for f in &finalizers {
if let Some(attr) = f(&mut FinalizeContext {
shared: SharedContext { cx: self, target_span, target, emit_lint: &mut emit_lint },
all_attrs: &attr_paths,

View file

@ -21,8 +21,8 @@ mod to_reuse {
#[attr = Cold]
fn foo_no_reason(x: usize) -> usize { x }
#[attr = Deprecation {deprecation: Deprecation {since: Unspecified}}]
#[attr = Cold]
#[attr = Deprecation {deprecation: Deprecation {since: Unspecified}}]
fn bar(x: usize) -> usize { x }
}

View file

@ -44,9 +44,9 @@ impl Trait for S {
fn foo0(arg0: _) -> _ { to_reuse::foo(self + 1) }
// Check that #[inline(hint)] is added when other attributes present in inner reuse
#[attr = Deprecation {deprecation: Deprecation {since: Unspecified}}]
#[attr = MustUse]
#[attr = Cold]
#[attr = MustUse]
#[attr = Deprecation {deprecation: Deprecation {since: Unspecified}}]
#[attr = Inline(Hint)]
fn foo1(arg0: _) -> _ { to_reuse::foo(self / 2) }
@ -59,18 +59,18 @@ impl Trait for S {
fn foo3(arg0: _) -> _ { to_reuse::foo(self / 2) }
// Check that #[inline(never)] is preserved when there are other attributes in inner reuse
#[attr = Deprecation {deprecation: Deprecation {since: Unspecified}}]
#[attr = Inline(Never)]
#[attr = MustUse]
#[attr = Cold]
#[attr = MustUse]
#[attr = Inline(Never)]
#[attr = Deprecation {deprecation: Deprecation {since: Unspecified}}]
fn foo4(arg0: _) -> _ { to_reuse::foo(self / 2) }
}.foo()
}
// Check that #[inline(hint)] is added when there are other attributes present in trait reuse
#[attr = Deprecation {deprecation: Deprecation {since: Unspecified}}]
#[attr = MustUse]
#[attr = Cold]
#[attr = MustUse]
#[attr = Deprecation {deprecation: Deprecation {since: Unspecified}}]
#[attr = Inline(Hint)]
fn foo1(self: _) -> _ { self.0.foo1() }
@ -83,10 +83,10 @@ impl Trait for S {
fn foo3(self: _) -> _ { self.0.foo3() }
// Check that #[inline(never)] is preserved when there are other attributes in trait reuse
#[attr = Deprecation {deprecation: Deprecation {since: Unspecified}}]
#[attr = Inline(Never)]
#[attr = MustUse]
#[attr = Cold]
#[attr = MustUse]
#[attr = Inline(Never)]
#[attr = Deprecation {deprecation: Deprecation {since: Unspecified}}]
fn foo4(self: _) -> _ { self.0.foo4() }
}

View file

@ -1,17 +1,3 @@
error: can't mark as unstable using an already stable feature
--> $DIR/unstable-attribute-rejects-already-stable-features.rs:7:1
|
LL | #[rustc_const_unstable(feature = "arbitrary_enum_discriminant", issue = "42")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this feature is already stable
LL | const fn my_fun() {}
| -------------------- the stability attribute annotates this item
|
help: consider removing the attribute
--> $DIR/unstable-attribute-rejects-already-stable-features.rs:7:1
|
LL | #[rustc_const_unstable(feature = "arbitrary_enum_discriminant", issue = "42")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: can't mark as unstable using an already stable feature
--> $DIR/unstable-attribute-rejects-already-stable-features.rs:6:1
|
@ -27,5 +13,19 @@ help: consider removing the attribute
LL | #[unstable(feature = "arbitrary_enum_discriminant", issue = "42")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: can't mark as unstable using an already stable feature
--> $DIR/unstable-attribute-rejects-already-stable-features.rs:7:1
|
LL | #[rustc_const_unstable(feature = "arbitrary_enum_discriminant", issue = "42")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this feature is already stable
LL | const fn my_fun() {}
| -------------------- the stability attribute annotates this item
|
help: consider removing the attribute
--> $DIR/unstable-attribute-rejects-already-stable-features.rs:7:1
|
LL | #[rustc_const_unstable(feature = "arbitrary_enum_discriminant", issue = "42")]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 2 previous errors

View file

@ -94,14 +94,6 @@ LL | #[const_continue]
|
= help: `#[const_continue]` can be applied to
error: `#[const_continue]` should be applied to a break expression
--> $DIR/invalid-attribute.rs:40:9
|
LL | #[const_continue]
| ^^^^^^^^^^^^^^^^^
LL | 5
| - not a break expression
error: `#[loop_match]` should be applied to a loop
--> $DIR/invalid-attribute.rs:39:9
|
@ -111,5 +103,13 @@ LL | #[const_continue]
LL | 5
| - not a loop
error: `#[const_continue]` should be applied to a break expression
--> $DIR/invalid-attribute.rs:40:9
|
LL | #[const_continue]
| ^^^^^^^^^^^^^^^^^
LL | 5
| - not a break expression
error: aborting due to 14 previous errors