Skip to content

Commit fbf1bec

Browse files
committed
resolve/expand: Cache intermediate results of #[derive] expansion
1 parent b1ea261 commit fbf1bec

File tree

5 files changed

+97
-73
lines changed

5 files changed

+97
-73
lines changed

compiler/rustc_builtin_macros/src/derive.rs

+32-25
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::cfg_eval::cfg_eval;
22

3-
use rustc_ast::{self as ast, token, ItemKind, MetaItemKind, NestedMetaItem, StmtKind};
3+
use rustc_ast::{self as ast, attr, token, ItemKind, MetaItemKind, NestedMetaItem, StmtKind};
44
use rustc_errors::{struct_span_err, Applicability};
55
use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier};
66
use rustc_feature::AttributeTemplate;
@@ -26,32 +26,39 @@ impl MultiItemModifier for Expander {
2626
return ExpandResult::Ready(vec![item]);
2727
}
2828

29-
let template =
30-
AttributeTemplate { list: Some("Trait1, Trait2, ..."), ..Default::default() };
31-
let attr = ecx.attribute(meta_item.clone());
32-
validate_attr::check_builtin_attribute(&sess.parse_sess, &attr, sym::derive, template);
29+
let result =
30+
ecx.resolver.resolve_derives(ecx.current_expansion.id, ecx.force_mode, &|| {
31+
let template =
32+
AttributeTemplate { list: Some("Trait1, Trait2, ..."), ..Default::default() };
33+
let attr = attr::mk_attr_outer(meta_item.clone());
34+
validate_attr::check_builtin_attribute(
35+
&sess.parse_sess,
36+
&attr,
37+
sym::derive,
38+
template,
39+
);
3340

34-
let derives: Vec<_> = attr
35-
.meta_item_list()
36-
.unwrap_or_default()
37-
.into_iter()
38-
.filter_map(|nested_meta| match nested_meta {
39-
NestedMetaItem::MetaItem(meta) => Some(meta),
40-
NestedMetaItem::Literal(lit) => {
41-
// Reject `#[derive("Debug")]`.
42-
report_unexpected_literal(sess, &lit);
43-
None
44-
}
45-
})
46-
.map(|meta| {
47-
// Reject `#[derive(Debug = "value", Debug(abc))]`, but recover the paths.
48-
report_path_args(sess, &meta);
49-
meta.path
50-
})
51-
.collect();
41+
attr.meta_item_list()
42+
.unwrap_or_default()
43+
.into_iter()
44+
.filter_map(|nested_meta| match nested_meta {
45+
NestedMetaItem::MetaItem(meta) => Some(meta),
46+
NestedMetaItem::Literal(lit) => {
47+
// Reject `#[derive("Debug")]`.
48+
report_unexpected_literal(sess, &lit);
49+
None
50+
}
51+
})
52+
.map(|meta| {
53+
// Reject `#[derive(Debug = "value", Debug(abc))]`, but recover the paths.
54+
report_path_args(sess, &meta);
55+
meta.path
56+
})
57+
.map(|path| (path, None))
58+
.collect()
59+
});
5260

