Skip to content

Commit d9617c8

Browse files
committed
Auto merge of #150310 - JonathanBrouwer:cfg_trace2, r=jdonszelmann
Port `#[cfg]` and `#[cfg_attr]` trace attributes to the new attribute parsers This PR converts `cfg` and `cfg_trace` attributes to the new parsed representation. The primary challenge is that re-parsing these attributes in the HIR is a performance regression, since these attributes were only used in rustdoc and clippy parsing them in the HIR is extra work that was not done in the compiler before. To solve this, we only parse the attributes once and then store their parsed representation in the AST.
2 parents 0aced20 + e9fdf11 commit d9617c8

38 files changed

Lines changed: 445 additions & 521 deletions

File tree

compiler/rustc_ast/src/ast.rs

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ use rustc_span::source_map::{Spanned, respan};
3434
use rustc_span::{ByteSymbol, DUMMY_SP, ErrorGuaranteed, Ident, Span, Symbol, kw, sym};
3535
use thin_vec::{ThinVec, thin_vec};
3636

37+
use crate::attr::data_structures::CfgEntry;
3738
pub use crate::format::*;
3839
use crate::token::{self, CommentKind, Delimiter};
3940
use crate::tokenstream::{DelimSpan, LazyAttrTokenStream, TokenStream};
@@ -3390,7 +3391,7 @@ impl NormalAttr {
33903391
item: AttrItem {
33913392
unsafety: Safety::Default,
33923393
path: Path::from_ident(ident),
3393-
args: AttrArgs::Empty,
3394+
args: AttrItemKind::Unparsed(AttrArgs::Empty),
33943395
tokens: None,
33953396
},
33963397
tokens: None,
@@ -3402,11 +3403,53 @@ impl NormalAttr {
34023403
pub struct AttrItem {
34033404
pub unsafety: Safety,
34043405
pub path: Path,
3405-
pub args: AttrArgs,
3406+
pub args: AttrItemKind,
34063407
// Tokens for the meta item, e.g. just the `foo` within `#[foo]` or `#![foo]`.
34073408
pub tokens: Option<LazyAttrTokenStream>,
34083409
}
34093410

3411+
/// Some attributes are stored in a parsed form, for performance reasons.
3412+
/// Their arguments don't have to be reparsed everytime they're used
3413+
#[derive(Clone, Encodable, Decodable, Debug, Walkable)]
3414+
pub enum AttrItemKind {
3415+
Parsed(EarlyParsedAttribute),
3416+
Unparsed(AttrArgs),
3417+
}
3418+
3419+
impl AttrItemKind {
3420+
pub fn unparsed(self) -> Option<AttrArgs> {
3421+
match self {
3422+
AttrItemKind::Unparsed(args) => Some(args),
3423+
AttrItemKind::Parsed(_) => None,
3424+
}
3425+
}
3426+
3427+
pub fn unparsed_ref(&self) -> Option<&AttrArgs> {
3428+
match self {
3429+
AttrItemKind::Unparsed(args) => Some(args),
3430+
AttrItemKind::Parsed(_) => None,
3431+
}
3432+
}
3433+
3434+
pub fn span(&self) -> Option<Span> {
3435+
match self {
3436+
AttrItemKind::Unparsed(args) => args.span(),
3437+
AttrItemKind::Parsed(_) => None,
3438+
}
3439+
}
3440+
}
3441+
3442+
/// Some attributes are stored in parsed form in the AST.
3443+
/// This is done for performance reasons, so the attributes don't need to be reparsed on every use.
3444+
///
3445+
/// Currently all early parsed attributes are excluded from pretty printing at rustc_ast_pretty::pprust::state::print_attribute_inline.
3446+
/// When adding new early parsed attributes, consider whether they should be pretty printed.
3447+
#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
3448+
pub enum EarlyParsedAttribute {
3449+
CfgTrace(CfgEntry),
3450+
CfgAttrTrace,
3451+
}
3452+
34103453
impl AttrItem {
34113454
pub fn is_valid_for_outer_style(&self) -> bool {
34123455
self.path == sym::cfg_attr
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
use std::fmt;
2+
3+
use rustc_macros::{Decodable, Encodable, HashStable_Generic};
4+
use rustc_span::{Span, Symbol};
5+
use thin_vec::ThinVec;
6+
7+
use crate::attr::version::RustcVersion;
8+
9+
#[derive(Encodable, Decodable, Clone, Debug, PartialEq, Eq, Hash, HashStable_Generic)]
10+
pub enum CfgEntry {
11+
All(ThinVec<CfgEntry>, Span),
12+
Any(ThinVec<CfgEntry>, Span),
13+
Not(Box<CfgEntry>, Span),
14+
Bool(bool, Span),
15+
NameValue { name: Symbol, value: Option<Symbol>, span: Span },
16+
Version(Option<RustcVersion>, Span),
17+
}
18+
19+
impl CfgEntry {
20+
pub fn lower_spans(&mut self, lower_span: impl Copy + Fn(Span) -> Span) {
21+
match self {
22+
CfgEntry::All(subs, span) | CfgEntry::Any(subs, span) => {
23+
*span = lower_span(*span);
24+
subs.iter_mut().for_each(|sub| sub.lower_spans(lower_span));
25+
}
26+
CfgEntry::Not(sub, span) => {
27+
*span = lower_span(*span);
28+
sub.lower_spans(lower_span);
29+
}
30+
CfgEntry::Bool(_, span)
31+
| CfgEntry::NameValue { span, .. }
32+
| CfgEntry::Version(_, span) => {
33+
*span = lower_span(*span);
34+
}
35+
}
36+
}
37+
38+
pub fn span(&self) -> Span {
39+
let (Self::All(_, span)
40+
| Self::Any(_, span)
41+
| Self::Not(_, span)
42+
| Self::Bool(_, span)
43+
| Self::NameValue { span, .. }
44+
| Self::Version(_, span)) = self;
45+
*span
46+
}
47+
48+
/// Same as `PartialEq` but doesn't check spans and ignore order of cfgs.
49+
pub fn is_equivalent_to(&self, other: &Self) -> bool {
50+
match (self, other) {
51+
(Self::All(a, _), Self::All(b, _)) | (Self::Any(a, _), Self::Any(b, _)) => {
52+
a.len() == b.len() && a.iter().all(|a| b.iter().any(|b| a.is_equivalent_to(b)))
53+
}
54+
(Self::Not(a, _), Self::Not(b, _)) => a.is_equivalent_to(b),
55+
(Self::Bool(a, _), Self::Bool(b, _)) => a == b,
56+
(
57+
Self::NameValue { name: name1, value: value1, .. },
58+
Self::NameValue { name: name2, value: value2, .. },
59+
) => name1 == name2 && value1 == value2,
60+
(Self::Version(a, _), Self::Version(b, _)) => a == b,
61+
_ => false,
62+
}
63+
}
64+
}
65+
66+
impl fmt::Display for CfgEntry {
67+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
68+
fn write_entries(
69+
name: &str,
70+
entries: &[CfgEntry],
71+
f: &mut fmt::Formatter<'_>,
72+
) -> fmt::Result {
73+
write!(f, "{name}(")?;
74+
for (nb, entry) in entries.iter().enumerate() {
75+
if nb != 0 {
76+
f.write_str(", ")?;
77+
}
78+
entry.fmt(f)?;
79+
}
80+
f.write_str(")")
81+
}
82+
match self {
83+
Self::All(entries, _) => write_entries("all", entries, f),
84+
Self::Any(entries, _) => write_entries("any", entries, f),
85+
Self::Not(entry, _) => write!(f, "not({entry})"),
86+
Self::Bool(value, _) => write!(f, "{value}"),
87+
Self::NameValue { name, value, .. } => {
88+
match value {
89+
// We use `as_str` and debug display to have characters escaped and `"`
90+
// characters surrounding the string.
91+
Some(value) => write!(f, "{name} = {:?}", value.as_str()),
92+
None => write!(f, "{name}"),
93+
}
94+
}
95+
Self::Version(version, _) => match version {
96+
Some(version) => write!(f, "{version}"),
97+
None => Ok(()),
98+
},
99+
}
100+
}
101+
}

compiler/rustc_ast/src/attr/mod.rs

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
//! Functions dealing with attributes and meta items.
22
3+
pub mod data_structures;
4+
pub mod version;
5+
36
use std::fmt::Debug;
47
use std::sync::atomic::{AtomicU32, Ordering};
58

@@ -8,6 +11,7 @@ use rustc_span::{Ident, Span, Symbol, sym};
811
use smallvec::{SmallVec, smallvec};
912
use thin_vec::{ThinVec, thin_vec};
1013

14+
use crate::AttrItemKind;
1115
use crate::ast::{
1216
AttrArgs, AttrId, AttrItem, AttrKind, AttrStyle, AttrVec, Attribute, DUMMY_NODE_ID, DelimArgs,
1317
Expr, ExprKind, LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit, NormalAttr, Path,
@@ -62,6 +66,15 @@ impl Attribute {
6266
}
6367
}
6468

69+
/// Replaces the arguments of this attribute with new arguments `AttrItemKind`.
70+
/// This is useful for making this attribute into a trace attribute, and should otherwise be avoided.
71+
pub fn replace_args(&mut self, new_args: AttrItemKind) {
72+
match &mut self.kind {
73+
AttrKind::Normal(normal) => normal.item.args = new_args,
74+
AttrKind::DocComment(..) => panic!("unexpected doc comment"),
75+
}
76+
}
77+
6578
pub fn unwrap_normal_item(self) -> AttrItem {
6679
match self.kind {
6780
AttrKind::Normal(normal) => normal.item,
@@ -77,7 +90,7 @@ impl AttributeExt for Attribute {
7790

7891
fn value_span(&self) -> Option<Span> {
7992
match &self.kind {
80-
AttrKind::Normal(normal) => match &normal.item.args {
93+
AttrKind::Normal(normal) => match &normal.item.args.unparsed_ref()? {
8194
AttrArgs::Eq { expr, .. } => Some(expr.span),
8295
_ => None,
8396
},
@@ -147,7 +160,7 @@ impl AttributeExt for Attribute {
147160

148161
fn is_word(&self) -> bool {
149162
if let AttrKind::Normal(normal) = &self.kind {
150-
matches!(normal.item.args, AttrArgs::Empty)
163+
matches!(normal.item.args, AttrItemKind::Unparsed(AttrArgs::Empty))
151164
} else {
152165
false
153166
}
@@ -303,7 +316,7 @@ impl AttrItem {
303316
}
304317

305318
pub fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {
306-
match &self.args {
319+
match &self.args.unparsed_ref()? {
307320
AttrArgs::Delimited(args) if args.delim == Delimiter::Parenthesis => {
308321
MetaItemKind::list_from_tokens(args.tokens.clone())
309322
}
@@ -324,7 +337,7 @@ impl AttrItem {
324337
/// #[attr("value")]
325338
/// ```
326339
fn value_str(&self) -> Option<Symbol> {
327-
match &self.args {
340+
match &self.args.unparsed_ref()? {
328341
AttrArgs::Eq { expr, .. } => match expr.kind {
329342
ExprKind::Lit(token_lit) => {
330343
LitKind::from_token_lit(token_lit).ok().and_then(|lit| lit.str())
@@ -348,7 +361,7 @@ impl AttrItem {
348361
/// #[attr("value")]
349362
/// ```
350363
fn value_span(&self) -> Option<Span> {
351-
match &self.args {
364+
match &self.args.unparsed_ref()? {
352365
AttrArgs::Eq { expr, .. } => Some(expr.span),
353366
AttrArgs::Delimited(_) | AttrArgs::Empty => None,
354367
}
@@ -364,7 +377,7 @@ impl AttrItem {
364377
}
365378

366379
pub fn meta_kind(&self) -> Option<MetaItemKind> {
367-
MetaItemKind::from_attr_args(&self.args)
380+
MetaItemKind::from_attr_args(self.args.unparsed_ref()?)
368381
}
369382
}
370383

@@ -699,7 +712,13 @@ fn mk_attr(
699712
args: AttrArgs,
700713
span: Span,
701714
) -> Attribute {
702-
mk_attr_from_item(g, AttrItem { unsafety, path, args, tokens: None }, None, style, span)
715+
mk_attr_from_item(
716+
g,
717+
AttrItem { unsafety, path, args: AttrItemKind::Unparsed(args), tokens: None },
718+
None,
719+
style,
720+
span,
721+
)
703722
}
704723

705724
pub fn mk_attr_from_item(
Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,10 @@
1-
use std::borrow::Cow;
21
use std::fmt::{self, Display};
32
use std::sync::OnceLock;
43

5-
use rustc_error_messages::{DiagArgValue, IntoDiagArg};
6-
use rustc_macros::{
7-
BlobDecodable, Encodable, HashStable_Generic, PrintAttribute, current_rustc_version,
8-
};
9-
10-
use crate::attrs::PrintAttribute;
4+
use rustc_macros::{BlobDecodable, Encodable, HashStable_Generic, current_rustc_version};
115

126
#[derive(Encodable, BlobDecodable, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
13-
#[derive(HashStable_Generic, PrintAttribute)]
7+
#[derive(HashStable_Generic)]
148
pub struct RustcVersion {
159
pub major: u16,
1610
pub minor: u16,
@@ -47,9 +41,3 @@ impl Display for RustcVersion {
4741
write!(formatter, "{}.{}.{}", self.major, self.minor, self.patch)
4842
}
4943
}
50-
51-
impl IntoDiagArg for RustcVersion {
52-
fn into_diag_arg(self, _: &mut Option<std::path::PathBuf>) -> DiagArgValue {
53-
DiagArgValue::Str(Cow::Owned(self.to_string()))
54-
}
55-
}

compiler/rustc_ast/src/visit.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,7 @@ macro_rules! common_visitor_and_walkers {
366366
crate::token::LitKind,
367367
crate::tokenstream::LazyAttrTokenStream,
368368
crate::tokenstream::TokenStream,
369+
EarlyParsedAttribute,
369370
Movability,
370371
Mutability,
371372
Pinnedness,
@@ -457,6 +458,7 @@ macro_rules! common_visitor_and_walkers {
457458
ModSpans,
458459
MutTy,
459460
NormalAttr,
461+
AttrItemKind,
460462
Parens,
461463
ParenthesizedArgs,
462464
PatFieldsRest,

compiler/rustc_ast_pretty/src/pprust/state.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -694,7 +694,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere
694694
}
695695
ast::Safety::Default | ast::Safety::Safe(_) => {}
696696
}
697-
match &item.args {
697+
match &item.args.unparsed_ref().expect("Parsed attributes are never printed") {
698698
AttrArgs::Delimited(DelimArgs { dspan: _, delim, tokens }) => self.print_mac_common(
699699
Some(MacHeader::Path(&item.path)),
700700
false,

compiler/rustc_attr_parsing/src/attributes/cfg.rs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -294,11 +294,9 @@ pub fn parse_cfg_attr(
294294
sess: &Session,
295295
features: Option<&Features>,
296296
) -> Option<(CfgEntry, Vec<(AttrItem, Span)>)> {
297-
match cfg_attr.get_normal_item().args {
298-
ast::AttrArgs::Delimited(ast::DelimArgs { dspan, delim, ref tokens })
299-
if !tokens.is_empty() =>
300-
{
301-
check_cfg_attr_bad_delim(&sess.psess, dspan, delim);
297+
match cfg_attr.get_normal_item().args.unparsed_ref().unwrap() {
298+
ast::AttrArgs::Delimited(ast::DelimArgs { dspan, delim, tokens }) if !tokens.is_empty() => {
299+
check_cfg_attr_bad_delim(&sess.psess, *dspan, *delim);
302300
match parse_in(&sess.psess, tokens.clone(), "`cfg_attr` input", |p| {
303301
parse_cfg_attr_internal(p, sess, features, cfg_attr)
304302
}) {
@@ -322,7 +320,7 @@ pub fn parse_cfg_attr(
322320
}
323321
_ => {
324322
let (span, reason) = if let ast::AttrArgs::Delimited(ast::DelimArgs { dspan, .. }) =
325-
cfg_attr.get_normal_item().args
323+
cfg_attr.get_normal_item().args.unparsed_ref()?
326324
{
327325
(dspan.entire(), AttributeParseErrorReason::ExpectedAtLeastOneArgument)
328326
} else {

0 commit comments

Comments
 (0)