diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index f56a27b9ae04a..2290428264221 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -95,13 +95,7 @@ pub fn lower_crate(sess: &Session, let _ignore = sess.dep_graph.in_ignore(); LoweringContext { - crate_root: if std_inject::no_core(krate) { - None - } else if std_inject::no_std(krate) { - Some("core") - } else { - Some("std") - }, + crate_root: std_inject::injected_crate_name(krate), sess: sess, parent_def: None, resolver: resolver, diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index ea42f7ba93e44..e8ab2f3a2405b 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -649,7 +649,7 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session, let resolver_arenas = Resolver::arenas(); let mut resolver = Resolver::new(sess, &krate, make_glob_map, &mut crate_loader, &resolver_arenas); - syntax_ext::register_builtins(&mut resolver, sess.features.borrow().quote); + syntax_ext::register_builtins(&mut resolver, syntax_exts, sess.features.borrow().quote); krate = time(time_passes, "expansion", || { // Windows dlls do not have rpaths, so they don't know how to find their @@ -686,11 +686,17 @@ pub fn phase_2_configure_and_expand<'a, F>(sess: &Session, ..syntax::ext::expand::ExpansionConfig::default(crate_name.to_string()) }; let mut ecx = ExtCtxt::new(&sess.parse_sess, krate.config.clone(), cfg, &mut resolver); - let ret = syntax::ext::expand::expand_crate(&mut ecx, syntax_exts, krate); + let err_count = ecx.parse_sess.span_diagnostic.err_count(); + + let krate = ecx.monotonic_expander().expand_crate(krate); + + if ecx.parse_sess.span_diagnostic.err_count() - ecx.resolve_err_count > err_count { + ecx.parse_sess.span_diagnostic.abort_if_errors(); + } if cfg!(windows) { env::set_var("PATH", &old_path); } - ret + krate }); krate.exported_macros = mem::replace(&mut resolver.exported_macros, Vec::new()); diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 9149202c1c1bb..5600669d45fb9 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, LegacyScope}; use resolve_imports::ImportDirectiveSubclass::{self, GlobImport}; use {Module, ModuleS, ModuleKind}; use Namespace::{self, TypeNS, ValueNS}; @@ -200,16 +200,16 @@ 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.expansion_data[&Mark::root()].module.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)); + if self.builtin_macros.insert(name, ext).is_some() && + expansion != Mark::root() { + let msg = format!("`{}` is already in scope", name); + self.session.struct_span_err(loaded_macro.import_site, &msg) + .note("macro-expanded `#[macro_use]`s may not shadow \ + existing macros (see RFC 1560)") + .emit(); + } self.macro_names.insert(name); } if def.export { @@ -250,7 +250,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 +519,26 @@ impl<'b> Resolver<'b> { pub struct BuildReducedGraphVisitor<'a, 'b: 'a> { pub resolver: &'a mut Resolver<'b>, + pub legacy_scope: LegacyScope<'b>, pub expansion: Mark, } impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> { - fn visit_invoc(&mut self, id: ast::NodeId) { - self.resolver.expansion_data.get_mut(&Mark::from_placeholder_id(id)).unwrap().module = - 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 +546,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; + let (parent, legacy_scope) = (self.resolver.current_module, self.legacy_scope); self.resolver.build_reduced_graph_for_item(item, self.expansion); 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 +583,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 +595,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 4f41dfc8b6455..38a042735ff85 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,6 +78,7 @@ use std::mem::replace; use std::rc::Rc; use resolve_imports::{ImportDirective, NameResolution}; +use macros::{InvocationData, LegacyBinding, LegacyScope}; // NB: This module needs to be declared first so diagnostics are // registered before they are used. @@ -791,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>; @@ -811,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, } } @@ -1076,7 +1073,7 @@ pub struct Resolver<'a> { privacy_errors: Vec>, ambiguity_errors: Vec>, - macro_shadowing_errors: FnvHashSet, + disallowed_shadowing: Vec<(Name, Span, LegacyScope<'a>)>, arenas: &'a ResolverArenas<'a>, dummy_binding: &'a NameBinding<'a>, @@ -1086,9 +1083,10 @@ 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. - expansion_data: FnvHashMap>, + invocations: FnvHashMap>, } pub struct ResolverArenas<'a> { @@ -1097,6 +1095,8 @@ pub struct ResolverArenas<'a> { name_bindings: arena::TypedArena>, import_directives: arena::TypedArena>, name_resolutions: arena::TypedArena>>, + invocation_data: arena::TypedArena>, + legacy_bindings: arena::TypedArena>, } impl<'a> ResolverArenas<'a> { @@ -1120,6 +1120,13 @@ impl<'a> ResolverArenas<'a> { fn alloc_name_resolution(&'a self) -> &'a RefCell> { self.name_resolutions.alloc(Default::default()) } + fn alloc_invocation_data(&'a self, expansion_data: InvocationData<'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> { @@ -1205,8 +1212,9 @@ impl<'a> Resolver<'a> { let mut definitions = Definitions::new(); DefCollector::new(&mut definitions).collect_root(); - let mut expansion_data = FnvHashMap(); - expansion_data.insert(Mark::root(), macros::ExpansionData::root(graph_root)); + let mut invocations = FnvHashMap(); + invocations.insert(Mark::root(), + arenas.alloc_invocation_data(InvocationData::root(graph_root))); Resolver { session: session, @@ -1252,7 +1260,7 @@ impl<'a> Resolver<'a> { privacy_errors: Vec::new(), ambiguity_errors: Vec::new(), - macro_shadowing_errors: FnvHashSet(), + disallowed_shadowing: Vec::new(), arenas: arenas, dummy_binding: arenas.alloc_name_binding(NameBinding { @@ -1266,7 +1274,8 @@ impl<'a> Resolver<'a> { derive_modes: FnvHashMap(), crate_loader: crate_loader, macro_names: FnvHashSet(), - expansion_data: expansion_data, + builtin_macros: FnvHashMap(), + invocations: invocations, } } @@ -1277,6 +1286,8 @@ impl<'a> Resolver<'a> { name_bindings: arena::TypedArena::new(), import_directives: arena::TypedArena::new(), name_resolutions: arena::TypedArena::new(), + invocation_data: arena::TypedArena::new(), + legacy_bindings: arena::TypedArena::new(), } } @@ -3342,7 +3353,8 @@ impl<'a> Resolver<'a> { vis.is_accessible_from(module.normal_ancestor_id.unwrap(), self) } - fn report_errors(&self) { + fn report_errors(&mut self) { + self.report_shadowing_errors(); let mut reported_spans = FnvHashSet(); for &AmbiguityError { span, name, b1, b2 } in &self.ambiguity_errors { @@ -3370,6 +3382,20 @@ impl<'a> Resolver<'a> { } } + fn report_shadowing_errors(&mut self) { + let mut reported_errors = FnvHashSet(); + for (name, span, scope) in replace(&mut self.disallowed_shadowing, Vec::new()) { + if self.resolve_macro_name(scope, name, false).is_some() && + reported_errors.insert((name, span)) { + let msg = format!("`{}` is already in scope", name); + self.session.struct_span_err(span, &msg) + .note("macro-expanded `macro_rules!`s may not shadow \ + existing macros (see RFC 1560)") + .emit(); + } + } + } + fn report_conflict(&self, parent: Module, name: Name, diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index 3f6c69278bee8..86ab077191eef 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -12,47 +12,76 @@ 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; use syntax::errors::DiagnosticBuilder; -use syntax::ext::base::{self, MultiModifier, MultiDecorator, MultiItemModifier}; +use syntax::ext::base::{self, Determinacy, 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 ExpansionData<'a> { - backtrace: SyntaxContext, - pub module: Module<'a>, +pub struct InvocationData<'a> { + 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> ExpansionData<'a> { +impl<'a> InvocationData<'a> { pub fn root(graph_root: Module<'a>) -> Self { - ExpansionData { - backtrace: SyntaxContext::empty(), - module: graph_root, + InvocationData { + 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>), +} + +impl<'a> LegacyScope<'a> { + fn simplify_expansion(mut invoc: &'a InvocationData<'a>) -> Self { + while let LegacyScope::Invocation(_) = invoc.expansion.get() { + match invoc.legacy_scope.get() { + LegacyScope::Expansion(new_invoc) => invoc = new_invoc, + LegacyScope::Binding(_) => break, + scope @ _ => return scope, + } } + LegacyScope::Expansion(invoc) } } +pub struct LegacyBinding<'a> { + parent: LegacyScope<'a>, + name: ast::Name, + ext: Rc, + span: Span, +} + +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() @@ -61,19 +90,28 @@ impl<'a> base::Resolver for Resolver<'a> { fn get_module_scope(&mut self, id: ast::NodeId) -> Mark { let mark = Mark::fresh(); let module = self.module_map[&id]; - self.expansion_data.insert(mark, ExpansionData { - backtrace: SyntaxContext::empty(), - module: module, + self.invocations.insert(mark, self.arenas.alloc_invocation_data(InvocationData { + 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.expansion_data[&mark].module; - 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), + expansion: mark, + }; + expansion.visit_with(&mut visitor); + invocation.expansion.set(visitor.legacy_scope); } fn add_macro(&mut self, scope: Mark, mut def: ast::MacroDef) { @@ -81,17 +119,14 @@ impl<'a> base::Resolver for Resolver<'a> { self.session.span_err(def.span, "user-defined macros may not be named `macro_rules`"); } if def.use_locally { - let ExpansionData { mut module, backtrace, .. } = self.expansion_data[&scope]; - while module.macros_escape { - module = module.parent.unwrap(); - } - let binding = NameBinding { + let invocation = self.invocations[&scope]; + let binding = self.arenas.alloc_legacy_binding(LegacyBinding { + parent: invocation.legacy_scope.get(), + name: def.ident.name, ext: Rc::new(macro_rules::compile(&self.session.parse_sess, &def)), - expansion: 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); + }); + invocation.legacy_scope.set(LegacyScope::Binding(binding)); self.macro_names.insert(def.ident.name); } if def.export { @@ -104,12 +139,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) { @@ -119,8 +149,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.expansion_data[&Mark::root()].module.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)) } @@ -132,7 +162,8 @@ impl<'a> base::Resolver for Resolver<'a> { None } - fn resolve_invoc(&mut self, scope: Mark, invoc: &Invocation) -> Option> { + fn resolve_invoc(&mut self, scope: Mark, invoc: &Invocation, force: bool) + -> Result, Determinacy> { let (name, span) = match invoc.kind { InvocationKind::Bang { ref mac, .. } => { let path = &mac.node.path; @@ -140,19 +171,27 @@ impl<'a> base::Resolver for Resolver<'a> { !path.segments[0].parameters.is_empty() { self.session.span_err(path.span, "expected macro name without module separators"); - return None; + return Err(Determinacy::Determined); } (path.segments[0].identifier.name, path.span) } InvocationKind::Attr { ref attr, .. } => (intern(&*attr.name()), attr.span), }; - self.resolve_macro_name(scope, name, true).or_else(|| { - let mut err = - self.session.struct_span_err(span, &format!("macro undefined: '{}!'", name)); - self.suggest_macro_name(&name.as_str(), &mut err); - err.emit(); - None + let invocation = self.invocations[&scope]; + if let LegacyScope::Expansion(parent) = invocation.legacy_scope.get() { + invocation.legacy_scope.set(LegacyScope::simplify_expansion(parent)); + } + self.resolve_macro_name(invocation.legacy_scope.get(), name, true).ok_or_else(|| { + if force { + let mut err = + self.session.struct_span_err(span, &format!("macro undefined: '{}!'", name)); + self.suggest_macro_name(&name.as_str(), &mut err); + err.emit(); + Determinacy::Determined + } else { + Determinacy::Undetermined + } }) } @@ -162,36 +201,40 @@ 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) + pub fn resolve_macro_name(&mut self, + mut scope: LegacyScope<'a>, + name: ast::Name, + record_used: bool) -> Option> { - let ExpansionData { mut module, backtrace, .. } = self.expansion_data[&scope]; + let mut relative_depth: u32 = 0; loop { - if let Some(binding) = module.macros.borrow().get(&name) { - let mut backtrace = backtrace.data(); - while binding.expansion != backtrace.outer_mark { - if backtrace.outer_mark != Mark::root() { - backtrace = backtrace.prev_ctxt.data(); - continue + 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() } - - 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(); + } + LegacyScope::Invocation(invocation) => { + relative_depth = relative_depth.saturating_sub(1); + invocation.legacy_scope.get() + } + LegacyScope::Binding(binding) => { + if binding.name == name { + if record_used && relative_depth > 0 { + self.disallowed_shadowing.push((name, binding.span, binding.parent)); + } + return Some(binding.ext.clone()); } - break + binding.parent } - return Some(binding.ext.clone()); - } - match module.parent { - Some(parent) => module = parent, - None => break, - } + }; } - None + + self.builtin_macros.get(&name).cloned() } fn suggest_macro_name(&mut self, name: &str, err: &mut DiagnosticBuilder<'a>) { @@ -204,15 +247,19 @@ impl<'a> Resolver<'a> { } } - fn collect_def_ids(&mut self, mark: Mark, expansion: &Expansion) { - let expansion_data = &mut self.expansion_data; - let ExpansionData { backtrace, def_index, const_integer, module } = expansion_data[&mark]; + 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, .. } = *invocation; + let visit_macro_invoc = &mut |invoc: map::MacroInvocationData| { - expansion_data.entry(invoc.mark).or_insert(ExpansionData { - backtrace: backtrace.apply_mark(invoc.mark), - def_index: invoc.def_index, - const_integer: invoc.const_integer, - module: module, + invocations.entry(invoc.mark).or_insert_with(|| { + arenas.alloc_invocation_data(InvocationData { + 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), + }) }); }; diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index 4689c4ded5c0a..a72a660a5c411 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use self::Determinacy::*; use self::ImportDirectiveSubclass::*; use Module; @@ -26,6 +25,7 @@ use rustc::lint::builtin::PRIVATE_IN_PUBLIC; use rustc::hir::def::*; use syntax::ast::{NodeId, Name}; +use syntax::ext::base::Determinacy::{self, Determined, Undetermined}; use syntax::util::lev_distance::find_best_match_for_name; use syntax_pos::Span; @@ -37,12 +37,6 @@ impl<'a> Resolver<'a> { } } -#[derive(Copy, Clone, Debug)] -pub enum Determinacy { - Determined, - Undetermined, -} - /// Contains data for specific types of import directives. #[derive(Clone, Debug)] pub enum ImportDirectiveSubclass<'a> { diff --git a/src/libsyntax/ext/base.rs b/src/libsyntax/ext/base.rs index b56018e1e9dcd..f7c88073c9d40 100644 --- a/src/libsyntax/ext/base.rs +++ b/src/libsyntax/ext/base.rs @@ -22,7 +22,6 @@ use parse::{self, parser}; use parse::token; use parse::token::{InternedString, str_to_ident}; use ptr::P; -use std_inject; use util::small_vector::SmallVector; use std::path::PathBuf; @@ -523,10 +522,17 @@ pub trait Resolver { fn add_expansions_at_stmt(&mut self, id: ast::NodeId, macros: Vec); fn find_attr_invoc(&mut self, attrs: &mut Vec) -> Option; - fn resolve_invoc(&mut self, scope: Mark, invoc: &Invocation) -> Option>; + fn resolve_invoc(&mut self, scope: Mark, invoc: &Invocation, force: bool) + -> Result, Determinacy>; fn resolve_derive_mode(&mut self, ident: ast::Ident) -> Option>; } +#[derive(Copy, Clone, Debug)] +pub enum Determinacy { + Determined, + Undetermined, +} + pub struct DummyResolver; impl Resolver for DummyResolver { @@ -540,8 +546,9 @@ impl Resolver for DummyResolver { fn find_attr_invoc(&mut self, _attrs: &mut Vec) -> Option { None } fn resolve_derive_mode(&mut self, _ident: ast::Ident) -> Option> { None } - fn resolve_invoc(&mut self, _scope: Mark, _invoc: &Invocation) -> Option> { - None + fn resolve_invoc(&mut self, _scope: Mark, _invoc: &Invocation, _force: bool) + -> Result, Determinacy> { + Err(Determinacy::Determined) } } @@ -737,28 +744,6 @@ impl<'a> ExtCtxt<'a> { pub fn name_of(&self, st: &str) -> ast::Name { token::intern(st) } - - pub fn initialize(&mut self, user_exts: Vec, krate: &ast::Crate) { - if std_inject::no_core(&krate) { - self.crate_root = None; - } else if std_inject::no_std(&krate) { - self.crate_root = Some("core"); - } else { - self.crate_root = Some("std"); - } - - for (name, extension) in user_exts { - let ident = ast::Ident::with_empty_ctxt(name); - self.resolver.add_ext(ident, Rc::new(extension)); - } - - let mut module = ModuleData { - mod_path: vec![token::str_to_ident(&self.ecfg.crate_name)], - directory: PathBuf::from(self.parse_sess.codemap().span_to_filename(krate.span)), - }; - module.directory.pop(); - self.current_expansion.module = Rc::new(module); - } } /// Extract a string literal from the macro expanded version of `expr`, diff --git a/src/libsyntax/ext/expand.rs b/src/libsyntax/ext/expand.rs index 3abe7626efc6e..363ceebf0f475 100644 --- a/src/libsyntax/ext/expand.rs +++ b/src/libsyntax/ext/expand.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use ast::{Block, Crate, Ident, Mac_, PatKind}; +use ast::{Block, Ident, Mac_, PatKind}; use ast::{Name, MacStmtStyle, StmtKind, ItemKind}; use ast; use ext::hygiene::Mark; @@ -26,6 +26,7 @@ use parse::parser::Parser; use parse::token::{self, intern, keywords}; use print::pprust; use ptr::P; +use std_inject; use tokenstream::{TokenTree, TokenStream}; use util::small_vector::SmallVector; use visit::Visitor; @@ -186,8 +187,14 @@ impl<'a, 'b> MacroExpander<'a, 'b> { MacroExpander { cx: cx, monotonic: monotonic } } - fn expand_crate(&mut self, mut krate: ast::Crate) -> ast::Crate { - let err_count = self.cx.parse_sess.span_diagnostic.err_count(); + pub fn expand_crate(&mut self, mut krate: ast::Crate) -> ast::Crate { + self.cx.crate_root = std_inject::injected_crate_name(&krate); + let mut module = ModuleData { + mod_path: vec![token::str_to_ident(&self.cx.ecfg.crate_name)], + directory: PathBuf::from(self.cx.codemap().span_to_filename(krate.span)), + }; + module.directory.pop(); + self.cx.current_expansion.module = Rc::new(module); let krate_item = Expansion::Items(SmallVector::one(P(ast::Item { attrs: krate.attrs, @@ -206,10 +213,6 @@ impl<'a, 'b> MacroExpander<'a, 'b> { _ => unreachable!(), }; - if self.cx.parse_sess.span_diagnostic.err_count() - self.cx.resolve_err_count > err_count { - self.cx.parse_sess.span_diagnostic.abort_if_errors(); - } - krate } @@ -221,25 +224,47 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let (expansion, mut invocations) = self.collect_invocations(expansion); invocations.reverse(); - let mut expansions = vec![vec![(0, expansion)]]; - while let Some(invoc) = invocations.pop() { + let mut expansions = Vec::new(); + let mut undetermined_invocations = Vec::new(); + let (mut progress, mut force) = (false, !self.monotonic); + loop { + let invoc = if let Some(invoc) = invocations.pop() { + invoc + } else if undetermined_invocations.is_empty() { + break + } else { + invocations = mem::replace(&mut undetermined_invocations, Vec::new()); + force = !mem::replace(&mut progress, false); + continue + }; + + let scope = + if self.monotonic { invoc.expansion_data.mark } else { orig_expansion_data.mark }; + let ext = match self.cx.resolver.resolve_invoc(scope, &invoc, force) { + Ok(ext) => Some(ext), + Err(Determinacy::Determined) => None, + Err(Determinacy::Undetermined) => { + undetermined_invocations.push(invoc); + continue + } + }; + + progress = true; let ExpansionData { depth, mark, .. } = invoc.expansion_data; self.cx.current_expansion = invoc.expansion_data.clone(); - let scope = if self.monotonic { mark } else { orig_expansion_data.mark }; self.cx.current_expansion.mark = scope; - let expansion = match self.cx.resolver.resolve_invoc(scope, &invoc) { + let expansion = match ext { Some(ext) => self.expand_invoc(invoc, ext), None => invoc.expansion_kind.dummy(invoc.span()), }; - self.cx.current_expansion.depth = depth + 1; let (expansion, new_invocations) = self.collect_invocations(expansion); - if expansions.len() == depth { + if expansions.len() < depth { expansions.push(Vec::new()); } - expansions[depth].push((mark.as_u32(), expansion)); + expansions[depth - 1].push((mark.as_u32(), expansion)); if !self.cx.ecfg.single_step { invocations.extend(new_invocations.into_iter().rev()); } @@ -250,12 +275,11 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let mut placeholder_expander = PlaceholderExpander::new(self.cx, self.monotonic); while let Some(expansions) = expansions.pop() { for (mark, expansion) in expansions.into_iter().rev() { - let expansion = expansion.fold_with(&mut placeholder_expander); placeholder_expander.add(ast::NodeId::from_u32(mark), expansion); } } - placeholder_expander.remove(ast::NodeId::from_u32(0)) + expansion.fold_with(&mut placeholder_expander) } fn collect_invocations(&mut self, expansion: Expansion) -> (Expansion, Vec) { @@ -538,7 +562,11 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { self.invocations.push(Invocation { kind: kind, expansion_kind: expansion_kind, - expansion_data: ExpansionData { mark: mark, ..self.cx.current_expansion.clone() }, + expansion_data: ExpansionData { + mark: mark, + depth: self.cx.current_expansion.depth + 1, + ..self.cx.current_expansion.clone() + }, }); placeholder(expansion_kind, ast::NodeId::from_u32(mark.as_u32())) } @@ -866,22 +894,6 @@ impl<'feat> ExpansionConfig<'feat> { } } -pub fn expand_crate(cx: &mut ExtCtxt, - user_exts: Vec, - c: Crate) -> Crate { - cx.initialize(user_exts, &c); - cx.monotonic_expander().expand_crate(c) -} - -// Expands crate using supplied MacroExpander - allows for -// non-standard expansion behaviour (e.g. step-wise). -pub fn expand_crate_with_expander(expander: &mut MacroExpander, - user_exts: Vec, - c: Crate) -> Crate { - expander.cx.initialize(user_exts, &c); - expander.expand_crate(c) -} - // A Marker adds the given mark to the syntax context and // sets spans' `expn_id` to the given expn_id (unless it is `None`). struct Marker { mark: Mark, expn_id: Option } diff --git a/src/libsyntax/ext/placeholders.rs b/src/libsyntax/ext/placeholders.rs index 0ede6dd98e5b8..e323dd2f62327 100644 --- a/src/libsyntax/ext/placeholders.rs +++ b/src/libsyntax/ext/placeholders.rs @@ -88,10 +88,11 @@ impl<'a, 'b> PlaceholderExpander<'a, 'b> { } pub fn add(&mut self, id: ast::NodeId, expansion: Expansion) { + let expansion = expansion.fold_with(self); self.expansions.insert(id, expansion); } - pub fn remove(&mut self, id: ast::NodeId) -> Expansion { + fn remove(&mut self, id: ast::NodeId) -> Expansion { self.expansions.remove(&id).unwrap() } } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 3c106970232cd..ecb437f31a5ad 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -112,7 +112,7 @@ pub fn print_crate<'a>(cm: &'a CodeMap, out, ann, is_expanded); - if is_expanded && !std_inject::no_std(krate) { + if is_expanded && !std_inject::injected_crate_name(krate).is_none() { // We need to print `#![no_std]` (and its feature gate) so that // compiling pretty-printed source won't inject libstd again. // However we don't want these attributes in the AST because diff --git a/src/libsyntax/std_inject.rs b/src/libsyntax/std_inject.rs index d1454ab06cbc8..1b63a2b70763a 100644 --- a/src/libsyntax/std_inject.rs +++ b/src/libsyntax/std_inject.rs @@ -34,23 +34,25 @@ fn ignored_span(sess: &ParseSess, sp: Span) -> Span { return sp; } -pub fn no_core(krate: &ast::Crate) -> bool { - attr::contains_name(&krate.attrs, "no_core") -} - -pub fn no_std(krate: &ast::Crate) -> bool { - attr::contains_name(&krate.attrs, "no_std") || no_core(krate) +pub fn injected_crate_name(krate: &ast::Crate) -> Option<&'static str> { + if attr::contains_name(&krate.attrs, "no_core") { + None + } else if attr::contains_name(&krate.attrs, "no_std") { + Some("core") + } else { + Some("std") + } } pub fn maybe_inject_crates_ref(sess: &ParseSess, mut krate: ast::Crate, alt_std_name: Option) -> ast::Crate { - if no_core(&krate) { - return krate; - } + let name = match injected_crate_name(&krate) { + Some(name) => name, + None => return krate, + }; - let name = if no_std(&krate) { "core" } else { "std" }; let crate_name = token::intern(&alt_std_name.unwrap_or(name.to_string())); krate.module.items.insert(0, P(ast::Item { diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs index e9d2c0a503be9..f336b26ae41f2 100644 --- a/src/libsyntax_ext/lib.rs +++ b/src/libsyntax_ext/lib.rs @@ -50,20 +50,23 @@ pub mod deriving; use std::rc::Rc; use syntax::ast; -use syntax::ext::base::{MacroExpanderFn, NormalTT, IdentTT, MultiModifier}; +use syntax::ext::base::{MacroExpanderFn, NormalTT, IdentTT, MultiModifier, NamedSyntaxExtension}; use syntax::ext::tt::macro_rules::MacroRulesExpander; use syntax::parse::token::intern; -pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver, enable_quotes: bool) { +pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver, + user_exts: Vec, + enable_quotes: bool) { let mut register = |name, ext| { - resolver.add_ext(ast::Ident::with_empty_ctxt(intern(name)), Rc::new(ext)); + resolver.add_ext(ast::Ident::with_empty_ctxt(name), Rc::new(ext)); }; - register("macro_rules", IdentTT(Box::new(MacroRulesExpander), None, false)); + register(intern("macro_rules"), IdentTT(Box::new(MacroRulesExpander), None, false)); macro_rules! register { ($( $name:ident: $f:expr, )*) => { $( - register(stringify!($name), NormalTT(Box::new($f as MacroExpanderFn), None, false)); + register(intern(stringify!($name)), + NormalTT(Box::new($f as MacroExpanderFn), None, false)); )* } } @@ -108,7 +111,11 @@ pub fn register_builtins(resolver: &mut syntax::ext::base::Resolver, enable_quot } // format_args uses `unstable` things internally. - register("format_args", NormalTT(Box::new(format::expand_format_args), None, true)); + register(intern("format_args"), NormalTT(Box::new(format::expand_format_args), None, true)); - register("derive", MultiModifier(Box::new(deriving::expand_derive))); + register(intern("derive"), MultiModifier(Box::new(deriving::expand_derive))); + + for (name, ext) in user_exts { + register(name, ext); + } } diff --git a/src/test/compile-fail/macro-shadowing.rs b/src/test/compile-fail/macro-shadowing.rs index 22463825ef98f..8381dc34a6a15 100644 --- a/src/test/compile-fail/macro-shadowing.rs +++ b/src/test/compile-fail/macro-shadowing.rs @@ -12,31 +12,28 @@ macro_rules! foo { () => {} } macro_rules! macro_one { () => {} } +#[macro_use(macro_two)] extern crate two_macros; macro_rules! m1 { () => { macro_rules! foo { () => {} } //~ ERROR `foo` is already in scope - //~^ NOTE macro-expanded `macro_rules!`s and `#[macro_use]`s may not shadow existing macros + //~^ NOTE macro-expanded `macro_rules!`s may not shadow existing macros - #[macro_use] //~ ERROR `macro_one` is already in scope - //~^ NOTE macro-expanded `macro_rules!`s and `#[macro_use]`s may not shadow existing macros - extern crate two_macros; + #[macro_use] //~ ERROR `macro_two` is already in scope + //~^ NOTE macro-expanded `#[macro_use]`s may not shadow existing macros + extern crate two_macros as __; }} m1!(); //~ NOTE in this expansion //~| NOTE in this expansion //~| NOTE in this expansion //~| NOTE in this expansion -fn f() { macro_one!(); } foo!(); macro_rules! m2 { () => { macro_rules! foo { () => {} } - #[macro_use] extern crate two_macros as __; - - fn g() { macro_one!(); } foo!(); }} m2!(); -//^ Since `foo` and `macro_one` are not used outside this expansion, they are not shadowing errors. +//^ Since `foo` is not used outside this expansion, it is not a shadowing error. fn main() {} diff --git a/src/test/compile-fail/macro-use-scope.rs b/src/test/compile-fail/macro-use-scope.rs index 5256396a242c9..9d389413ba9ad 100644 --- a/src/test/compile-fail/macro-use-scope.rs +++ b/src/test/compile-fail/macro-use-scope.rs @@ -19,10 +19,10 @@ fn f() { #[macro_use(macro_one)] // Check that this macro is usable in the above function extern crate two_macros; +fn g() { + macro_two!(); +} macro_rules! m { () => { - fn g() { - macro_two!(); - } #[macro_use(macro_two)] // Check that this macro is usable in the above function extern crate two_macros as _two_macros; } }