diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 5ec8dd0156ab1..1e4114add814d 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -393,7 +393,7 @@ impl<'a> LoweringContext<'a> { } fn allow_internal_unstable(&self, reason: &'static str, mut span: Span) -> Span { - let mark = Mark::fresh(); + let mark = Mark::fresh(Mark::root()); mark.set_expn_info(codemap::ExpnInfo { call_site: span, callee: codemap::NameAndSpan { diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs index 4e16c97ca4d86..9d098557367cd 100644 --- a/src/librustc_metadata/cstore_impl.rs +++ b/src/librustc_metadata/cstore_impl.rs @@ -388,7 +388,7 @@ impl CrateStore for cstore::CStore { attrs: attrs.iter().cloned().collect(), node: ast::ItemKind::MacroDef(ast::MacroDef { tokens: body.into(), - legacy: true, + legacy: def.legacy, }), vis: ast::Visibility::Inherited, }) diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 93fcdc455e5dd..07c475949d435 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -1104,6 +1104,7 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { Entry { kind: EntryKind::MacroDef(self.lazy(&MacroDef { body: pprust::tts_to_string(¯o_def.body.trees().collect::>()), + legacy: macro_def.legacy, })), visibility: self.lazy(&ty::Visibility::Public), span: self.lazy(¯o_def.span), diff --git a/src/librustc_metadata/schema.rs b/src/librustc_metadata/schema.rs index 91a22d922199d..2ffe7cc02aaac 100644 --- a/src/librustc_metadata/schema.rs +++ b/src/librustc_metadata/schema.rs @@ -433,9 +433,10 @@ impl_stable_hash_for!(struct ModData { reexports }); #[derive(RustcEncodable, RustcDecodable)] pub struct MacroDef { pub body: String, + pub legacy: bool, } -impl_stable_hash_for!(struct MacroDef { body }); +impl_stable_hash_for!(struct MacroDef { body, legacy }); #[derive(RustcEncodable, RustcDecodable)] pub struct FnData { diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 18798962df1be..72f555b47cc9a 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -23,7 +23,7 @@ use {resolve_error, resolve_struct_error, ResolutionError}; use rustc::middle::cstore::LoadedMacro; use rustc::hir::def::*; -use rustc::hir::def_id::{CrateNum, BUILTIN_MACROS_CRATE, CRATE_DEF_INDEX, DefId}; +use rustc::hir::def_id::{BUILTIN_MACROS_CRATE, CRATE_DEF_INDEX, LOCAL_CRATE, DefId}; use rustc::ty; use std::cell::Cell; @@ -150,7 +150,7 @@ impl<'a> Resolver<'a> { view_path.span, ResolutionError::SelfImportsOnlyAllowedWithin); } else if source_name == "$crate" && full_path.segments.len() == 1 { - let crate_root = self.resolve_crate_var(source.ctxt, item.span); + let crate_root = self.resolve_crate_root(source.ctxt); let crate_name = match crate_root.kind { ModuleKind::Def(_, name) => name, ModuleKind::Block(..) => unreachable!(), @@ -247,7 +247,8 @@ impl<'a> Resolver<'a> { // n.b. we don't need to look at the path option here, because cstore already did let crate_id = self.session.cstore.extern_mod_stmt_cnum(item.id).unwrap(); - let module = self.get_extern_crate_root(crate_id, item.span); + let module = + self.get_module(DefId { krate: crate_id, index: CRATE_DEF_INDEX }); self.populate_module_if_necessary(module); let used = self.process_legacy_macro_imports(item, module, expansion); let binding = @@ -279,7 +280,7 @@ impl<'a> Resolver<'a> { no_implicit_prelude: parent.no_implicit_prelude || { attr::contains_name(&item.attrs, "no_implicit_prelude") }, - ..ModuleData::new(Some(parent), module_kind, def_id, item.span) + ..ModuleData::new(Some(parent), module_kind, def_id, expansion, item.span) }); self.define(parent, ident, TypeNS, (module, vis, sp, expansion)); self.module_map.insert(def_id, module); @@ -317,6 +318,7 @@ impl<'a> Resolver<'a> { let module = self.new_module(parent, module_kind, parent.normal_ancestor_id, + expansion, item.span); self.define(parent, ident, TypeNS, (module, vis, sp, expansion)); @@ -376,6 +378,7 @@ impl<'a> Resolver<'a> { let module = self.new_module(parent, module_kind, parent.normal_ancestor_id, + expansion, item.span); self.define(parent, ident, TypeNS, (module, vis, sp, expansion)); self.current_module = module; @@ -421,12 +424,13 @@ impl<'a> Resolver<'a> { self.define(parent, item.ident, ValueNS, (def, vis, item.span, expansion)); } - fn build_reduced_graph_for_block(&mut self, block: &Block) { + fn build_reduced_graph_for_block(&mut self, block: &Block, expansion: Mark) { let parent = self.current_module; if self.block_needs_anonymous_module(block) { let module = self.new_module(parent, ModuleKind::Block(block.id), parent.normal_ancestor_id, + expansion, block.span); self.block_map.insert(block.id, module); self.current_module = module; // Descend into the block. @@ -440,23 +444,24 @@ impl<'a> Resolver<'a> { let def_id = def.def_id(); let vis = self.session.cstore.visibility(def_id); let span = child.span; - + let expansion = Mark::root(); // FIXME(jseyfried) intercrate hygiene match def { Def::Mod(..) | Def::Enum(..) => { let module = self.new_module(parent, ModuleKind::Def(def, ident.name), def_id, + expansion, span); - self.define(parent, ident, TypeNS, (module, vis, DUMMY_SP, Mark::root())); + self.define(parent, ident, TypeNS, (module, vis, DUMMY_SP, expansion)); } Def::Variant(..) | Def::TyAlias(..) => { - self.define(parent, ident, TypeNS, (def, vis, DUMMY_SP, Mark::root())); + self.define(parent, ident, TypeNS, (def, vis, DUMMY_SP, expansion)); } Def::Fn(..) | Def::Static(..) | Def::Const(..) | Def::VariantCtor(..) => { - self.define(parent, ident, ValueNS, (def, vis, DUMMY_SP, Mark::root())); + self.define(parent, ident, ValueNS, (def, vis, DUMMY_SP, expansion)); } Def::StructCtor(..) => { - self.define(parent, ident, ValueNS, (def, vis, DUMMY_SP, Mark::root())); + self.define(parent, ident, ValueNS, (def, vis, DUMMY_SP, expansion)); if let Some(struct_def_id) = self.session.cstore.def_key(def_id).parent @@ -469,14 +474,15 @@ impl<'a> Resolver<'a> { let module = self.new_module(parent, module_kind, parent.normal_ancestor_id, + expansion, span); - self.define(parent, ident, TypeNS, (module, vis, DUMMY_SP, Mark::root())); + self.define(parent, ident, TypeNS, (module, vis, DUMMY_SP, expansion)); for child in self.session.cstore.item_children(def_id) { let ns = if let Def::AssociatedTy(..) = child.def { TypeNS } else { ValueNS }; let ident = Ident::with_empty_ctxt(child.name); self.define(module, ident, ns, (child.def, ty::Visibility::Public, - DUMMY_SP, Mark::root())); + DUMMY_SP, expansion)); if self.session.cstore.associated_item_cloned(child.def.def_id()) .method_has_self_argument { @@ -486,31 +492,42 @@ impl<'a> Resolver<'a> { module.populated.set(true); } Def::Struct(..) | Def::Union(..) => { - self.define(parent, ident, TypeNS, (def, vis, DUMMY_SP, Mark::root())); + self.define(parent, ident, TypeNS, (def, vis, DUMMY_SP, expansion)); // Record field names for error reporting. let field_names = self.session.cstore.struct_field_names(def_id); self.insert_field_names(def_id, field_names); } Def::Macro(..) => { - self.define(parent, ident, MacroNS, (def, vis, DUMMY_SP, Mark::root())); + self.define(parent, ident, MacroNS, (def, vis, DUMMY_SP, expansion)); } _ => bug!("unexpected definition: {:?}", def) } } - fn get_extern_crate_root(&mut self, cnum: CrateNum, span: Span) -> Module<'a> { - let def_id = DefId { krate: cnum, index: CRATE_DEF_INDEX }; - let name = self.session.cstore.crate_name(cnum); - let macros_only = self.session.cstore.dep_kind(cnum).macros_only(); - let module_kind = ModuleKind::Def(Def::Mod(def_id), name); - let arenas = self.arenas; - *self.extern_crate_roots.entry((cnum, macros_only)).or_insert_with(|| { - arenas.alloc_module(ModuleData::new(None, module_kind, def_id, span)) - }) + pub fn get_module(&mut self, def_id: DefId) -> Module<'a> { + if def_id.krate == LOCAL_CRATE { + return self.module_map[&def_id] + } + + let macros_only = self.session.cstore.dep_kind(def_id.krate).macros_only(); + if let Some(&module) = self.extern_module_map.get(&(def_id, macros_only)) { + return module; + } + + let (name, parent) = if def_id.index == CRATE_DEF_INDEX { + (self.session.cstore.crate_name(def_id.krate), None) + } else { + let def_key = self.session.cstore.def_key(def_id); + (def_key.disambiguated_data.data.get_opt_name().unwrap(), + Some(self.get_module(DefId { index: def_key.parent.unwrap(), ..def_id }))) + }; + + let kind = ModuleKind::Def(Def::Mod(def_id), name); + self.arenas.alloc_module(ModuleData::new(parent, kind, def_id, Mark::root(), DUMMY_SP)) } - pub fn macro_def_scope(&mut self, expansion: Mark, span: Span) -> Module<'a> { + pub fn macro_def_scope(&mut self, expansion: Mark) -> Module<'a> { let def_id = self.macro_defs[&expansion]; if let Some(id) = self.definitions.as_local_node_id(def_id) { self.local_macro_def_scopes[&id] @@ -519,7 +536,7 @@ impl<'a> Resolver<'a> { self.graph_root } else { let module_def_id = ty::DefIdTree::parent(&*self, def_id).unwrap(); - self.get_extern_crate_root(module_def_id.krate, span) + self.get_module(module_def_id) } } @@ -766,7 +783,7 @@ impl<'a, 'b> Visitor<'a> for BuildReducedGraphVisitor<'a, 'b> { fn visit_block(&mut self, block: &'a Block) { let (parent, legacy_scope) = (self.resolver.current_module, self.legacy_scope); - self.resolver.build_reduced_graph_for_block(block); + self.resolver.build_reduced_graph_for_block(block, self.expansion); visit::walk_block(self, block); self.resolver.current_module = parent; self.legacy_scope = legacy_scope; diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 6d8ca09935cff..180a608e63104 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -43,7 +43,7 @@ use rustc::middle::cstore::CrateLoader; use rustc::session::Session; use rustc::lint; use rustc::hir::def::*; -use rustc::hir::def_id::{CrateNum, CRATE_DEF_INDEX, LOCAL_CRATE, DefId}; +use rustc::hir::def_id::{CRATE_DEF_INDEX, LOCAL_CRATE, DefId}; use rustc::ty; use rustc::hir::{Freevar, FreevarMap, TraitCandidate, TraitMap, GlobMap}; use rustc::util::nodemap::{NodeMap, NodeSet, FxHashMap, FxHashSet, DefIdMap}; @@ -51,7 +51,7 @@ use rustc::util::nodemap::{NodeMap, NodeSet, FxHashMap, FxHashSet, DefIdMap}; use syntax::ext::hygiene::{Mark, SyntaxContext}; use syntax::ast::{self, Name, NodeId, Ident, SpannedIdent, FloatTy, IntTy, UintTy}; use syntax::ext::base::SyntaxExtension; -use syntax::ext::base::Determinacy::{Determined, Undetermined}; +use syntax::ext::base::Determinacy::{self, Determined, Undetermined}; use syntax::ext::base::MacroKind; use syntax::symbol::{Symbol, keywords}; use syntax::util::lev_distance::find_best_match_for_name; @@ -861,6 +861,8 @@ pub struct ModuleData<'a> { /// Span of the module itself. Used for error reporting. span: Span, + + expansion: Mark, } pub type Module<'a> = &'a ModuleData<'a>; @@ -869,6 +871,7 @@ impl<'a> ModuleData<'a> { fn new(parent: Option>, kind: ModuleKind, normal_ancestor_id: DefId, + expansion: Mark, span: Span) -> Self { ModuleData { parent: parent, @@ -884,6 +887,7 @@ impl<'a> ModuleData<'a> { traits: RefCell::new(None), populated: Cell::new(normal_ancestor_id.is_local()), span: span, + expansion: expansion, } } @@ -1165,7 +1169,7 @@ pub struct Resolver<'a> { // entry block for `f`. block_map: NodeMap>, module_map: FxHashMap>, - extern_crate_roots: FxHashMap<(CrateNum, bool /* MacrosOnly? */), Module<'a>>, + extern_module_map: FxHashMap<(DefId, bool /* MacrosOnly? */), Module<'a>>, pub make_glob_map: bool, // Maps imports to the names of items actually imported (this actually maps @@ -1185,9 +1189,9 @@ pub struct Resolver<'a> { use_extern_macros: bool, // true if `#![feature(use_extern_macros)]` crate_loader: &'a mut CrateLoader, - macro_names: FxHashSet, + macro_names: FxHashSet, global_macros: FxHashMap>, - lexical_macro_resolutions: Vec<(Name, &'a Cell>)>, + lexical_macro_resolutions: Vec<(Ident, &'a Cell>)>, macro_map: FxHashMap>, macro_defs: FxHashMap, local_macro_def_scopes: FxHashMap>, @@ -1310,7 +1314,7 @@ impl<'a> Resolver<'a> { let root_module_kind = ModuleKind::Def(Def::Mod(root_def_id), keywords::Invalid.name()); let graph_root = arenas.alloc_module(ModuleData { no_implicit_prelude: attr::contains_name(&krate.attrs, "no_implicit_prelude"), - ..ModuleData::new(None, root_module_kind, root_def_id, krate.span) + ..ModuleData::new(None, root_module_kind, root_def_id, Mark::root(), krate.span) }); let mut module_map = FxHashMap(); module_map.insert(DefId::local(CRATE_DEF_INDEX), graph_root); @@ -1364,7 +1368,7 @@ impl<'a> Resolver<'a> { trait_map: NodeMap(), module_map: module_map, block_map: NodeMap(), - extern_crate_roots: FxHashMap(), + extern_module_map: FxHashMap(), make_glob_map: make_glob_map == MakeGlobMap::Yes, glob_map: NodeMap(), @@ -1449,9 +1453,11 @@ impl<'a> Resolver<'a> { parent: Module<'a>, kind: ModuleKind, normal_ancestor_id: DefId, + expansion: Mark, span: Span, ) -> Module<'a> { - self.arenas.alloc_module(ModuleData::new(Some(parent), kind, normal_ancestor_id, span)) + let module = ModuleData::new(Some(parent), kind, normal_ancestor_id, expansion, span); + self.arenas.alloc_module(module) } fn record_use(&mut self, ident: Ident, ns: Namespace, binding: &'a NameBinding<'a>, span: Span) @@ -1513,10 +1519,11 @@ impl<'a> Resolver<'a> { path_span: Span) -> Option> { if ns == TypeNS { - ident = ident.unhygienize(); + ident.ctxt = ident.ctxt.modern(); } // Walk backwards up the ribs in scope. + let mut module = self.graph_root; for i in (0 .. self.ribs[ns].len()).rev() { if let Some(def) = self.ribs[ns][i].bindings.get(&ident).cloned() { // The ident resolves to a type parameter or local variable. @@ -1525,45 +1532,120 @@ impl<'a> Resolver<'a> { )); } - if let ModuleRibKind(module) = self.ribs[ns][i].kind { - let item = self.resolve_ident_in_module(module, ident, ns, false, - record_used, path_span); - if let Ok(binding) = item { - // The ident resolves to an item. - return Some(LexicalScopeBinding::Item(binding)); + module = match self.ribs[ns][i].kind { + ModuleRibKind(module) => module, + MacroDefinition(def) if def == self.macro_defs[&ident.ctxt.outer()] => { + // If an invocation of this macro created `ident`, give up on `ident` + // and switch to `ident`'s source from the macro definition. + ident.ctxt.remove_mark(); + continue } + _ => continue, + }; - if let ModuleKind::Block(..) = module.kind { // We can see through blocks - } else if !module.no_implicit_prelude { - return self.prelude.and_then(|prelude| { - self.resolve_ident_in_module(prelude, ident, ns, false, - false, path_span).ok() - }).map(LexicalScopeBinding::Item) - } else { - return None; - } + let item = self.resolve_ident_in_module_unadjusted( + module, ident, ns, false, record_used, path_span, + ); + if let Ok(binding) = item { + // The ident resolves to an item. + return Some(LexicalScopeBinding::Item(binding)); } - if let MacroDefinition(def) = self.ribs[ns][i].kind { - // If an invocation of this macro created `ident`, give up on `ident` - // and switch to `ident`'s source from the macro definition. - let ctxt_data = ident.ctxt.data(); - if def == self.macro_defs[&ctxt_data.outer_mark] { - ident.ctxt = ctxt_data.prev_ctxt; - } + match module.kind { + ModuleKind::Block(..) => {}, // We can see through blocks + _ => break, + } + } + + ident.ctxt = ident.ctxt.modern(); + loop { + module = unwrap_or!(self.hygienic_lexical_parent(module, &mut ident.ctxt), break); + let orig_current_module = self.current_module; + self.current_module = module; // Lexical resolutions can never be a privacy error. + let result = self.resolve_ident_in_module_unadjusted( + module, ident, ns, false, record_used, path_span, + ); + self.current_module = orig_current_module; + + match result { + Ok(binding) => return Some(LexicalScopeBinding::Item(binding)), + Err(Undetermined) => return None, + Err(Determined) => {} + } + } + + match self.prelude { + Some(prelude) if !module.no_implicit_prelude => { + self.resolve_ident_in_module_unadjusted(prelude, ident, ns, false, false, path_span) + .ok().map(LexicalScopeBinding::Item) + } + _ => None, + } + } + + fn hygienic_lexical_parent(&mut self, mut module: Module<'a>, ctxt: &mut SyntaxContext) + -> Option> { + if !module.expansion.is_descendant_of(ctxt.outer()) { + return Some(self.macro_def_scope(ctxt.remove_mark())); + } + + if let ModuleKind::Block(..) = module.kind { + return Some(module.parent.unwrap()); + } + + let mut module_expansion = module.expansion.modern(); // for backward compatability + while let Some(parent) = module.parent { + let parent_expansion = parent.expansion.modern(); + if module_expansion.is_descendant_of(parent_expansion) && + parent_expansion != module_expansion { + return if parent_expansion.is_descendant_of(ctxt.outer()) { + Some(parent) + } else { + None + }; } + module = parent; + module_expansion = parent_expansion; } None } - fn resolve_crate_var(&mut self, crate_var_ctxt: SyntaxContext, span: Span) -> Module<'a> { - let mut ctxt_data = crate_var_ctxt.data(); - while ctxt_data.prev_ctxt != SyntaxContext::empty() { - ctxt_data = ctxt_data.prev_ctxt.data(); + fn resolve_ident_in_module(&mut self, + module: Module<'a>, + mut ident: Ident, + ns: Namespace, + ignore_unresolved_invocations: bool, + record_used: bool, + span: Span) + -> Result<&'a NameBinding<'a>, Determinacy> { + ident.ctxt = ident.ctxt.modern(); + let orig_current_module = self.current_module; + if let Some(def) = ident.ctxt.adjust(module.expansion) { + self.current_module = self.macro_def_scope(def); + } + let result = self.resolve_ident_in_module_unadjusted( + module, ident, ns, ignore_unresolved_invocations, record_used, span, + ); + self.current_module = orig_current_module; + result + } + + fn resolve_crate_root(&mut self, mut ctxt: SyntaxContext) -> Module<'a> { + let module = match ctxt.adjust(Mark::root()) { + Some(def) => self.macro_def_scope(def), + None => return self.graph_root, + }; + self.get_module(DefId { index: CRATE_DEF_INDEX, ..module.normal_ancestor_id }) + } + + fn resolve_self(&mut self, ctxt: &mut SyntaxContext, module: Module<'a>) -> Module<'a> { + let mut module = self.get_module(module.normal_ancestor_id); + while module.span.ctxt.modern() != *ctxt { + let parent = module.parent.unwrap_or_else(|| self.macro_def_scope(ctxt.remove_mark())); + module = self.get_module(parent.normal_ancestor_id); } - let module = self.macro_def_scope(ctxt_data.outer_mark, span); - if module.is_local() { self.graph_root } else { module } + module } // AST resolution @@ -1611,15 +1693,12 @@ impl<'a> Resolver<'a> { fn search_label(&self, mut ident: Ident) -> Option { for rib in self.label_ribs.iter().rev() { match rib.kind { - NormalRibKind => { - // Continue - } + NormalRibKind => {} + // If an invocation of this macro created `ident`, give up on `ident` + // and switch to `ident`'s source from the macro definition. MacroDefinition(def) => { - // If an invocation of this macro created `ident`, give up on `ident` - // and switch to `ident`'s source from the macro definition. - let ctxt_data = ident.ctxt.data(); - if def == self.macro_defs[&ctxt_data.outer_mark] { - ident.ctxt = ctxt_data.prev_ctxt; + if def == self.macro_defs[&ident.ctxt.outer()] { + ident.ctxt.remove_mark(); } } _ => { @@ -1751,7 +1830,7 @@ impl<'a> Resolver<'a> { let mut function_type_rib = Rib::new(rib_kind); let mut seen_bindings = FxHashMap(); for type_parameter in &generics.ty_params { - let ident = type_parameter.ident.unhygienize(); + let ident = type_parameter.ident.modern(); debug!("with_type_parameter_rib: {}", type_parameter.id); if seen_bindings.contains_key(&ident) { @@ -2504,7 +2583,7 @@ impl<'a> Resolver<'a> { } let is_global = self.global_macros.get(&path[0].name).cloned() .map(|binding| binding.get_macro(self).kind() == MacroKind::Bang).unwrap_or(false); - if primary_ns != MacroNS && (is_global || self.macro_names.contains(&path[0].name)) { + if primary_ns != MacroNS && (is_global || self.macro_names.contains(&path[0].modern())) { // Return some dummy definition, it's enough for error reporting. return Some( PathResolution::new(Def::Macro(DefId::local(CRATE_DEF_INDEX), MacroKind::Bang)) @@ -2613,13 +2692,17 @@ impl<'a> Resolver<'a> { let ns = if is_last { opt_ns.unwrap_or(TypeNS) } else { TypeNS }; if i == 0 && ns == TypeNS && ident.name == keywords::SelfValue.name() { - module = Some(self.module_map[&self.current_module.normal_ancestor_id]); + let mut ctxt = ident.ctxt.modern(); + module = Some(self.resolve_self(&mut ctxt, self.current_module)); continue } else if allow_super && ns == TypeNS && ident.name == keywords::Super.name() { - let current_module = if i == 0 { self.current_module } else { module.unwrap() }; - let self_module = self.module_map[¤t_module.normal_ancestor_id]; + let mut ctxt = ident.ctxt.modern(); + let self_module = match i { + 0 => self.resolve_self(&mut ctxt, self.current_module), + _ => module.unwrap(), + }; if let Some(parent) = self_module.parent { - module = Some(self.module_map[&parent.normal_ancestor_id]); + module = Some(self.resolve_self(&mut ctxt, parent)); continue } else { let msg = "There are too many initial `super`s.".to_string(); @@ -2629,10 +2712,10 @@ impl<'a> Resolver<'a> { allow_super = false; if i == 0 && ns == TypeNS && ident.name == keywords::CrateRoot.name() { - module = Some(self.graph_root); + module = Some(self.resolve_crate_root(ident.ctxt.modern())); continue } else if i == 0 && ns == TypeNS && ident.name == "$crate" { - module = Some(self.resolve_crate_var(ident.ctxt, path_span)); + module = Some(self.resolve_crate_root(ident.ctxt)); continue } @@ -3108,7 +3191,8 @@ impl<'a> Resolver<'a> { } } - fn get_traits_containing_item(&mut self, ident: Ident, ns: Namespace) -> Vec { + fn get_traits_containing_item(&mut self, mut ident: Ident, ns: Namespace) + -> Vec { debug!("(getting traits containing item) looking for '{}'", ident.name); let mut found_traits = Vec::new(); @@ -3120,13 +3204,12 @@ impl<'a> Resolver<'a> { } } + ident.ctxt = ident.ctxt.modern(); let mut search_module = self.current_module; loop { self.get_traits_in_module_containing_item(ident, ns, search_module, &mut found_traits); - match search_module.kind { - ModuleKind::Block(..) => search_module = search_module.parent.unwrap(), - _ => break, - } + search_module = + unwrap_or!(self.hygienic_lexical_parent(search_module, &mut ident.ctxt), break); } if let Some(prelude) = self.prelude { @@ -3157,7 +3240,12 @@ impl<'a> Resolver<'a> { for &(trait_name, binding) in traits.as_ref().unwrap().iter() { let module = binding.module().unwrap(); - if self.resolve_ident_in_module(module, ident, ns, false, false, module.span).is_ok() { + let mut ident = ident; + if ident.ctxt.glob_adjust(module.expansion, binding.span.ctxt.modern()).is_none() { + continue + } + if self.resolve_ident_in_module_unadjusted(module, ident, ns, false, false, module.span) + .is_ok() { let import_id = match binding.kind { NameBindingKind::Import { directive, .. } => { self.maybe_unused_trait_imports.insert(directive.id); @@ -3348,15 +3436,15 @@ impl<'a> Resolver<'a> { } fn report_shadowing_errors(&mut self) { - for (name, scope) in replace(&mut self.lexical_macro_resolutions, Vec::new()) { - self.resolve_legacy_scope(scope, name, true); + for (ident, scope) in replace(&mut self.lexical_macro_resolutions, Vec::new()) { + self.resolve_legacy_scope(scope, ident, true); } let mut reported_errors = FxHashSet(); for binding in replace(&mut self.disallowed_shadowing, Vec::new()) { - if self.resolve_legacy_scope(&binding.parent, binding.name, false).is_some() && - reported_errors.insert((binding.name, binding.span)) { - let msg = format!("`{}` is already in scope", binding.name); + if self.resolve_legacy_scope(&binding.parent, binding.ident, false).is_some() && + reported_errors.insert((binding.ident, binding.span)) { + let msg = format!("`{}` is already in scope", binding.ident); self.session.struct_span_err(binding.span, &msg) .note("macro-expanded `macro_rules!`s may not shadow \ existing macros (see RFC 1560)") diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index bf21344330bca..9aba892e0ff5e 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -76,7 +76,7 @@ pub enum LegacyScope<'a> { pub struct LegacyBinding<'a> { pub parent: Cell>, - pub name: ast::Name, + pub ident: Ident, def_id: DefId, pub span: Span, } @@ -110,7 +110,7 @@ impl<'a> base::Resolver for Resolver<'a> { } fn get_module_scope(&mut self, id: ast::NodeId) -> Mark { - let mark = Mark::fresh(); + let mark = Mark::fresh(Mark::root()); let module = self.module_map[&self.definitions.local_def_id(id)]; self.invocations.insert(mark, self.arenas.alloc_invocation_data(InvocationData { module: Cell::new(module), @@ -130,7 +130,7 @@ impl<'a> base::Resolver for Resolver<'a> { let ident = path.segments[0].identifier; if ident.name == "$crate" { path.segments[0].identifier.name = keywords::CrateRoot.name(); - let module = self.0.resolve_crate_var(ident.ctxt, self.1); + let module = self.0.resolve_crate_root(ident.ctxt); if !module.is_local() { let span = path.segments[0].span; path.segments.insert(1, match module.kind { @@ -292,7 +292,11 @@ impl<'a> base::Resolver for Resolver<'a> { }; self.macro_defs.insert(invoc.expansion_data.mark, def.def_id()); self.unused_macros.remove(&def.def_id()); - Ok(Some(self.get_macro(def))) + let ext = self.get_macro(def); + if ext.is_modern() { + invoc.expansion_data.mark.set_modern(); + } + Ok(Some(ext)) } fn resolve_macro(&mut self, scope: Mark, path: &ast::Path, kind: MacroKind, force: bool) @@ -416,8 +420,7 @@ impl<'a> Resolver<'a> { return def; } - let name = path[0].name; - let legacy_resolution = self.resolve_legacy_scope(&invocation.legacy_scope, name, false); + let legacy_resolution = self.resolve_legacy_scope(&invocation.legacy_scope, path[0], false); let result = if let Some(MacroBinding::Legacy(binding)) = legacy_resolution { Ok(Def::Macro(binding.def_id, MacroKind::Bang)) } else { @@ -439,26 +442,31 @@ impl<'a> Resolver<'a> { // Resolve the initial segment of a non-global macro path (e.g. `foo` in `foo::bar!();`) pub fn resolve_lexical_macro_path_segment(&mut self, - ident: Ident, + mut ident: Ident, ns: Namespace, record_used: bool, path_span: Span) -> Result, Determinacy> { + ident = ident.modern(); let mut module = Some(self.current_module); let mut potential_illegal_shadower = Err(Determinacy::Determined); let determinacy = if record_used { Determinacy::Determined } else { Determinacy::Undetermined }; loop { + let orig_current_module = self.current_module; let result = if let Some(module) = module { + self.current_module = module; // Lexical resolutions can never be a privacy error. // Since expanded macros may not shadow the lexical scope and // globs may not shadow global macros (both enforced below), // we resolve with restricted shadowing (indicated by the penultimate argument). - self.resolve_ident_in_module(module, ident, ns, true, record_used, path_span) - .map(MacroBinding::Modern) + self.resolve_ident_in_module_unadjusted( + module, ident, ns, true, record_used, path_span, + ).map(MacroBinding::Modern) } else { self.global_macros.get(&ident.name).cloned().ok_or(determinacy) .map(MacroBinding::Global) }; + self.current_module = orig_current_module; match result.map(MacroBinding::binding) { Ok(binding) => { @@ -491,10 +499,7 @@ impl<'a> Resolver<'a> { } module = match module { - Some(module) => match module.kind { - ModuleKind::Block(..) => module.parent, - ModuleKind::Def(..) => None, - }, + Some(module) => self.hygienic_lexical_parent(module, &mut ident.ctxt), None => return potential_illegal_shadower, } } @@ -502,9 +507,10 @@ impl<'a> Resolver<'a> { pub fn resolve_legacy_scope(&mut self, mut scope: &'a Cell>, - name: Name, + ident: Ident, record_used: bool) -> Option> { + let ident = ident.modern(); let mut possible_time_travel = None; let mut relative_depth: u32 = 0; let mut binding = None; @@ -531,7 +537,7 @@ impl<'a> Resolver<'a> { scope = &invocation.legacy_scope; } LegacyScope::Binding(potential_binding) => { - if potential_binding.name == name { + if potential_binding.ident == ident { if (!self.use_extern_macros || record_used) && relative_depth > 0 { self.disallowed_shadowing.push(potential_binding); } @@ -545,9 +551,9 @@ impl<'a> Resolver<'a> { let binding = if let Some(binding) = binding { MacroBinding::Legacy(binding) - } else if let Some(binding) = self.global_macros.get(&name).cloned() { + } else if let Some(binding) = self.global_macros.get(&ident.name).cloned() { if !self.use_extern_macros { - self.record_use(Ident::with_empty_ctxt(name), MacroNS, binding, DUMMY_SP); + self.record_use(ident, MacroNS, binding, DUMMY_SP); } MacroBinding::Global(binding) } else { @@ -557,7 +563,7 @@ impl<'a> Resolver<'a> { if !self.use_extern_macros { if let Some(scope) = possible_time_travel { // Check for disallowed shadowing later - self.lexical_macro_resolutions.push((name, scope)); + self.lexical_macro_resolutions.push((ident, scope)); } } @@ -578,7 +584,7 @@ impl<'a> Resolver<'a> { for &(mark, ident, span, kind) in module.legacy_macro_resolutions.borrow().iter() { let legacy_scope = &self.invocations[&mark].legacy_scope; - let legacy_resolution = self.resolve_legacy_scope(legacy_scope, ident.name, true); + let legacy_resolution = self.resolve_legacy_scope(legacy_scope, ident, true); let resolution = self.resolve_lexical_macro_path_segment(ident, MacroNS, true, span); match (legacy_resolution, resolution) { (Some(MacroBinding::Legacy(legacy_binding)), Ok(MacroBinding::Modern(binding))) => { @@ -615,7 +621,7 @@ impl<'a> Resolver<'a> { err: &mut DiagnosticBuilder<'a>, span: Span) { // First check if this is a locally-defined bang macro. let suggestion = if let MacroKind::Bang = kind { - find_best_match_for_name(self.macro_names.iter(), name, None) + find_best_match_for_name(self.macro_names.iter().map(|ident| &ident.name), name, None) } else { None // Then check global macros. @@ -705,9 +711,10 @@ impl<'a> Resolver<'a> { let def = match item.node { ast::ItemKind::MacroDef(ref def) => def, _ => unreachable!() }; if def.legacy { - self.macro_names.insert(ident.name); + let ident = ident.modern(); + self.macro_names.insert(ident); *legacy_scope = LegacyScope::Binding(self.arenas.alloc_legacy_binding(LegacyBinding { - parent: Cell::new(*legacy_scope), name: ident.name, def_id: def_id, span: item.span, + parent: Cell::new(*legacy_scope), ident: ident, def_id: def_id, span: item.span, })); if attr::contains_name(&item.attrs, "macro_export") { let def = Def::Macro(def_id, MacroKind::Bang); diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index fdca931ad4784..f304aad4638ce 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -134,21 +134,20 @@ impl<'a> NameResolution<'a> { impl<'a> Resolver<'a> { fn resolution(&self, module: Module<'a>, ident: Ident, ns: Namespace) -> &'a RefCell> { - let ident = ident.unhygienize(); - *module.resolutions.borrow_mut().entry((ident, ns)) + *module.resolutions.borrow_mut().entry((ident.modern(), ns)) .or_insert_with(|| self.arenas.alloc_name_resolution()) } /// Attempts to resolve `ident` in namespaces `ns` of `module`. /// Invariant: if `record_used` is `Some`, import resolution must be complete. - pub fn resolve_ident_in_module(&mut self, - module: Module<'a>, - ident: Ident, - ns: Namespace, - restricted_shadowing: bool, - record_used: bool, - path_span: Span) - -> Result<&'a NameBinding<'a>, Determinacy> { + pub fn resolve_ident_in_module_unadjusted(&mut self, + module: Module<'a>, + ident: Ident, + ns: Namespace, + restricted_shadowing: bool, + record_used: bool, + path_span: Span) + -> Result<&'a NameBinding<'a>, Determinacy> { self.populate_module_if_necessary(module); let resolution = self.resolution(module, ident, ns) @@ -233,20 +232,22 @@ impl<'a> Resolver<'a> { return Err(Determined); } for directive in module.globs.borrow().iter() { - if self.is_accessible(directive.vis.get()) { - if let Some(module) = directive.imported_module.get() { - let result = self.resolve_ident_in_module(module, - ident, - ns, - false, - false, - path_span); - if let Err(Undetermined) = result { - return Err(Undetermined); - } - } else { - return Err(Undetermined); - } + if !self.is_accessible(directive.vis.get()) { + continue + } + let module = unwrap_or!(directive.imported_module.get(), return Err(Undetermined)); + let (orig_current_module, mut ident) = (self.current_module, ident.modern()); + match ident.ctxt.glob_adjust(module.expansion, directive.span.ctxt.modern()) { + Some(Some(def)) => self.current_module = self.macro_def_scope(def), + Some(None) => {} + None => continue, + }; + let result = self.resolve_ident_in_module_unadjusted( + module, ident, ns, false, false, path_span, + ); + self.current_module = orig_current_module; + if let Err(Undetermined) = result { + return Err(Undetermined); } } @@ -394,7 +395,14 @@ impl<'a> Resolver<'a> { // Define `binding` in `module`s glob importers. for directive in module.glob_importers.borrow_mut().iter() { - if self.is_accessible_from(binding.vis, directive.parent) { + let mut ident = ident.modern(); + let scope = match ident.ctxt.reverse_glob_adjust(module.expansion, + directive.span.ctxt.modern()) { + Some(Some(def)) => self.macro_def_scope(def), + Some(None) => directive.parent, + None => continue, + }; + if self.is_accessible_from(binding.vis, scope) { let imported_binding = self.import(binding, directive); let _ = self.try_define(directive.parent, ident, ns, imported_binding); } @@ -767,8 +775,14 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { let bindings = module.resolutions.borrow().iter().filter_map(|(&ident, resolution)| { resolution.borrow().binding().map(|binding| (ident, binding)) }).collect::>(); - for ((ident, ns), binding) in bindings { - if binding.pseudo_vis() == ty::Visibility::Public || self.is_accessible(binding.vis) { + for ((mut ident, ns), binding) in bindings { + let scope = match ident.ctxt.reverse_glob_adjust(module.expansion, + directive.span.ctxt.modern()) { + Some(Some(def)) => self.macro_def_scope(def), + Some(None) => self.current_module, + None => continue, + }; + if self.is_accessible_from(binding.pseudo_vis(), scope) { let imported_binding = self.import(binding, directive); let _ = self.try_define(directive.parent, ident, ns, imported_binding); } diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index 1930f61121bb0..71dc81c375923 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -550,12 +550,16 @@ pub enum SyntaxExtension { /// An attribute-like procedural macro that derives a builtin trait. BuiltinDerive(BuiltinDeriveFn), + + /// A declarative macro, e.g. `macro m() {}`. + DeclMacro(Box, Option /* definition site span */), } impl SyntaxExtension { /// Return which kind of macro calls this syntax extension. pub fn kind(&self) -> MacroKind { match *self { + SyntaxExtension::DeclMacro(..) | SyntaxExtension::NormalTT(..) | SyntaxExtension::IdentTT(..) | SyntaxExtension::ProcMacro(..) => @@ -569,6 +573,13 @@ impl SyntaxExtension { MacroKind::Derive, } } + + pub fn is_modern(&self) -> bool { + match *self { + SyntaxExtension::DeclMacro(..) => true, + _ => false, + } + } } pub type NamedSyntaxExtension = (Name, SyntaxExtension); diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 25e0aed220ab3..be077b481113f 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -288,7 +288,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let derives = derives.entry(invoc.expansion_data.mark).or_insert_with(Vec::new); for path in &traits { - let mark = Mark::fresh(); + let mark = Mark::fresh(self.cx.current_expansion.mark); derives.push(mark); let item = match self.cx.resolver.resolve_macro( Mark::root(), path, MacroKind::Derive, false) { @@ -455,25 +455,37 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let path = &mac.node.path; let ident = ident.unwrap_or_else(|| keywords::Invalid.ident()); + let validate_and_set_expn_info = |def_site_span, allow_internal_unstable| { + if ident.name != keywords::Invalid.name() { + return Err(format!("macro {}! expects no ident argument, given '{}'", path, ident)); + } + mark.set_expn_info(ExpnInfo { + call_site: span, + callee: NameAndSpan { + format: MacroBang(Symbol::intern(&format!("{}", path))), + span: def_site_span, + allow_internal_unstable: allow_internal_unstable, + }, + }); + Ok(()) + }; + let marked_tts = noop_fold_tts(mac.node.stream(), &mut Marker(mark)); let opt_expanded = match *ext { - NormalTT(ref expandfun, exp_span, allow_internal_unstable) => { - if ident.name != keywords::Invalid.name() { - let msg = - format!("macro {}! expects no ident argument, given '{}'", path, ident); + SyntaxExtension::DeclMacro(ref expand, def_site_span) => { + if let Err(msg) = validate_and_set_expn_info(def_site_span, false) { self.cx.span_err(path.span, &msg); return kind.dummy(span); } + kind.make_from(expand.expand(self.cx, span, marked_tts)) + } - invoc.expansion_data.mark.set_expn_info(ExpnInfo { - call_site: span, - callee: NameAndSpan { - format: MacroBang(Symbol::intern(&format!("{}", path))), - span: exp_span.map(|(_, s)| s), - allow_internal_unstable: allow_internal_unstable, - }, - }); - + NormalTT(ref expandfun, def_info, allow_internal_unstable) => { + if let Err(msg) = validate_and_set_expn_info(def_info.map(|(_, s)| s), + allow_internal_unstable) { + self.cx.span_err(path.span, &msg); + return kind.dummy(span); + } kind.make_from(expandfun.expand(self.cx, span, marked_tts)) } @@ -687,7 +699,7 @@ macro_rules! fully_configure { impl<'a, 'b> InvocationCollector<'a, 'b> { fn collect(&mut self, expansion_kind: ExpansionKind, kind: InvocationKind) -> Expansion { - let mark = Mark::fresh(); + let mark = Mark::fresh(self.cx.current_expansion.mark); self.invocations.push(Invocation { kind: kind, expansion_kind: expansion_kind, diff --git a/src/libsyntax/ext/tt/macro_rules.rs b/src/libsyntax/ext/tt/macro_rules.rs index ad09d58373412..a9252d0818e38 100644 --- a/src/libsyntax/ext/tt/macro_rules.rs +++ b/src/libsyntax/ext/tt/macro_rules.rs @@ -253,9 +253,12 @@ pub fn compile(sess: &ParseSess, features: &RefCell, def: &ast::Item) valid: valid, }); - NormalTT(exp, - Some((def.id, def.span)), - attr::contains_name(&def.attrs, "allow_internal_unstable")) + if body.legacy { + let allow_internal_unstable = attr::contains_name(&def.attrs, "allow_internal_unstable"); + NormalTT(exp, Some((def.id, def.span)), allow_internal_unstable) + } else { + SyntaxExtension::DeclMacro(exp, Some(def.span)) + } } fn check_lhs_nt_follows(sess: &ParseSess, diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 1d1e46cf576d1..5bbda5f2689d3 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -428,7 +428,7 @@ pub fn noop_fold_global_asm(ga: P, pub fn noop_fold_variant(v: Variant, fld: &mut T) -> Variant { Spanned { node: Variant_ { - name: v.node.name, + name: fld.fold_ident(v.node.name), attrs: fold_attrs(v.node.attrs, fld), data: fld.fold_variant_data(v.node.data), disr_expr: v.node.disr_expr.map(|e| fld.fold_expr(e)), diff --git a/src/libsyntax/std_inject.rs b/src/libsyntax/std_inject.rs index 8e257102e1c13..a8a9ae556f108 100644 --- a/src/libsyntax/std_inject.rs +++ b/src/libsyntax/std_inject.rs @@ -21,7 +21,7 @@ use tokenstream::TokenStream; /// call to codemap's `is_internal` check. /// The expanded code uses the unstable `#[prelude_import]` attribute. fn ignored_span(sp: Span) -> Span { - let mark = Mark::fresh(); + let mark = Mark::fresh(Mark::root()); mark.set_expn_info(ExpnInfo { call_site: DUMMY_SP, callee: NameAndSpan { diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index bb1a6ff65a596..3df61fadc3503 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -276,7 +276,7 @@ fn generate_test_harness(sess: &ParseSess, let mut cleaner = EntryPointCleaner { depth: 0 }; let krate = cleaner.fold_crate(krate); - let mark = Mark::fresh(); + let mark = Mark::fresh(Mark::root()); let mut cx: TestCtxt = TestCtxt { sess: sess, span_diagnostic: sd, diff --git a/src/libsyntax_ext/deriving/mod.rs b/src/libsyntax_ext/deriving/mod.rs index 31c7cc3367677..00dcfc7a58706 100644 --- a/src/libsyntax_ext/deriving/mod.rs +++ b/src/libsyntax_ext/deriving/mod.rs @@ -162,7 +162,7 @@ fn call_intrinsic(cx: &ExtCtxt, } else { // Avoid instability errors with user defined curstom derives, cc #36316 let mut info = cx.current_expansion.mark.expn_info().unwrap(); info.callee.allow_internal_unstable = true; - let mark = Mark::fresh(); + let mark = Mark::fresh(Mark::root()); mark.set_expn_info(info); span.ctxt = SyntaxContext::empty().apply_mark(mark); } diff --git a/src/libsyntax_ext/proc_macro_registrar.rs b/src/libsyntax_ext/proc_macro_registrar.rs index 6318abec69f06..ab6d73e5061a0 100644 --- a/src/libsyntax_ext/proc_macro_registrar.rs +++ b/src/libsyntax_ext/proc_macro_registrar.rs @@ -361,7 +361,7 @@ fn mk_registrar(cx: &mut ExtCtxt, custom_derives: &[ProcMacroDerive], custom_attrs: &[ProcMacroDef], custom_macros: &[ProcMacroDef]) -> P { - let mark = Mark::fresh(); + let mark = Mark::fresh(Mark::root()); mark.set_expn_info(ExpnInfo { call_site: DUMMY_SP, callee: NameAndSpan { diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index 8a9ff647b3ea1..24a21faa7eb9a 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -24,23 +24,31 @@ use std::collections::HashMap; use std::fmt; /// A SyntaxContext represents a chain of macro expansions (represented by marks). -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Clone, Copy, PartialEq, Eq, Default, PartialOrd, Ord, Hash)] pub struct SyntaxContext(u32); -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Default)] pub struct SyntaxContextData { pub outer_mark: Mark, pub prev_ctxt: SyntaxContext, + pub modern: SyntaxContext, } /// A mark is a unique id associated with a macro expansion. #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default, RustcEncodable, RustcDecodable)] pub struct Mark(u32); +#[derive(Default)] +struct MarkData { + parent: Mark, + modern: bool, + expn_info: Option, +} + impl Mark { - pub fn fresh() -> Self { + pub fn fresh(parent: Mark) -> Self { HygieneData::with(|data| { - data.marks.push(None); + data.marks.push(MarkData { parent: parent, modern: false, expn_info: None }); Mark(data.marks.len() as u32 - 1) }) } @@ -59,16 +67,43 @@ impl Mark { } pub fn expn_info(self) -> Option { - HygieneData::with(|data| data.marks[self.0 as usize].clone()) + HygieneData::with(|data| data.marks[self.0 as usize].expn_info.clone()) } pub fn set_expn_info(self, info: ExpnInfo) { - HygieneData::with(|data| data.marks[self.0 as usize] = Some(info)) + HygieneData::with(|data| data.marks[self.0 as usize].expn_info = Some(info)) + } + + pub fn modern(mut self) -> Mark { + HygieneData::with(|data| { + loop { + if self == Mark::root() || data.marks[self.0 as usize].modern { + return self; + } + self = data.marks[self.0 as usize].parent; + } + }) + } + + pub fn set_modern(self) { + HygieneData::with(|data| data.marks[self.0 as usize].modern = true) + } + + pub fn is_descendant_of(mut self, ancestor: Mark) -> bool { + HygieneData::with(|data| { + while self != ancestor { + if self == Mark::root() { + return false; + } + self = data.marks[self.0 as usize].parent; + } + true + }) } } struct HygieneData { - marks: Vec>, + marks: Vec, syntax_contexts: Vec, markings: HashMap<(SyntaxContext, Mark), SyntaxContext>, } @@ -76,11 +111,8 @@ struct HygieneData { impl HygieneData { fn new() -> Self { HygieneData { - marks: vec![None], - syntax_contexts: vec![SyntaxContextData { - outer_mark: Mark::root(), - prev_ctxt: SyntaxContext::empty(), - }], + marks: vec![MarkData::default()], + syntax_contexts: vec![SyntaxContextData::default()], markings: HashMap::new(), } } @@ -102,30 +134,146 @@ impl SyntaxContext { SyntaxContext(0) } - pub fn data(self) -> SyntaxContextData { - HygieneData::with(|data| data.syntax_contexts[self.0 as usize]) - } - /// Extend a syntax context with a given mark pub fn apply_mark(self, mark: Mark) -> SyntaxContext { - // Applying the same mark twice is a no-op - let ctxt_data = self.data(); - if mark == ctxt_data.outer_mark { - return ctxt_data.prev_ctxt; - } - HygieneData::with(|data| { let syntax_contexts = &mut data.syntax_contexts; + let ctxt_data = syntax_contexts[self.0 as usize]; + if mark == ctxt_data.outer_mark { + return ctxt_data.prev_ctxt; + } + + let modern = if data.marks[mark.0 as usize].modern { + *data.markings.entry((ctxt_data.modern, mark)).or_insert_with(|| { + let modern = SyntaxContext(syntax_contexts.len() as u32); + syntax_contexts.push(SyntaxContextData { + outer_mark: mark, + prev_ctxt: ctxt_data.modern, + modern: modern, + }); + modern + }) + } else { + ctxt_data.modern + }; + *data.markings.entry((self, mark)).or_insert_with(|| { syntax_contexts.push(SyntaxContextData { outer_mark: mark, prev_ctxt: self, + modern: modern, }); SyntaxContext(syntax_contexts.len() as u32 - 1) }) }) } + pub fn remove_mark(&mut self) -> Mark { + HygieneData::with(|data| { + let outer_mark = data.syntax_contexts[self.0 as usize].outer_mark; + *self = data.syntax_contexts[self.0 as usize].prev_ctxt; + outer_mark + }) + } + + /// Adjust this context for resolution in a scope created by the given expansion. + /// For example, consider the following three resolutions of `f`: + /// ```rust + /// mod foo { pub fn f() {} } // `f`'s `SyntaxContext` is empty. + /// m!(f); + /// macro m($f:ident) { + /// mod bar { + /// pub fn f() {} // `f`'s `SyntaxContext` has a single `Mark` from `m`. + /// pub fn $f() {} // `$f`'s `SyntaxContext` is empty. + /// } + /// foo::f(); // `f`'s `SyntaxContext` has a single `Mark` from `m` + /// //^ Since `mod foo` is outside this expansion, `adjust` removes the mark from `f`, + /// //| and it resolves to `::foo::f`. + /// bar::f(); // `f`'s `SyntaxContext` has a single `Mark` from `m` + /// //^ Since `mod bar` not outside this expansion, `adjust` does not change `f`, + /// //| and it resolves to `::bar::f`. + /// bar::$f(); // `f`'s `SyntaxContext` is empty. + /// //^ Since `mod bar` is not outside this expansion, `adjust` does not change `$f`, + /// //| and it resolves to `::bar::$f`. + /// } + /// ``` + /// This returns the expansion whose definition scope we use to privacy check the resolution, + /// or `None` if we privacy check as usual (i.e. not w.r.t. a macro definition scope). + pub fn adjust(&mut self, expansion: Mark) -> Option { + let mut scope = None; + while !expansion.is_descendant_of(self.outer()) { + scope = Some(self.remove_mark()); + } + scope + } + + /// Adjust this context for resolution in a scope created by the given expansion + /// via a glob import with the given `SyntaxContext`. + /// For example, + /// ```rust + /// m!(f); + /// macro m($i:ident) { + /// mod foo { + /// pub fn f() {} // `f`'s `SyntaxContext` has a single `Mark` from `m`. + /// pub fn $i() {} // `$i`'s `SyntaxContext` is empty. + /// } + /// n(f); + /// macro n($j:ident) { + /// use foo::*; + /// f(); // `f`'s `SyntaxContext` has a mark from `m` and a mark from `n` + /// //^ `glob_adjust` removes the mark from `n`, so this resolves to `foo::f`. + /// $i(); // `$i`'s `SyntaxContext` has a mark from `n` + /// //^ `glob_adjust` removes the mark from `n`, so this resolves to `foo::$i`. + /// $j(); // `$j`'s `SyntaxContext` has a mark from `m` + /// //^ This cannot be glob-adjusted, so this is a resolution error. + /// } + /// } + /// ``` + /// This returns `None` if the context cannot be glob-adjusted. + /// Otherwise, it returns the scope to use when privacy checking (see `adjust` for details). + pub fn glob_adjust(&mut self, expansion: Mark, mut glob_ctxt: SyntaxContext) + -> Option> { + let mut scope = None; + while !expansion.is_descendant_of(glob_ctxt.outer()) { + scope = Some(glob_ctxt.remove_mark()); + if self.remove_mark() != scope.unwrap() { + return None; + } + } + if self.adjust(expansion).is_some() { + return None; + } + Some(scope) + } + + /// Undo `glob_adjust` if possible: + /// ```rust + /// if let Some(privacy_checking_scope) = self.reverse_glob_adjust(expansion, glob_ctxt) { + /// assert!(self.glob_adjust(expansion, glob_ctxt) == Some(privacy_checking_scope)); + /// } + /// ``` + pub fn reverse_glob_adjust(&mut self, expansion: Mark, mut glob_ctxt: SyntaxContext) + -> Option> { + if self.adjust(expansion).is_some() { + return None; + } + + let mut marks = Vec::new(); + while !expansion.is_descendant_of(glob_ctxt.outer()) { + marks.push(glob_ctxt.remove_mark()); + } + + let scope = marks.last().cloned(); + while let Some(mark) = marks.pop() { + *self = self.apply_mark(mark); + } + Some(scope) + } + + pub fn modern(self) -> SyntaxContext { + HygieneData::with(|data| data.syntax_contexts[self.0 as usize].modern) + } + pub fn outer(self) -> Mark { HygieneData::with(|data| data.syntax_contexts[self.0 as usize].outer_mark) } diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index b866652c49f85..070fab57afa82 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -35,8 +35,8 @@ impl Ident { Ident::with_empty_ctxt(Symbol::intern(string)) } - pub fn unhygienize(self) -> Ident { - Ident { name: self.name, ctxt: SyntaxContext::empty() } + pub fn modern(self) -> Ident { + Ident { name: self.name, ctxt: self.ctxt.modern() } } }