53-
// FIXME: Try to cache intermediate results to avoid collecting same paths multiple times.
54-
match ecx.resolver.resolve_derives(ecx.current_expansion.id, derives, ecx.force_mode) {
61+
match result {
5562
Ok(()) => ExpandResult::Ready(cfg_eval(ecx, item)),
5663
Err(Indeterminate) => ExpandResult::Retry(item),
5764
}

compiler/rustc_expand/src/base.rs

+4-5
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,8 @@ impl SyntaxExtension {
868868
/// Error type that denotes indeterminacy.
869869
pub struct Indeterminate;
870870

871+
pub type DeriveResolutions = Vec<(ast::Path, Option<Lrc<SyntaxExtension>>)>;
872+
871873
pub trait ResolverExpand {
872874
fn next_node_id(&mut self) -> NodeId;
873875

@@ -904,15 +906,12 @@ pub trait ResolverExpand {
904906
fn resolve_derives(
905907
&mut self,
906908
expn_id: ExpnId,
907-
derives: Vec<ast::Path>,
908909
force: bool,
910+
derive_paths: &dyn Fn() -> DeriveResolutions,
909911
) -> Result<(), Indeterminate>;
910912
/// Take resolutions for paths inside the `#[derive(...)]` attribute with the given `ExpnId`
911913
/// back from resolver.
912-
fn take_derive_resolutions(
913-
&mut self,
914-
expn_id: ExpnId,
915-
) -> Option<Vec<(Lrc<SyntaxExtension>, ast::Path)>>;
914+
fn take_derive_resolutions(&mut self, expn_id: ExpnId) -> Option<DeriveResolutions>;
916915
/// Path resolution logic for `#[cfg_accessible(path)]`.
917916
fn cfg_accessible(&mut self, expn_id: ExpnId, path: &ast::Path) -> Result<bool, Indeterminate>;
918917
}

compiler/rustc_expand/src/expand.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -515,7 +515,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> {
515515
invocations.reserve(derives.len());
516516
derives
517517
.into_iter()
518-
.map(|(_exts, path)| {
518+
.map(|(path, _exts)| {
519519
// FIXME: Consider using the derive resolutions (`_exts`)
520520
// instead of enqueuing the derives to be resolved again later.
521521
let expn_id = ExpnId::fresh(None);

compiler/rustc_resolve/src/lib.rs

+11-4
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
3737
use rustc_data_structures::ptr_key::PtrKey;
3838
use rustc_data_structures::sync::Lrc;
3939
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
40-
use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind};
40+
use rustc_expand::base::{DeriveResolutions, SyntaxExtension, SyntaxExtensionKind};
4141
use rustc_hir::def::Namespace::*;
4242
use rustc_hir::def::{self, CtorOf, DefKind, NonMacroAttrKind, PartialRes};
4343
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, CRATE_DEF_INDEX};
@@ -851,6 +851,12 @@ enum BuiltinMacroState {
851851
AlreadySeen(Span),
852852
}
853853

854+
struct DeriveData {
855+
resolutions: DeriveResolutions,
856+
helper_attrs: Vec<Ident>,
857+
has_derive_copy: bool,
858+
}
859+
854860
/// The main resolver class.
855861
///
856862
/// This is the visitor that walks the whole crate.
@@ -973,8 +979,9 @@ pub struct Resolver<'a> {
973979
output_macro_rules_scopes: FxHashMap<ExpnId, MacroRulesScopeRef<'a>>,
974980
/// Helper attributes that are in scope for the given expansion.
975981
helper_attrs: FxHashMap<ExpnId, Vec<Ident>>,
976-
/// Resolutions for paths inside the `#[derive(...)]` attribute with the given `ExpnId`.
977-
derive_resolutions: FxHashMap<ExpnId, Vec<(Lrc<SyntaxExtension>, ast::Path)>>,
982+
/// Ready or in-progress results of resolving paths inside the `#[derive(...)]` attribute
983+
/// with the given `ExpnId`.
984+
derive_data: FxHashMap<ExpnId, DeriveData>,
978985

979986
/// Avoid duplicated errors for "name already defined".
980987
name_already_seen: FxHashMap<Symbol, Span>,
@@ -1310,7 +1317,7 @@ impl<'a> Resolver<'a> {
13101317
invocation_parent_scopes: Default::default(),
13111318
output_macro_rules_scopes: Default::default(),
13121319
helper_attrs: Default::default(),
1313-
derive_resolutions: Default::default(),
1320+
derive_data: Default::default(),
13141321
local_macro_def_scopes: FxHashMap::default(),
13151322
name_already_seen: FxHashMap::default(),
13161323
potentially_unused_imports: Vec::new(),

compiler/rustc_resolve/src/macros.rs

+49-38
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
use crate::imports::ImportResolver;
55
use crate::Namespace::*;
66
use crate::{AmbiguityError, AmbiguityErrorMisc, AmbiguityKind, BuiltinMacroState, Determinacy};
7-
use crate::{CrateLint, ParentScope, ResolutionError, Resolver, Scope, ScopeSet, Weak};
7+
use crate::{CrateLint, DeriveData, ParentScope, ResolutionError, Resolver, Scope, ScopeSet, Weak};
88
use crate::{ModuleKind, ModuleOrUniformRoot, NameBinding, PathResult, Segment, ToNameBinding};
99
use rustc_ast::{self as ast, Inline, ItemKind, ModKind, NodeId};
1010
use rustc_ast_lowering::ResolverAstLowering;
@@ -14,8 +14,8 @@ use rustc_data_structures::fx::FxHashSet;
1414
use rustc_data_structures::ptr_key::PtrKey;
1515
use rustc_data_structures::sync::Lrc;
1616
use rustc_errors::struct_span_err;
17-
use rustc_expand::base::Annotatable;
18-
use rustc_expand::base::{Indeterminate, ResolverExpand, SyntaxExtension, SyntaxExtensionKind};
17+
use rustc_expand::base::{Annotatable, DeriveResolutions, Indeterminate, ResolverExpand};
18+
use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind};
1919
use rustc_expand::compile_declarative_macro;
2020
use rustc_expand::expand::{AstFragment, Invocation, InvocationKind, SupportsMacroExpansion};
2121
use rustc_feature::is_builtin_attr_name;
@@ -359,58 +359,69 @@ impl<'a> ResolverExpand for Resolver<'a> {
359359
fn resolve_derives(
360360
&mut self,
361361
expn_id: ExpnId,
362-
derives: Vec<ast::Path>,
363362
force: bool,
363+
derive_paths: &dyn Fn() -> DeriveResolutions,
364364
) -> Result<(), Indeterminate> {
365365
// Block expansion of the container until we resolve all derives in it.
366366
// This is required for two reasons:
367367
// - Derive helper attributes are in scope for the item to which the `#[derive]`
368368
// is applied, so they have to be produced by the container's expansion rather
369369
// than by individual derives.
370370
// - Derives in the container need to know whether one of them is a built-in `Copy`.
371-
// FIXME: Try to cache intermediate results to avoid resolving same derives multiple times.
371+
// Temporarily take the data to avoid borrow checker conflicts.
372+
let mut derive_data = mem::take(&mut self.derive_data);
373+
let entry = derive_data.entry(expn_id).or_insert_with(|| DeriveData {
374+
resolutions: derive_paths(),
375+
helper_attrs: Vec::new(),
376+
has_derive_copy: false,
377+
});
372378
let parent_scope = self.invocation_parent_scopes[&expn_id];
373-
let mut exts = Vec::new();
374-
let mut helper_attrs = Vec::new();
375-
let mut has_derive_copy = false;
376-
for path in derives {
377-
exts.push((
378-
match self.resolve_macro_path(
379-
&path,
380-
Some(MacroKind::Derive),
381-
&parent_scope,
382-
true,
383-
force,
384-
) {
385-
Ok((Some(ext), _)) => {
386-
let span =
387-
path.segments.last().unwrap().ident.span.normalize_to_macros_2_0();
388-
helper_attrs
389-
.extend(ext.helper_attrs.iter().map(|name| Ident::new(*name, span)));
390-
has_derive_copy |= ext.builtin_name == Some(sym::Copy);
391-
ext
392-
}
393-
Ok(_) | Err(Determinacy::Determined) => self.dummy_ext(MacroKind::Derive),
394-
Err(Determinacy::Undetermined) => return Err(Indeterminate),
395-
},
396-
path,
397-
))
379+
for (path, opt_ext) in &mut entry.resolutions {
380+
if opt_ext.is_none() {
381+
*opt_ext = Some(
382+
match self.resolve_macro_path(
383+
&path,
384+
Some(MacroKind::Derive),
385+
&parent_scope,
386+
true,
387+
force,
388+
) {
389+
Ok((Some(ext), _)) => {
390+
if !ext.helper_attrs.is_empty() {
391+
let last_seg = path.segments.last().unwrap();
392+
let span = last_seg.ident.span.normalize_to_macros_2_0();
393+
entry.helper_attrs.extend(
394+
ext.helper_attrs.iter().map(|name| Ident::new(*name, span)),
395+
);
396+
}
397+
entry.has_derive_copy |= ext.builtin_name == Some(sym::Copy);
398+
ext
399+
}
400+
Ok(_) | Err(Determinacy::Determined) => self.dummy_ext(MacroKind::Derive),
401+
Err(Determinacy::Undetermined) => {
402+
assert!(self.derive_data.is_empty());
403+
self.derive_data = derive_data;
404+
return Err(Indeterminate);
405+
}
406+
},
407+
);
408+
}
398409
}
399-
self.derive_resolutions.insert(expn_id, exts);
400-
self.helper_attrs.insert(expn_id, helper_attrs);
410+
// If we get to here, then `derive_data` for the given `expn_id` will only be accessed by
411+
// `take_derive_resolutions` later, so we can steal `helper_attrs` instead of cloning them.
412+
self.helper_attrs.insert(expn_id, mem::take(&mut entry.helper_attrs));
401413
// Mark this derive as having `Copy` either if it has `Copy` itself or if its parent derive
402414
// has `Copy`, to support cases like `#[derive(Clone, Copy)] #[derive(Debug)]`.
403-
if has_derive_copy || self.has_derive_copy(parent_scope.expansion) {
415+
if entry.has_derive_copy || self.has_derive_copy(parent_scope.expansion) {
404416
self.containers_deriving_copy.insert(expn_id);
405417
}
418+
assert!(self.derive_data.is_empty());
419+
self.derive_data = derive_data;
406420
Ok(())
407421
}
408422

409-
fn take_derive_resolutions(
410-
&mut self,
411-
expn_id: ExpnId,
412-
) -> Option<Vec<(Lrc<SyntaxExtension>, ast::Path)>> {
413-
self.derive_resolutions.remove(&expn_id)
423+
fn take_derive_resolutions(&mut self, expn_id: ExpnId) -> Option<DeriveResolutions> {
424+
self.derive_data.remove(&expn_id).map(|data| data.resolutions)
414425
}
415426

416427
// The function that implements the resolution logic of `#[cfg_accessible(path)]`.

0 commit comments

Comments
 (0)