1
Fork 0

Rollup merge of #79034 - petrochenkov:mrscopes3, r=eddyb

rustc_resolve: Make `macro_rules` scope chain compression lazy

As suggested in https://github.com/rust-lang/rust/pull/78826#issuecomment-723420664.
This commit is contained in:
Jonas Schievink 2020-11-15 13:39:57 +01:00 committed by GitHub
commit b0178f4cc7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 21 additions and 36 deletions

View file

@ -1163,9 +1163,7 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
let old_parent_scope = self.r.invocation_parent_scopes.insert(invoc_id, self.parent_scope); let old_parent_scope = self.r.invocation_parent_scopes.insert(invoc_id, self.parent_scope);
assert!(old_parent_scope.is_none(), "invocation data is reset for an invocation"); assert!(old_parent_scope.is_none(), "invocation data is reset for an invocation");
let scope = self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Invocation(invoc_id)); self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Invocation(invoc_id))
self.r.invocation_macro_rules_scopes.entry(invoc_id).or_default().insert(scope);
scope
} }
fn proc_macro_stub(&self, item: &ast::Item) -> Option<(MacroKind, Ident, Span)> { fn proc_macro_stub(&self, item: &ast::Item) -> Option<(MacroKind, Ident, Span)> {

View file

@ -976,9 +976,6 @@ pub struct Resolver<'a> {
/// `macro_rules` scopes *produced* by expanding the macro invocations, /// `macro_rules` scopes *produced* by expanding the macro invocations,
/// include all the `macro_rules` items and other invocations generated by them. /// include all the `macro_rules` items and other invocations generated by them.
output_macro_rules_scopes: FxHashMap<ExpnId, MacroRulesScopeRef<'a>>, output_macro_rules_scopes: FxHashMap<ExpnId, MacroRulesScopeRef<'a>>,
/// References to all `MacroRulesScope::Invocation(invoc_id)`s, used to update such scopes
/// when their corresponding `invoc_id`s get expanded.
invocation_macro_rules_scopes: FxHashMap<ExpnId, FxHashSet<MacroRulesScopeRef<'a>>>,
/// Helper attributes that are in scope for the given expansion. /// Helper attributes that are in scope for the given expansion.
helper_attrs: FxHashMap<ExpnId, Vec<Ident>>, helper_attrs: FxHashMap<ExpnId, Vec<Ident>>,
@ -1310,7 +1307,6 @@ impl<'a> Resolver<'a> {
non_macro_attrs: [non_macro_attr(false), non_macro_attr(true)], non_macro_attrs: [non_macro_attr(false), non_macro_attr(true)],
invocation_parent_scopes: Default::default(), invocation_parent_scopes: Default::default(),
output_macro_rules_scopes: Default::default(), output_macro_rules_scopes: Default::default(),
invocation_macro_rules_scopes: Default::default(),
helper_attrs: Default::default(), helper_attrs: Default::default(),
local_macro_def_scopes: FxHashMap::default(), local_macro_def_scopes: FxHashMap::default(),
name_already_seen: FxHashMap::default(), name_already_seen: FxHashMap::default(),
@ -1680,7 +1676,20 @@ impl<'a> Resolver<'a> {
!(expn_id == parent_scope.expansion && macro_kind == Some(MacroKind::Derive)) !(expn_id == parent_scope.expansion && macro_kind == Some(MacroKind::Derive))
} }
Scope::DeriveHelpersCompat => true, Scope::DeriveHelpersCompat => true,
Scope::MacroRules(..) => true, Scope::MacroRules(macro_rules_scope) => {
// Use "path compression" on `macro_rules` scope chains. This is an optimization
// used to avoid long scope chains, see the comments on `MacroRulesScopeRef`.
// As another consequence of this optimization visitors never observe invocation
// scopes for macros that were already expanded.
while let MacroRulesScope::Invocation(invoc_id) = macro_rules_scope.get() {
if let Some(next_scope) = self.output_macro_rules_scopes.get(&invoc_id) {
macro_rules_scope.set(next_scope.get());
} else {
break;
}
}
true
}
Scope::CrateRoot => true, Scope::CrateRoot => true,
Scope::Module(..) => true, Scope::Module(..) => true,
Scope::RegisteredAttrs => use_prelude, Scope::RegisteredAttrs => use_prelude,
@ -1716,11 +1725,9 @@ impl<'a> Resolver<'a> {
MacroRulesScope::Binding(binding) => { MacroRulesScope::Binding(binding) => {
Scope::MacroRules(binding.parent_macro_rules_scope) Scope::MacroRules(binding.parent_macro_rules_scope)
} }
MacroRulesScope::Invocation(invoc_id) => Scope::MacroRules( MacroRulesScope::Invocation(invoc_id) => {
self.output_macro_rules_scopes.get(&invoc_id).cloned().unwrap_or_else( Scope::MacroRules(self.invocation_parent_scopes[&invoc_id].macro_rules)
|| self.invocation_parent_scopes[&invoc_id].macro_rules, }
),
),
MacroRulesScope::Empty => Scope::Module(module), MacroRulesScope::Empty => Scope::Module(module),
}, },
Scope::CrateRoot => match ns { Scope::CrateRoot => match ns {

View file

@ -62,8 +62,8 @@ pub enum MacroRulesScope<'a> {
} }
/// `macro_rules!` scopes are always kept by reference and inside a cell. /// `macro_rules!` scopes are always kept by reference and inside a cell.
/// The reason is that we update all scopes with value `MacroRulesScope::Invocation(invoc_id)` /// The reason is that we update scopes with value `MacroRulesScope::Invocation(invoc_id)`
/// in-place immediately after `invoc_id` gets expanded. /// in-place after `invoc_id` gets expanded.
/// This helps to avoid uncontrollable growth of `macro_rules!` scope chains, /// This helps to avoid uncontrollable growth of `macro_rules!` scope chains,
/// which usually grow lineraly with the number of macro invocations /// which usually grow lineraly with the number of macro invocations
/// in a module (including derives) and hurt performance. /// in a module (including derives) and hurt performance.
@ -173,22 +173,6 @@ impl<'a> ResolverExpand for Resolver<'a> {
let output_macro_rules_scope = self.build_reduced_graph(fragment, parent_scope); let output_macro_rules_scope = self.build_reduced_graph(fragment, parent_scope);
self.output_macro_rules_scopes.insert(expansion, output_macro_rules_scope); self.output_macro_rules_scopes.insert(expansion, output_macro_rules_scope);
// Update all `macro_rules` scopes referring to this invocation. This is an optimization
// used to avoid long scope chains, see the comments on `MacroRulesScopeRef`.
if let Some(invocation_scopes) = self.invocation_macro_rules_scopes.remove(&expansion) {
for invocation_scope in &invocation_scopes {
invocation_scope.set(output_macro_rules_scope.get());
}
// All `macro_rules` scopes that previously referred to `expansion`
// are now rerouted to its output scope, if it's also an invocation.
if let MacroRulesScope::Invocation(invoc_id) = output_macro_rules_scope.get() {
self.invocation_macro_rules_scopes
.entry(invoc_id)
.or_default()
.extend(invocation_scopes);
}
}
parent_scope.module.unexpanded_invocations.borrow_mut().remove(&expansion); parent_scope.module.unexpanded_invocations.borrow_mut().remove(&expansion);
} }
@ -687,11 +671,7 @@ impl<'a> Resolver<'a> {
{ {
Ok((macro_rules_binding.binding, Flags::MACRO_RULES)) Ok((macro_rules_binding.binding, Flags::MACRO_RULES))
} }
MacroRulesScope::Invocation(invoc_id) MacroRulesScope::Invocation(_) => Err(Determinacy::Undetermined),
if !this.output_macro_rules_scopes.contains_key(&invoc_id) =>
{
Err(Determinacy::Undetermined)
}
_ => Err(Determinacy::Determined), _ => Err(Determinacy::Determined),
}, },
Scope::CrateRoot => { Scope::CrateRoot => {