diff options
| author | bors <bors@rust-lang.org> | 2018-09-09 21:00:55 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2018-09-09 21:00:55 +0000 |
| commit | 2d4e34ca8bb1369f7e0eea4cb50e6faa0827a6e5 (patch) | |
| tree | 9e250b17aba2c7174bea5e7d7fb1a598da7e0ff2 /src/librustc_resolve | |
| parent | f50b7758f4dc85dc1c5e38258adaa94213ac6ed1 (diff) | |
| parent | 2dce3779bbc0353ff9fb544774417a851027fcab (diff) | |
| download | rust-2d4e34ca8bb1369f7e0eea4cb50e6faa0827a6e5.tar.gz rust-2d4e34ca8bb1369f7e0eea4cb50e6faa0827a6e5.zip | |
Auto merge of #53778 - petrochenkov:shadrelax2, r=nikomatsakis
resolve: Relax shadowing restrictions on macro-expanded macros Previously any macro-expanded macros weren't allowed to shadow macros from outer scopes. Now only "more macro-expanded" macros cannot shadow "less macro-expanded" macros. See comments to `fn may_appear_after` and added tests for more details and examples. The functional changes are a21f6f588fc28c97533130ae44a6957b579ab58c and 46dd365ce9ca0a6b8653849b80267763c542842a, other commits are refactorings.
Diffstat (limited to 'src/librustc_resolve')
| -rw-r--r-- | src/librustc_resolve/build_reduced_graph.rs | 25 | ||||
| -rw-r--r-- | src/librustc_resolve/lib.rs | 119 | ||||
| -rw-r--r-- | src/librustc_resolve/macros.rs | 296 | ||||
| -rw-r--r-- | src/librustc_resolve/resolve_imports.rs | 9 |
4 files changed, 246 insertions, 203 deletions
diff --git a/src/librustc_resolve/build_reduced_graph.rs b/src/librustc_resolve/build_reduced_graph.rs index 92a9057d96e..63f643d7a29 100644 --- a/src/librustc_resolve/build_reduced_graph.rs +++ b/src/librustc_resolve/build_reduced_graph.rs @@ -946,7 +946,7 @@ impl<'a, 'cl> Resolver<'a, 'cl> { pub struct BuildReducedGraphVisitor<'a, 'b: 'a, 'c: 'b> { pub resolver: &'a mut Resolver<'b, 'c>, - pub legacy_scope: LegacyScope<'b>, + pub current_legacy_scope: LegacyScope<'b>, pub expansion: Mark, } @@ -956,7 +956,8 @@ impl<'a, 'b, 'cl> BuildReducedGraphVisitor<'a, 'b, 'cl> { self.resolver.current_module.unresolved_invocations.borrow_mut().insert(mark); let invocation = self.resolver.invocations[&mark]; invocation.module.set(self.resolver.current_module); - invocation.legacy_scope.set(self.legacy_scope); + invocation.parent_legacy_scope.set(self.current_legacy_scope); + invocation.output_legacy_scope.set(self.current_legacy_scope); invocation } } @@ -982,29 +983,30 @@ impl<'a, 'b, 'cl> Visitor<'a> for BuildReducedGraphVisitor<'a, 'b, 'cl> { fn visit_item(&mut self, item: &'a Item) { let macro_use = match item.node { ItemKind::MacroDef(..) => { - self.resolver.define_macro(item, self.expansion, &mut self.legacy_scope); + self.resolver.define_macro(item, self.expansion, &mut self.current_legacy_scope); return } ItemKind::Mac(..) => { - self.legacy_scope = LegacyScope::Expansion(self.visit_invoc(item.id)); + self.current_legacy_scope = LegacyScope::Invocation(self.visit_invoc(item.id)); return } ItemKind::Mod(..) => self.resolver.contains_macro_use(&item.attrs), _ => false, }; - let (parent, legacy_scope) = (self.resolver.current_module, self.legacy_scope); + let orig_current_module = self.resolver.current_module; + let orig_current_legacy_scope = self.current_legacy_scope; self.resolver.build_reduced_graph_for_item(item, self.expansion); visit::walk_item(self, item); - self.resolver.current_module = parent; + self.resolver.current_module = orig_current_module; if !macro_use { - self.legacy_scope = legacy_scope; + self.current_legacy_scope = orig_current_legacy_scope; } } fn visit_stmt(&mut self, stmt: &'a ast::Stmt) { if let ast::StmtKind::Mac(..) = stmt.node { - self.legacy_scope = LegacyScope::Expansion(self.visit_invoc(stmt.id)); + self.current_legacy_scope = LegacyScope::Invocation(self.visit_invoc(stmt.id)); } else { visit::walk_stmt(self, stmt); } @@ -1021,11 +1023,12 @@ impl<'a, 'b, 'cl> Visitor<'a> for BuildReducedGraphVisitor<'a, 'b, 'cl> { } fn visit_block(&mut self, block: &'a Block) { - let (parent, legacy_scope) = (self.resolver.current_module, self.legacy_scope); + let orig_current_module = self.resolver.current_module; + let orig_current_legacy_scope = self.current_legacy_scope; 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; + self.resolver.current_module = orig_current_module; + self.current_legacy_scope = orig_current_legacy_scope; } fn visit_trait_item(&mut self, item: &'a TraitItem) { diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 453e7421ff1..5cb615554ee 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -1177,9 +1177,7 @@ struct UseError<'a> { } struct AmbiguityError<'a> { - span: Span, - name: Name, - lexical: bool, + ident: Ident, b1: &'a NameBinding<'a>, b2: &'a NameBinding<'a>, } @@ -1283,6 +1281,26 @@ impl<'a> NameBinding<'a> { fn descr(&self) -> &'static str { if self.is_extern_crate() { "extern crate" } else { self.def().kind_name() } } + + // Suppose that we resolved macro invocation with `invoc_id` to binding `binding` at some + // expansion round `max(invoc_id, binding)` when they both emerged from macros. + // Then this function returns `true` if `self` may emerge from a macro *after* that + // in some later round and screw up our previously found resolution. + // See more detailed explanation in + // https://github.com/rust-lang/rust/pull/53778#issuecomment-419224049 + fn may_appear_after(&self, invoc_id: Mark, binding: &NameBinding) -> bool { + // self > max(invoc_id, binding) => !(self <= invoc_id || self <= binding) + // Expansions are partially ordered, so "may appear after" is an inversion of + // "certainly appears before or simultaneously" and includes unordered cases. + let self_parent_expansion = self.expansion; + let other_parent_expansion = binding.expansion; + let invoc_parent_expansion = invoc_id.parent(); + let certainly_before_other_or_simultaneously = + other_parent_expansion.is_descendant_of(self_parent_expansion); + let certainly_before_invoc_or_simultaneously = + invoc_parent_expansion.is_descendant_of(self_parent_expansion); + !(certainly_before_other_or_simultaneously || certainly_before_invoc_or_simultaneously) + } } /// Interns the names of the primitive types. @@ -1416,8 +1434,6 @@ pub struct Resolver<'a, 'b: 'a> { proc_mac_errors: Vec<macros::ProcMacError>, /// crate-local macro expanded `macro_export` referred to by a module-relative path macro_expanded_macro_export_errors: BTreeSet<(Span, Span)>, - /// macro-expanded `macro_rules` shadowing existing macros - disallowed_shadowing: Vec<&'a LegacyBinding<'a>>, arenas: &'a ResolverArenas<'a>, dummy_binding: &'a NameBinding<'a>, @@ -1729,7 +1745,6 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { ambiguity_errors: Vec::new(), use_injections: Vec::new(), proc_mac_errors: Vec::new(), - disallowed_shadowing: Vec::new(), macro_expanded_macro_export_errors: BTreeSet::new(), arenas, @@ -1815,7 +1830,7 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { self.arenas.alloc_module(module) } - fn record_use(&mut self, ident: Ident, ns: Namespace, binding: &'a NameBinding<'a>, span: Span) + fn record_use(&mut self, ident: Ident, ns: Namespace, binding: &'a NameBinding<'a>) -> bool /* true if an error was reported */ { match binding.kind { NameBindingKind::Import { directive, binding, ref used } @@ -1824,13 +1839,11 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { directive.used.set(true); self.used_imports.insert((directive.id, ns)); self.add_to_glob_map(directive.id, ident); - self.record_use(ident, ns, binding, span) + self.record_use(ident, ns, binding) } NameBindingKind::Import { .. } => false, NameBindingKind::Ambiguity { b1, b2 } => { - self.ambiguity_errors.push(AmbiguityError { - span, name: ident.name, lexical: false, b1, b2, - }); + self.ambiguity_errors.push(AmbiguityError { ident, b1, b2 }); true } _ => false @@ -2850,7 +2863,7 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { Def::Const(..) if is_syntactic_ambiguity => { // Disambiguate in favor of a unit struct/variant // or constant pattern. - self.record_use(ident, ValueNS, binding.unwrap(), ident.span); + self.record_use(ident, ValueNS, binding.unwrap()); Some(PathResolution::new(def)) } Def::StructCtor(..) | Def::VariantCtor(..) | @@ -3484,6 +3497,20 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { path_span: Span, crate_lint: CrateLint, ) -> PathResult<'a> { + self.resolve_path_with_invoc_id(base_module, path, opt_ns, Mark::root(), + record_used, path_span, crate_lint) + } + + fn resolve_path_with_invoc_id( + &mut self, + base_module: Option<ModuleOrUniformRoot<'a>>, + path: &[Ident], + opt_ns: Option<Namespace>, // `None` indicates a module path + invoc_id: Mark, + record_used: bool, + path_span: Span, + crate_lint: CrateLint, + ) -> PathResult<'a> { let mut module = base_module; let mut allow_super = true; let mut second_binding = None; @@ -3572,8 +3599,9 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { self.resolve_ident_in_module(module, ident, ns, record_used, path_span) } else if opt_ns == Some(MacroNS) { assert!(ns == TypeNS); - self.resolve_lexical_macro_path_segment(ident, ns, record_used, record_used, - false, path_span).map(|(b, _)| b) + self.resolve_lexical_macro_path_segment(ident, ns, invoc_id, record_used, + record_used, false, path_span) + .map(|(binding, _)| binding) } else { let record_used_id = if record_used { crate_lint.node_id().or(Some(CRATE_NODE_ID)) } else { None }; @@ -4514,35 +4542,33 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { vis.is_accessible_from(module.normal_ancestor_id, self) } - fn report_ambiguity_error( - &self, name: Name, span: Span, _lexical: bool, - def1: Def, is_import1: bool, is_glob1: bool, from_expansion1: bool, span1: Span, - def2: Def, is_import2: bool, _is_glob2: bool, _from_expansion2: bool, span2: Span, - ) { + fn report_ambiguity_error(&self, ident: Ident, b1: &NameBinding, b2: &NameBinding) { let participle = |is_import: bool| if is_import { "imported" } else { "defined" }; - let msg1 = format!("`{}` could refer to the name {} here", name, participle(is_import1)); + let msg1 = + format!("`{}` could refer to the name {} here", ident, participle(b1.is_import())); let msg2 = - format!("`{}` could also refer to the name {} here", name, participle(is_import2)); - let note = if from_expansion1 { - Some(if let Def::Macro(..) = def1 { + format!("`{}` could also refer to the name {} here", ident, participle(b2.is_import())); + let note = if b1.expansion != Mark::root() { + Some(if let Def::Macro(..) = b1.def() { format!("macro-expanded {} do not shadow", - if is_import1 { "macro imports" } else { "macros" }) + if b1.is_import() { "macro imports" } else { "macros" }) } else { format!("macro-expanded {} do not shadow when used in a macro invocation path", - if is_import1 { "imports" } else { "items" }) + if b1.is_import() { "imports" } else { "items" }) }) - } else if is_glob1 { - Some(format!("consider adding an explicit import of `{}` to disambiguate", name)) + } else if b1.is_glob_import() { + Some(format!("consider adding an explicit import of `{}` to disambiguate", ident)) } else { None }; - let mut err = struct_span_err!(self.session, span, E0659, "`{}` is ambiguous", name); - err.span_note(span1, &msg1); - match def2 { - Def::Macro(..) if span2.is_dummy() => - err.note(&format!("`{}` is also a builtin macro", name)), - _ => err.span_note(span2, &msg2), + let mut err = struct_span_err!(self.session, ident.span, E0659, "`{}` is ambiguous", ident); + err.span_label(ident.span, "ambiguous name"); + err.span_note(b1.span, &msg1); + match b2.def() { + Def::Macro(..) if b2.span.is_dummy() => + err.note(&format!("`{}` is also a builtin macro", ident)), + _ => err.span_note(b2.span, &msg2), }; if let Some(note) = note { err.note(¬e); @@ -4551,7 +4577,6 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { } fn report_errors(&mut self, krate: &Crate) { - self.report_shadowing_errors(); self.report_with_use_injections(krate); self.report_proc_macro_import(krate); let mut reported_spans = FxHashSet(); @@ -4567,15 +4592,9 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { ); } - for &AmbiguityError { span, name, b1, b2, lexical } in &self.ambiguity_errors { - if reported_spans.insert(span) { - self.report_ambiguity_error( - name, span, lexical, - b1.def(), b1.is_import(), b1.is_glob_import(), - b1.expansion != Mark::root(), b1.span, - b2.def(), b2.is_import(), b2.is_glob_import(), - b2.expansion != Mark::root(), b2.span, - ); + for &AmbiguityError { ident, b1, b2 } in &self.ambiguity_errors { + if reported_spans.insert(ident.span) { + self.report_ambiguity_error(ident, b1, b2); } } @@ -4595,20 +4614,6 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> { } } - fn report_shadowing_errors(&mut self) { - let mut reported_errors = FxHashSet(); - for binding in replace(&mut self.disallowed_shadowing, Vec::new()) { - 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)") - .emit(); - } - } - } - fn report_conflict<'b>(&mut self, parent: Module, ident: Ident, diff --git a/src/librustc_resolve/macros.rs b/src/librustc_resolve/macros.rs index fc6e0eb3b7f..93874ee0e85 100644 --- a/src/librustc_resolve/macros.rs +++ b/src/librustc_resolve/macros.rs @@ -45,18 +45,21 @@ use std::mem; use rustc_data_structures::sync::Lrc; use rustc_data_structures::small_vec::ExpectOne; +#[derive(Clone, Copy)] crate struct FromPrelude(bool); -crate struct FromExpansion(bool); #[derive(Clone)] pub struct InvocationData<'a> { - pub module: Cell<Module<'a>>, - pub def_index: DefIndex, - // The scope in which the invocation path is resolved. - pub legacy_scope: Cell<LegacyScope<'a>>, - // The smallest scope that includes this invocation's expansion, - // or `Empty` if this invocation has not been expanded yet. - pub expansion: Cell<LegacyScope<'a>>, + def_index: DefIndex, + /// Module in which the macro was invoked. + crate module: Cell<Module<'a>>, + /// Legacy scope in which the macro was invoked. + /// The invocation path is resolved in this scope. + crate parent_legacy_scope: Cell<LegacyScope<'a>>, + /// Legacy scope *produced* by expanding this macro invocation, + /// includes all the macro_rules items, other invocations, etc generated by it. + /// Set to the parent scope if the macro is not expanded yet (as if the macro produced nothing). + crate output_legacy_scope: Cell<LegacyScope<'a>>, } impl<'a> InvocationData<'a> { @@ -64,31 +67,38 @@ impl<'a> InvocationData<'a> { InvocationData { module: Cell::new(graph_root), def_index: CRATE_DEF_INDEX, - legacy_scope: Cell::new(LegacyScope::Empty), - expansion: Cell::new(LegacyScope::Empty), + parent_legacy_scope: Cell::new(LegacyScope::Empty), + output_legacy_scope: Cell::new(LegacyScope::Empty), } } } +/// Binding produced by a `macro_rules` item. +/// Not modularized, can shadow previous legacy bindings, etc. +pub struct LegacyBinding<'a> { + binding: &'a NameBinding<'a>, + /// Legacy scope into which the `macro_rules` item was planted. + parent_legacy_scope: LegacyScope<'a>, + ident: Ident, +} + +/// Scope introduced by a `macro_rules!` macro. +/// Starts at the macro's definition and ends at the end of the macro's parent module +/// (named or unnamed), or even further if it escapes with `#[macro_use]`. +/// Some macro invocations need to introduce legacy scopes too because they +/// potentially can expand into macro definitions. #[derive(Copy, Clone)] pub enum LegacyScope<'a> { + /// Created when invocation data is allocated in the arena, + /// must be replaced with a proper scope later. + Uninitialized, + /// Empty "root" scope at the crate start containing no names. 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 + /// Scope introduced by a `macro_rules!` macro definition. Binding(&'a LegacyBinding<'a>), -} - -pub struct LegacyBinding<'a> { - pub parent: Cell<LegacyScope<'a>>, - pub ident: Ident, - def_id: DefId, - pub span: Span, -} - -impl<'a> LegacyBinding<'a> { - fn def(&self) -> Def { - Def::Macro(self.def_id, MacroKind::Bang) - } + /// Scope introduced by a macro invocation that can potentially + /// create a `macro_rules!` macro definition. + Invocation(&'a InvocationData<'a>), } pub struct ProcMacError { @@ -110,8 +120,8 @@ impl<'a, 'crateloader: 'a> base::Resolver for Resolver<'a, 'crateloader> { self.invocations.insert(mark, self.arenas.alloc_invocation_data(InvocationData { module: Cell::new(module), def_index: module.def_id().unwrap().index, - legacy_scope: Cell::new(LegacyScope::Empty), - expansion: Cell::new(LegacyScope::Empty), + parent_legacy_scope: Cell::new(LegacyScope::Empty), + output_legacy_scope: Cell::new(LegacyScope::Empty), })); mark } @@ -183,11 +193,11 @@ impl<'a, 'crateloader: 'a> base::Resolver for Resolver<'a, 'crateloader> { } let mut visitor = BuildReducedGraphVisitor { resolver: self, - legacy_scope: LegacyScope::Invocation(invocation), + current_legacy_scope: invocation.parent_legacy_scope.get(), expansion: mark, }; fragment.visit_with(&mut visitor); - invocation.expansion.set(visitor.legacy_scope); + invocation.output_legacy_scope.set(visitor.current_legacy_scope); } fn add_builtin(&mut self, ident: ast::Ident, ext: Lrc<SyntaxExtension>) { @@ -437,12 +447,12 @@ impl<'a, 'cl> Resolver<'a, 'cl> { Ok((def, self.get_macro(def))) } - pub fn resolve_macro_to_def_inner(&mut self, path: &ast::Path, kind: MacroKind, scope: Mark, + pub fn resolve_macro_to_def_inner(&mut self, path: &ast::Path, kind: MacroKind, invoc_id: Mark, derives_in_scope: &[ast::Path], force: bool) -> Result<Def, Determinacy> { let ast::Path { ref segments, span } = *path; let mut path: Vec<_> = segments.iter().map(|seg| seg.ident).collect(); - let invocation = self.invocations[&scope]; + let invocation = self.invocations[&invoc_id]; let module = invocation.module.get(); self.current_module = if module.is_trait() { module.parent.unwrap() } else { module }; @@ -454,8 +464,8 @@ impl<'a, 'cl> Resolver<'a, 'cl> { } if path.len() > 1 { - let res = self.resolve_path(None, &path, Some(MacroNS), false, span, CrateLint::No); - let def = match res { + let def = match self.resolve_path_with_invoc_id(None, &path, Some(MacroNS), invoc_id, + false, span, CrateLint::No) { PathResult::NonModule(path_res) => match path_res.base_def() { Def::Err => Err(Determinacy::Determined), def @ _ => { @@ -486,11 +496,13 @@ impl<'a, 'cl> Resolver<'a, 'cl> { } } - let legacy_resolution = self.resolve_legacy_scope(&invocation.legacy_scope, path[0], false); - let result = if let Some((legacy_binding, _)) = legacy_resolution { + let legacy_resolution = self.resolve_legacy_scope( + path[0], invoc_id, invocation.parent_legacy_scope.get(), false + ); + let result = if let Some(legacy_binding) = legacy_resolution { Ok(legacy_binding.def()) } else { - match self.resolve_lexical_macro_path_segment(path[0], MacroNS, false, force, + match self.resolve_lexical_macro_path_segment(path[0], MacroNS, invoc_id, false, force, kind == MacroKind::Attr, span) { Ok((binding, _)) => Ok(binding.def_ignoring_ambiguity()), Err(Determinacy::Undetermined) => return Err(Determinacy::Undetermined), @@ -502,7 +514,7 @@ impl<'a, 'cl> Resolver<'a, 'cl> { }; self.current_module.nearest_item_scope().legacy_macro_resolutions.borrow_mut() - .push((scope, path[0], kind, result.ok())); + .push((invoc_id, path[0], kind, result.ok())); if let Ok(Def::NonMacroAttr(NonMacroAttrKind::Custom)) = result {} else { return result; @@ -521,7 +533,7 @@ impl<'a, 'cl> Resolver<'a, 'cl> { enum ConvertToDeriveHelper { Yes, No, DontKnow } let mut convert_to_derive_helper = ConvertToDeriveHelper::No; for derive in derives_in_scope { - match self.resolve_macro_path(derive, MacroKind::Derive, scope, &[], force) { + match self.resolve_macro_path(derive, MacroKind::Derive, invoc_id, &[], force) { Ok(ext) => if let SyntaxExtension::ProcMacroDerive(_, ref inert_attrs, _) = *ext { if inert_attrs.contains(&path[0].name) { convert_to_derive_helper = ConvertToDeriveHelper::Yes; @@ -549,10 +561,11 @@ impl<'a, 'cl> Resolver<'a, 'cl> { &mut self, mut ident: Ident, ns: Namespace, + invoc_id: Mark, record_used: bool, force: bool, is_attr: bool, - path_span: Span + path_span: Span, ) -> Result<(&'a NameBinding<'a>, FromPrelude), Determinacy> { // General principles: // 1. Not controlled (user-defined) names should have higher priority than controlled names @@ -585,15 +598,18 @@ impl<'a, 'cl> Resolver<'a, 'cl> { assert!(force || !record_used); // `record_used` implies `force` ident = ident.modern(); - // Names from inner scope that can't shadow names from outer scopes, e.g. - // mod m { ... } + // This is *the* result, resolution from the scope closest to the resolved identifier. + // However, sometimes this result is "weak" because it comes from a glob import or + // a macro expansion, and in this case it cannot shadow names from outer scopes, e.g. + // mod m { ... } // solution in outer scope // { - // use prefix::*; // if this imports another `m`, then it can't shadow the outer `m` - // // and we have and ambiguity error + // use prefix::*; // imports another `m` - innermost solution + // // weak, cannot shadow the outer `m`, need to report ambiguity error // m::mac!(); // } - // This includes names from globs and from macro expansions. - let mut potentially_ambiguous_result: Option<(&NameBinding, FromPrelude)> = None; + // So we have to save the innermost solution and continue searching in outer scopes + // to detect potential ambiguities. + let mut innermost_result: Option<(&NameBinding, FromPrelude)> = None; enum WhereToResolve<'a> { Module(Module<'a>), @@ -736,33 +752,24 @@ impl<'a, 'cl> Resolver<'a, 'cl> { return Ok(result); } - // Found a solution that is ambiguous with a previously found solution. - // Push an ambiguity error for later reporting and - // return something for better recovery. - if let Some(previous_result) = potentially_ambiguous_result { - if result.0.def() != previous_result.0.def() { + if let Some(innermost_result) = innermost_result { + // Found another solution, if the first one was "weak", report an error. + if result.0.def() != innermost_result.0.def() && + (innermost_result.0.is_glob_import() || + innermost_result.0.may_appear_after(invoc_id, result.0)) { self.ambiguity_errors.push(AmbiguityError { - span: path_span, - name: ident.name, - b1: previous_result.0, + ident, + b1: innermost_result.0, b2: result.0, - lexical: true, }); - return Ok(previous_result); + return Ok(innermost_result); } + } else { + // Found the first solution. + innermost_result = Some(result); } - // Found a solution that's not an ambiguity yet, but is "suspicious" and - // can participate in ambiguities later on. - // Remember it and go search for other solutions in outer scopes. - if result.0.is_glob_import() || result.0.expansion != Mark::root() { - potentially_ambiguous_result = Some(result); - - continue_search!(); - } - - // Found a solution that can't be ambiguous, great success. - return Ok(result); + continue_search!(); }, Err(Determinacy::Determined) => { continue_search!(); @@ -771,9 +778,9 @@ impl<'a, 'cl> Resolver<'a, 'cl> { } } - // Previously found potentially ambiguous result turned out to not be ambiguous after all. - if let Some(previous_result) = potentially_ambiguous_result { - return Ok(previous_result); + // The first found solution was the only one, return it. + if let Some(innermost_result) = innermost_result { + return Ok(innermost_result); } let determinacy = Determinacy::determined(force); @@ -791,45 +798,79 @@ impl<'a, 'cl> Resolver<'a, 'cl> { } } - crate fn resolve_legacy_scope(&mut self, - mut scope: &'a Cell<LegacyScope<'a>>, - ident: Ident, - record_used: bool) - -> Option<(&'a LegacyBinding<'a>, FromExpansion)> { + fn resolve_legacy_scope(&mut self, + ident: Ident, + invoc_id: Mark, + invoc_parent_legacy_scope: LegacyScope<'a>, + record_used: bool) + -> Option<&'a NameBinding<'a>> { let ident = ident.modern(); - let mut relative_depth: u32 = 0; + + // This is *the* result, resolution from the scope closest to the resolved identifier. + // However, sometimes this result is "weak" because it comes from a macro expansion, + // and in this case it cannot shadow names from outer scopes, e.g. + // macro_rules! m { ... } // solution in outer scope + // { + // define_m!(); // generates another `macro_rules! m` - innermost solution + // // weak, cannot shadow the outer `m`, need to report ambiguity error + // m!(); + // } + // So we have to save the innermost solution and continue searching in outer scopes + // to detect potential ambiguities. + let mut innermost_result: Option<&NameBinding> = None; + + // Go through all the scopes and try to resolve the name. + let mut where_to_resolve = invoc_parent_legacy_scope; loop { - match scope.get() { - LegacyScope::Empty => break, - LegacyScope::Expansion(invocation) => { - match invocation.expansion.get() { - LegacyScope::Invocation(_) => scope.set(invocation.legacy_scope.get()), - LegacyScope::Empty => { - scope = &invocation.legacy_scope; - } - _ => { - relative_depth += 1; - scope = &invocation.expansion; - } + let result = match where_to_resolve { + LegacyScope::Binding(legacy_binding) if ident == legacy_binding.ident => + Some(legacy_binding.binding), + _ => None, + }; + + macro_rules! continue_search { () => { + where_to_resolve = match where_to_resolve { + LegacyScope::Empty => break, // nowhere else to search + LegacyScope::Binding(binding) => binding.parent_legacy_scope, + LegacyScope::Invocation(invocation) => invocation.output_legacy_scope.get(), + LegacyScope::Uninitialized => unreachable!(), + }; + + continue; + }} + + match result { + Some(result) => { + if !record_used { + return Some(result); } - } - LegacyScope::Invocation(invocation) => { - relative_depth = relative_depth.saturating_sub(1); - scope = &invocation.legacy_scope; - } - LegacyScope::Binding(potential_binding) => { - if potential_binding.ident == ident { - if record_used && relative_depth > 0 { - self.disallowed_shadowing.push(potential_binding); + + if let Some(innermost_result) = innermost_result { + // Found another solution, if the first one was "weak", report an error. + if result.def() != innermost_result.def() && + innermost_result.may_appear_after(invoc_id, result) { + self.ambiguity_errors.push(AmbiguityError { + ident, + b1: innermost_result, + b2: result, + }); + return Some(innermost_result); } - return Some((potential_binding, FromExpansion(relative_depth > 0))); + } else { + // Found the first solution. + innermost_result = Some(result); } - scope = &potential_binding.parent; + + continue_search!(); } - }; + None => { + continue_search!(); + } + } } - None + // The first found solution was the only one (or there was no solution at all), return it. + innermost_result } pub fn finalize_current_module_macro_resolutions(&mut self) { @@ -844,17 +885,19 @@ impl<'a, 'cl> Resolver<'a, 'cl> { } } - for &(mark, ident, kind, def) in module.legacy_macro_resolutions.borrow().iter() { + for &(invoc_id, ident, kind, def) in module.legacy_macro_resolutions.borrow().iter() { let span = ident.span; - let legacy_scope = &self.invocations[&mark].legacy_scope; - let legacy_resolution = self.resolve_legacy_scope(legacy_scope, ident, true); - let resolution = self.resolve_lexical_macro_path_segment(ident, MacroNS, true, true, - kind == MacroKind::Attr, span); + let invocation = self.invocations[&invoc_id]; + let legacy_resolution = self.resolve_legacy_scope( + ident, invoc_id, invocation.parent_legacy_scope.get(), true + ); + let resolution = self.resolve_lexical_macro_path_segment( + ident, MacroNS, invoc_id, true, true, kind == MacroKind::Attr, span + ); let check_consistency = |this: &Self, new_def: Def| { if let Some(def) = def { - if this.ambiguity_errors.is_empty() && this.disallowed_shadowing.is_empty() && - new_def != def && new_def != Def::Err { + if this.ambiguity_errors.is_empty() && new_def != def && new_def != Def::Err { // Make sure compilation does not succeed if preferred macro resolution // has changed after the macro had been expanded. In theory all such // situations should be reported as ambiguity errors, so this is span-bug. @@ -882,31 +925,23 @@ impl<'a, 'cl> Resolver<'a, 'cl> { self.suggest_macro_name(&ident.as_str(), kind, &mut err, span); err.emit(); }, - (Some((legacy_binding, FromExpansion(from_expansion))), - Ok((binding, FromPrelude(false)))) | - (Some((legacy_binding, FromExpansion(from_expansion @ true))), - Ok((binding, FromPrelude(true)))) => { - if legacy_binding.def() != binding.def_ignoring_ambiguity() { - self.report_ambiguity_error( - ident.name, span, true, - legacy_binding.def(), false, false, - from_expansion, legacy_binding.span, - binding.def(), binding.is_import(), binding.is_glob_import(), - binding.expansion != Mark::root(), binding.span, - ); + (Some(legacy_binding), Ok((binding, FromPrelude(from_prelude)))) + if !from_prelude || legacy_binding.may_appear_after(invoc_id, binding) => { + if legacy_binding.def_ignoring_ambiguity() != binding.def_ignoring_ambiguity() { + self.report_ambiguity_error(ident, legacy_binding, binding); } }, - // OK, non-macro-expanded legacy wins over macro prelude even if defs are different - (Some((legacy_binding, FromExpansion(false))), Ok((_, FromPrelude(true)))) | + // OK, non-macro-expanded legacy wins over prelude even if defs are different + (Some(legacy_binding), Ok(_)) | // OK, unambiguous resolution - (Some((legacy_binding, _)), Err(_)) => { + (Some(legacy_binding), Err(_)) => { check_consistency(self, legacy_binding.def()); } // OK, unambiguous resolution (None, Ok((binding, FromPrelude(from_prelude)))) => { check_consistency(self, binding.def_ignoring_ambiguity()); if from_prelude { - self.record_use(ident, MacroNS, binding, span); + self.record_use(ident, MacroNS, binding); self.err_if_macro_use_proc_macro(ident.name, span, binding); } } @@ -981,8 +1016,8 @@ impl<'a, 'cl> Resolver<'a, 'cl> { arenas.alloc_invocation_data(InvocationData { def_index: invoc.def_index, module: Cell::new(graph_root), - expansion: Cell::new(LegacyScope::Empty), - legacy_scope: Cell::new(LegacyScope::Empty), + parent_legacy_scope: Cell::new(LegacyScope::Uninitialized), + output_legacy_scope: Cell::new(LegacyScope::Uninitialized), }) }); }; @@ -997,7 +1032,7 @@ impl<'a, 'cl> Resolver<'a, 'cl> { pub fn define_macro(&mut self, item: &ast::Item, expansion: Mark, - legacy_scope: &mut LegacyScope<'a>) { + current_legacy_scope: &mut LegacyScope<'a>) { self.local_macro_def_scopes.insert(item.id, self.current_module); let ident = item.ident; if ident.name == "macro_rules" { @@ -1014,10 +1049,13 @@ impl<'a, 'cl> Resolver<'a, 'cl> { if def.legacy { let ident = ident.modern(); self.macro_names.insert(ident); - *legacy_scope = LegacyScope::Binding(self.arenas.alloc_legacy_binding(LegacyBinding { - parent: Cell::new(*legacy_scope), ident: ident, def_id: def_id, span: item.span, - })); let def = Def::Macro(def_id, MacroKind::Bang); + let vis = ty::Visibility::Invisible; // Doesn't matter for legacy bindings + let binding = (def, vis, item.span, expansion).to_name_binding(self.arenas); + let legacy_binding = self.arenas.alloc_legacy_binding(LegacyBinding { + parent_legacy_scope: *current_legacy_scope, binding, ident + }); + *current_legacy_scope = LegacyScope::Binding(legacy_binding); self.all_macros.insert(ident.name, def); if attr::contains_name(&item.attrs, "macro_export") { let module = self.graph_root; diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index a72ee7ae379..f8767fa553f 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -242,22 +242,19 @@ impl<'a, 'crateloader> Resolver<'a, 'crateloader> { if record_used { if let Some(binding) = resolution.binding { if let Some(shadowed_glob) = resolution.shadowed_glob { - let name = ident.name; // Forbid expanded shadowing to avoid time travel. if restricted_shadowing && binding.expansion != Mark::root() && ns != MacroNS && // In MacroNS, `try_define` always forbids this shadowing binding.def() != shadowed_glob.def() { self.ambiguity_errors.push(AmbiguityError { - span: path_span, - name, - lexical: false, + ident, b1: binding, b2: shadowed_glob, }); } } - if self.record_use(ident, ns, binding, path_span) { + if self.record_use(ident, ns, binding) { return Ok(self.dummy_binding); } if !self.is_accessible(binding.vis) { @@ -937,7 +934,7 @@ impl<'a, 'b:'a, 'c: 'b> ImportResolver<'a, 'b, 'c> { self.per_ns(|this, ns| if !type_ns_only || ns == TypeNS { if let Ok(binding) = result[ns].get() { all_ns_err = false; - if this.record_use(ident, ns, binding, directive.span) { + if this.record_use(ident, ns, binding) { if let ModuleOrUniformRoot::Module(module) = module { this.resolution(module, ident, ns).borrow_mut().binding = Some(this.dummy_binding); |
