From fbc96e18ad0c2a3f5be7e4ef003c720146b0a52d Mon Sep 17 00:00:00 2001 From: Jeffrey Seyfried Date: Thu, 6 Oct 2016 08:04:30 +0000 Subject: [PATCH] Persistent macro scopes. --- src/librustc_resolve/build_reduced_graph.rs | 69 +++++--- src/librustc_resolve/lib.rs | 15 +- src/librustc_resolve/macros.rs | 184 +++++++++++++------- 3 files changed, 168 insertions(+), 100 deletions(-) diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 6d9786c8109..c1c58232054 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -13,7 +13,7 @@ //! Here we build the "reduced graph": the graph of the module tree without //! any imports resolved. -use macros; +use macros::{InvocationData, LegacyImports, LegacyScope}; use resolve_imports::ImportDirectiveSubclass::{self, GlobImport}; use {Module, ModuleS, ModuleKind}; use Namespace::{self, TypeNS, ValueNS}; @@ -84,7 +84,7 @@ impl<'b> Resolver<'b> { } /// Constructs the reduced graph for one item. - fn build_reduced_graph_for_item(&mut self, item: &Item, expansion: Mark) { + fn build_reduced_graph_for_item(&mut self, item: &Item, legacy_imports: &mut LegacyImports) { let parent = self.current_module; let name = item.ident.name; let sp = item.span; @@ -200,16 +200,9 @@ impl<'b> Resolver<'b> { LoadedMacroKind::Def(mut def) => { let name = def.ident.name; if def.use_locally { - let ext = macro_rules::compile(&self.session.parse_sess, &def); - let shadowing = - self.resolve_macro_name(Mark::root(), name, false).is_some(); - self.invocations[&Mark::root()].module.get().macros.borrow_mut() - .insert(name, macros::NameBinding { - ext: Rc::new(ext), - expansion: expansion, - shadowing: shadowing, - span: loaded_macro.import_site, - }); + let ext = + Rc::new(macro_rules::compile(&self.session.parse_sess, &def)); + legacy_imports.insert(name, (ext, loaded_macro.import_site)); self.macro_names.insert(name); } if def.export { @@ -250,7 +243,6 @@ impl<'b> Resolver<'b> { attr::contains_name(&item.attrs, "no_implicit_prelude") }, normal_ancestor_id: Some(item.id), - macros_escape: self.contains_macro_use(&item.attrs), ..ModuleS::new(Some(parent), ModuleKind::Def(def, name)) }); self.define(parent, name, TypeNS, (module, sp, vis)); @@ -520,22 +512,26 @@ impl<'b> Resolver<'b> { pub struct BuildReducedGraphVisitor<'a, 'b: 'a> { pub resolver: &'a mut Resolver<'b>, - pub expansion: Mark, + pub legacy_scope: LegacyScope<'b>, + pub legacy_imports: LegacyImports, } impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { - fn visit_invoc(&mut self, id: ast::NodeId) { - let mark = Mark::from_placeholder_id(id); - self.resolver.invocations[&mark].module.set(self.resolver.current_module); + fn visit_invoc(&mut self, id: ast::NodeId) -> &'b InvocationData<'b> { + let invocation = self.resolver.invocations[&Mark::from_placeholder_id(id)]; + invocation.module.set(self.resolver.current_module); + invocation.legacy_scope.set(self.legacy_scope); + invocation } } macro_rules! method { ($visit:ident: $ty:ty, $invoc:path, $walk:ident) => { fn $visit(&mut self, node: &$ty) { - match node.node { - $invoc(..) => self.visit_invoc(node.id), - _ => visit::$walk(self, node), + if let $invoc(..) = node.node { + self.visit_invoc(node.id); + } else { + visit::$walk(self, node); } } } @@ -543,22 +539,35 @@ macro_rules! method { impl<'a, 'b> Visitor for BuildReducedGraphVisitor<'a, 'b> { method!(visit_impl_item: ast::ImplItem, ast::ImplItemKind::Macro, walk_impl_item); - method!(visit_stmt: ast::Stmt, ast::StmtKind::Mac, walk_stmt); method!(visit_expr: ast::Expr, ast::ExprKind::Mac, walk_expr); method!(visit_pat: ast::Pat, ast::PatKind::Mac, walk_pat); method!(visit_ty: ast::Ty, ast::TyKind::Mac, walk_ty); fn visit_item(&mut self, item: &Item) { - match item.node { + let macro_use = match item.node { ItemKind::Mac(..) if item.id == ast::DUMMY_NODE_ID => return, // Scope placeholder - ItemKind::Mac(..) => return self.visit_invoc(item.id), - _ => {} - } + ItemKind::Mac(..) => { + return self.legacy_scope = LegacyScope::Expansion(self.visit_invoc(item.id)); + } + ItemKind::Mod(..) => self.resolver.contains_macro_use(&item.attrs), + _ => false, + }; - let parent = self.resolver.current_module; - self.resolver.build_reduced_graph_for_item(item, self.expansion); + let (parent, legacy_scope) = (self.resolver.current_module, self.legacy_scope); + self.resolver.build_reduced_graph_for_item(item, &mut self.legacy_imports); visit::walk_item(self, item); self.resolver.current_module = parent; + if !macro_use { + self.legacy_scope = legacy_scope; + } + } + + fn visit_stmt(&mut self, stmt: &ast::Stmt) { + if let ast::StmtKind::Mac(..) = stmt.node { + self.legacy_scope = LegacyScope::Expansion(self.visit_invoc(stmt.id)); + } else { + visit::walk_stmt(self, stmt); + } } fn visit_foreign_item(&mut self, foreign_item: &ForeignItem) { @@ -567,10 +576,11 @@ impl<'a, 'b> Visitor for BuildReducedGraphVisitor<'a, 'b> { } fn visit_block(&mut self, block: &Block) { - let parent = self.resolver.current_module; + let (parent, legacy_scope) = (self.resolver.current_module, self.legacy_scope); self.resolver.build_reduced_graph_for_block(block); visit::walk_block(self, block); self.resolver.current_module = parent; + self.legacy_scope = legacy_scope; } fn visit_trait_item(&mut self, item: &TraitItem) { @@ -578,7 +588,8 @@ impl<'a, 'b> Visitor for BuildReducedGraphVisitor<'a, 'b> { let def_id = parent.def_id().unwrap(); if let TraitItemKind::Macro(_) = item.node { - return self.visit_invoc(item.id); + self.visit_invoc(item.id); + return } // Add the item to the trait info. diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 2507d747452..219c2abff2c 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -57,6 +57,7 @@ use syntax::ext::base::MultiItemModifier; use syntax::ext::hygiene::Mark; use syntax::ast::{self, FloatTy}; use syntax::ast::{CRATE_NODE_ID, Name, NodeId, IntTy, UintTy}; +use syntax::ext::base::SyntaxExtension; use syntax::parse::token::{self, keywords}; use syntax::util::lev_distance::find_best_match_for_name; @@ -77,7 +78,7 @@ use std::mem::replace; use std::rc::Rc; use resolve_imports::{ImportDirective, NameResolution}; -use macros::InvocationData; +use macros::{InvocationData, LegacyBinding}; // NB: This module needs to be declared first so diagnostics are // registered before they are used. @@ -792,9 +793,6 @@ pub struct ModuleS<'a> { // access the children must be preceded with a // `populate_module_if_necessary` call. populated: Cell, - - macros: RefCell>, - macros_escape: bool, } pub type Module<'a> = &'a ModuleS<'a>; @@ -812,8 +810,6 @@ impl<'a> ModuleS<'a> { globs: RefCell::new((Vec::new())), traits: RefCell::new(None), populated: Cell::new(true), - macros: RefCell::new(FnvHashMap()), - macros_escape: false, } } @@ -1087,6 +1083,7 @@ pub struct Resolver<'a> { pub derive_modes: FnvHashMap>, crate_loader: &'a mut CrateLoader, macro_names: FnvHashSet, + builtin_macros: FnvHashMap>, // Maps the `Mark` of an expansion to its containing module or block. invocations: FnvHashMap>, @@ -1099,6 +1096,7 @@ pub struct ResolverArenas<'a> { import_directives: arena::TypedArena>, name_resolutions: arena::TypedArena>>, invocation_data: arena::TypedArena>, + legacy_bindings: arena::TypedArena>, } impl<'a> ResolverArenas<'a> { @@ -1126,6 +1124,9 @@ impl<'a> ResolverArenas<'a> { -> &'a InvocationData<'a> { self.invocation_data.alloc(expansion_data) } + fn alloc_legacy_binding(&'a self, binding: LegacyBinding<'a>) -> &'a LegacyBinding<'a> { + self.legacy_bindings.alloc(binding) + } } impl<'a> ty::NodeIdTree for Resolver<'a> { @@ -1273,6 +1274,7 @@ impl<'a> Resolver<'a> { derive_modes: FnvHashMap(), crate_loader: crate_loader, macro_names: FnvHashSet(), + builtin_macros: FnvHashMap(), invocations: invocations, } } @@ -1285,6 +1287,7 @@ impl<'a> Resolver<'a> { import_directives: arena::TypedArena::new(), name_resolutions: arena::TypedArena::new(), invocation_data: arena::TypedArena::new(), + legacy_bindings: arena::TypedArena::new(), } } diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 650642cee70..31b0180f6f1 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -12,6 +12,7 @@ use {Module, Resolver}; use build_reduced_graph::BuildReducedGraphVisitor; use rustc::hir::def_id::{CRATE_DEF_INDEX, DefIndex}; use rustc::hir::map::{self, DefCollector}; +use rustc::util::nodemap::FnvHashMap; use std::cell::Cell; use std::rc::Rc; use syntax::ast; @@ -19,41 +20,58 @@ use syntax::errors::DiagnosticBuilder; use syntax::ext::base::{self, MultiModifier, MultiDecorator, MultiItemModifier}; use syntax::ext::base::{NormalTT, SyntaxExtension}; use syntax::ext::expand::{Expansion, Invocation, InvocationKind}; -use syntax::ext::hygiene::{Mark, SyntaxContext}; +use syntax::ext::hygiene::Mark; use syntax::ext::tt::macro_rules; use syntax::parse::token::intern; use syntax::util::lev_distance::find_best_match_for_name; -use syntax_pos::{Span, DUMMY_SP}; - -// FIXME(jseyfried) Merge with `::NameBinding`. -pub struct NameBinding { - pub ext: Rc, - pub expansion: Mark, - pub shadowing: bool, - pub span: Span, -} +use syntax_pos::Span; #[derive(Clone)] pub struct InvocationData<'a> { - backtrace: SyntaxContext, pub module: Cell>, def_index: DefIndex, // True if this expansion is in a `const_integer` position, for example `[u32; m!()]`. // c.f. `DefCollector::visit_ast_const_integer`. const_integer: bool, + // The scope in which the invocation path is resolved. + pub legacy_scope: Cell>, + // The smallest scope that includes this invocation's expansion, + // or `Empty` if this invocation has not been expanded yet. + pub expansion: Cell>, } impl<'a> InvocationData<'a> { pub fn root(graph_root: Module<'a>) -> Self { InvocationData { - backtrace: SyntaxContext::empty(), module: Cell::new(graph_root), def_index: CRATE_DEF_INDEX, const_integer: false, + legacy_scope: Cell::new(LegacyScope::Empty), + expansion: Cell::new(LegacyScope::Empty), } } } +#[derive(Copy, Clone)] +pub enum LegacyScope<'a> { + Empty, + Invocation(&'a InvocationData<'a>), // The scope of the invocation, not including its expansion + Expansion(&'a InvocationData<'a>), // The scope of the invocation, including its expansion + Binding(&'a LegacyBinding<'a>), +} + +pub struct LegacyBinding<'a> { + parent: LegacyScope<'a>, + kind: LegacyBindingKind, +} + +pub enum LegacyBindingKind { + MacroRules(ast::Name, Rc, Span), + MacroUse(LegacyImports), +} + +pub type LegacyImports = FnvHashMap, Span)>; + impl<'a> base::Resolver for Resolver<'a> { fn next_node_id(&mut self) -> ast::NodeId { self.session.next_node_id() @@ -63,18 +81,36 @@ impl<'a> base::Resolver for Resolver<'a> { let mark = Mark::fresh(); let module = self.module_map[&id]; self.invocations.insert(mark, self.arenas.alloc_invocation_data(InvocationData { - backtrace: SyntaxContext::empty(), module: Cell::new(module), def_index: module.def_id().unwrap().index, const_integer: false, + legacy_scope: Cell::new(LegacyScope::Empty), + expansion: Cell::new(LegacyScope::Empty), })); mark } fn visit_expansion(&mut self, mark: Mark, expansion: &Expansion) { - self.collect_def_ids(mark, expansion); - self.current_module = self.invocations[&mark].module.get(); - expansion.visit_with(&mut BuildReducedGraphVisitor { resolver: self, expansion: mark }); + let invocation = self.invocations[&mark]; + self.collect_def_ids(invocation, expansion); + + self.current_module = invocation.module.get(); + let mut visitor = BuildReducedGraphVisitor { + resolver: self, + legacy_scope: LegacyScope::Invocation(invocation), + legacy_imports: FnvHashMap(), + }; + expansion.visit_with(&mut visitor); + invocation.expansion.set(visitor.legacy_scope); + + if !visitor.legacy_imports.is_empty() { + invocation.legacy_scope.set({ + LegacyScope::Binding(self.arenas.alloc_legacy_binding(LegacyBinding { + parent: invocation.legacy_scope.get(), + kind: LegacyBindingKind::MacroUse(visitor.legacy_imports), + })) + }); + } } fn add_macro(&mut self, scope: Mark, mut def: ast::MacroDef) { @@ -83,17 +119,12 @@ impl<'a> base::Resolver for Resolver<'a> { } if def.use_locally { let invocation = self.invocations[&scope]; - let mut module = invocation.module.get(); - while module.macros_escape { - module = module.parent.unwrap(); - } - let binding = NameBinding { - ext: Rc::new(macro_rules::compile(&self.session.parse_sess, &def)), - expansion: invocation.backtrace.data().prev_ctxt.data().outer_mark, - shadowing: self.resolve_macro_name(scope, def.ident.name, false).is_some(), - span: def.span, - }; - module.macros.borrow_mut().insert(def.ident.name, binding); + let ext = Rc::new(macro_rules::compile(&self.session.parse_sess, &def)); + let binding = self.arenas.alloc_legacy_binding(LegacyBinding { + parent: invocation.legacy_scope.get(), + kind: LegacyBindingKind::MacroRules(def.ident.name, ext, def.span), + }); + invocation.legacy_scope.set(LegacyScope::Binding(binding)); self.macro_names.insert(def.ident.name); } if def.export { @@ -106,12 +137,7 @@ impl<'a> base::Resolver for Resolver<'a> { if let NormalTT(..) = *ext { self.macro_names.insert(ident.name); } - self.graph_root.macros.borrow_mut().insert(ident.name, NameBinding { - ext: ext, - expansion: Mark::root(), - shadowing: false, - span: DUMMY_SP, - }); + self.builtin_macros.insert(ident.name, ext); } fn add_expansions_at_stmt(&mut self, id: ast::NodeId, macros: Vec) { @@ -121,8 +147,8 @@ impl<'a> base::Resolver for Resolver<'a> { fn find_attr_invoc(&mut self, attrs: &mut Vec) -> Option { for i in 0..attrs.len() { let name = intern(&attrs[i].name()); - match self.invocations[&Mark::root()].module.get().macros.borrow().get(&name) { - Some(binding) => match *binding.ext { + match self.builtin_macros.get(&name) { + Some(ext) => match **ext { MultiModifier(..) | MultiDecorator(..) | SyntaxExtension::AttrProcMacro(..) => { return Some(attrs.remove(i)) } @@ -149,6 +175,7 @@ impl<'a> base::Resolver for Resolver<'a> { InvocationKind::Attr { ref attr, .. } => (intern(&*attr.name()), attr.span), }; + let scope = self.invocations[&scope].legacy_scope.get(); self.resolve_macro_name(scope, name, true).or_else(|| { let mut err = self.session.struct_span_err(span, &format!("macro undefined: '{}!'", name)); @@ -164,37 +191,63 @@ impl<'a> base::Resolver for Resolver<'a> { } impl<'a> Resolver<'a> { - pub fn resolve_macro_name(&mut self, scope: Mark, name: ast::Name, record_used: bool) - -> Option> { - let invocation = self.invocations[&scope]; - let mut module = invocation.module.get(); - loop { - if let Some(binding) = module.macros.borrow().get(&name) { - let mut backtrace = invocation.backtrace.data(); - while binding.expansion != backtrace.outer_mark { - if backtrace.outer_mark != Mark::root() { - backtrace = backtrace.prev_ctxt.data(); - continue - } + fn resolve_macro_name(&mut self, + mut scope: LegacyScope<'a>, + name: ast::Name, + record_used: bool) + -> Option> { + let check_shadowing = |this: &mut Self, relative_depth, scope, span| { + if record_used && relative_depth > 0 && + this.resolve_macro_name(scope, name, false).is_some() && + this.macro_shadowing_errors.insert(span) { + let msg = format!("`{}` is already in scope", name); + this.session.struct_span_err(span, &msg) + .note("macro-expanded `macro_rules!`s and `#[macro_use]`s \ + may not shadow existing macros (see RFC 1560)") + .emit(); + } + }; - if record_used && binding.shadowing && - self.macro_shadowing_errors.insert(binding.span) { - let msg = format!("`{}` is already in scope", name); - self.session.struct_span_err(binding.span, &msg) - .note("macro-expanded `macro_rules!`s and `#[macro_use]`s \ - may not shadow existing macros (see RFC 1560)") - .emit(); + let mut relative_depth: u32 = 0; + loop { + scope = match scope { + LegacyScope::Empty => break, + LegacyScope::Expansion(invocation) => { + if let LegacyScope::Empty = invocation.expansion.get() { + invocation.legacy_scope.get() + } else { + relative_depth += 1; + invocation.expansion.get() } - break } - return Some(binding.ext.clone()); - } - match module.parent { - Some(parent) => module = parent, - None => break, - } + LegacyScope::Invocation(invocation) => { + let new_relative_depth = relative_depth.saturating_sub(1); + let mut scope = invocation.legacy_scope.get(); + if let LegacyScope::Binding(binding) = scope { + match binding.kind { + LegacyBindingKind::MacroUse(ref imports) => { + if let Some(&(ref ext, span)) = imports.get(&name) { + check_shadowing(self, relative_depth, binding.parent, span); + return Some(ext.clone()); + } + }, + LegacyBindingKind::MacroRules(name_, ref ext, span) => { + if name_ == name { + check_shadowing(self, new_relative_depth, binding.parent, span); + return Some(ext.clone()); + } + } + } + scope = binding.parent + } + relative_depth = new_relative_depth; + scope + } + _ => unreachable!(), + }; } - None + + self.builtin_macros.get(&name).cloned() } fn suggest_macro_name(&mut self, name: &str, err: &mut DiagnosticBuilder<'a>) { @@ -207,17 +260,18 @@ impl<'a> Resolver<'a> { } } - fn collect_def_ids(&mut self, mark: Mark, expansion: &Expansion) { + fn collect_def_ids(&mut self, invocation: &'a InvocationData<'a>, expansion: &Expansion) { let Resolver { ref mut invocations, arenas, graph_root, .. } = *self; - let InvocationData { def_index, const_integer, backtrace, .. } = invocations[&mark].clone(); + let InvocationData { def_index, const_integer, .. } = *invocation; let visit_macro_invoc = &mut |invoc: map::MacroInvocationData| { invocations.entry(invoc.mark).or_insert_with(|| { arenas.alloc_invocation_data(InvocationData { - backtrace: backtrace.apply_mark(invoc.mark), def_index: invoc.def_index, const_integer: invoc.const_integer, module: Cell::new(graph_root), + expansion: Cell::new(LegacyScope::Empty), + legacy_scope: Cell::new(LegacyScope::Empty), }) }); };