Skip to content

Commit 5597ee8

Browse files
committed
Auto merge of #54605 - petrochenkov:mambig, r=alexcrichton
resolve: Disambiguate a subset of conflicts "macro_rules" vs "macro name in module" Currently if macro name may refer to both a `macro_rules` macro definition and a macro defined/imported into module we conservatively report an ambiguity error. Unfortunately, these errors became a source of regressions when macro modularization was enabled - see issue #54472. This PR disambiguates such conflicts in favor of `macro_rules` if both the `macro_rules` item and in-module macro name are defined in the same normal (named) module and `macro_rules` is closer in scope to the point of use (see the tests for examples). This is a subset of more general approach described in #54472 (comment). The subset is enough to fix all the regressions from #54472, but it can be extended to apply to all "macro_rules" vs "macro name in module" conflicts in the future. To give an analogy, this is equivalent to scoping rules for `let` variables and items defined in blocks (`macro_rules` behaves like "`let` at module level" in general). ```rust { // beginning of the block use xxx::m; // (1) // Starting from the beginning of the block and until here m!() refers to (1) macro_rules! m { ... } // (2) // Starting from here and until the end of the block m!() refers to (2) } // end of the block ``` More complex examples with `use` and `macro_rules` from different modules still report ambiguity errors, even if equivalent examples with `let` are legal. Fixes #54472 (stable-to-beta regression)
2 parents 6ddab3e + 078fc52 commit 5597ee8

File tree

7 files changed

+130
-23
lines changed

7 files changed

+130
-23
lines changed

src/librustc_resolve/lib.rs

+40-3
Original file line numberDiff line numberDiff line change
@@ -72,11 +72,10 @@ use syntax_pos::{Span, DUMMY_SP, MultiSpan};
7272
use errors::{Applicability, DiagnosticBuilder, DiagnosticId};
7373

7474
use std::cell::{Cell, RefCell};
75-
use std::cmp;
75+
use std::{cmp, fmt, iter, ptr};
7676
use std::collections::BTreeSet;
77-
use std::fmt;
78-
use std::iter;
7977
use std::mem::replace;
78+
use rustc_data_structures::ptr_key::PtrKey;
8079
use rustc_data_structures::sync::Lrc;
8180

8281
use resolve_imports::{ImportDirective, ImportDirectiveSubclass, NameResolution, ImportResolver};
@@ -1114,6 +1113,17 @@ impl<'a> ModuleData<'a> {
11141113
fn nearest_item_scope(&'a self) -> Module<'a> {
11151114
if self.is_trait() { self.parent.unwrap() } else { self }
11161115
}
1116+
1117+
fn is_ancestor_of(&self, mut other: &Self) -> bool {
1118+
while !ptr::eq(self, other) {
1119+
if let Some(parent) = other.parent {
1120+
other = parent;
1121+
} else {
1122+
return false;
1123+
}
1124+
}
1125+
true
1126+
}
11171127
}
11181128

11191129
impl<'a> fmt::Debug for ModuleData<'a> {
@@ -1411,6 +1421,7 @@ pub struct Resolver<'a, 'b: 'a> {
14111421
block_map: NodeMap<Module<'a>>,
14121422
module_map: FxHashMap<DefId, Module<'a>>,
14131423
extern_module_map: FxHashMap<(DefId, bool /* MacrosOnly? */), Module<'a>>,
1424+
binding_parent_modules: FxHashMap<PtrKey<'a, NameBinding<'a>>, Module<'a>>,
14141425

14151426
pub make_glob_map: bool,
14161427
/// Maps imports to the names of items actually imported (this actually maps
@@ -1714,6 +1725,7 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> {
17141725
module_map,
17151726
block_map: NodeMap(),
17161727
extern_module_map: FxHashMap(),
1728+
binding_parent_modules: FxHashMap(),
17171729

17181730
make_glob_map: make_glob_map == MakeGlobMap::Yes,
17191731
glob_map: NodeMap(),
@@ -4596,6 +4608,31 @@ impl<'a, 'crateloader: 'a> Resolver<'a, 'crateloader> {
45964608
vis.is_accessible_from(module.normal_ancestor_id, self)
45974609
}
45984610

4611+
fn set_binding_parent_module(&mut self, binding: &'a NameBinding<'a>, module: Module<'a>) {
4612+
if let Some(old_module) = self.binding_parent_modules.insert(PtrKey(binding), module) {
4613+
if !ptr::eq(module, old_module) {
4614+
span_bug!(binding.span, "parent module is reset for binding");
4615+
}
4616+
}
4617+
}
4618+
4619+
fn disambiguate_legacy_vs_modern(
4620+
&self,
4621+
legacy: &'a NameBinding<'a>,
4622+
modern: &'a NameBinding<'a>,
4623+
) -> bool {
4624+
// Some non-controversial subset of ambiguities "modern macro name" vs "macro_rules"
4625+
// is disambiguated to mitigate regressions from macro modularization.
4626+
// Scoping for `macro_rules` behaves like scoping for `let` at module level, in general.
4627+
match (self.binding_parent_modules.get(&PtrKey(legacy)),
4628+
self.binding_parent_modules.get(&PtrKey(modern))) {
4629+
(Some(legacy), Some(modern)) =>
4630+
legacy.normal_ancestor_id == modern.normal_ancestor_id &&
4631+
modern.is_ancestor_of(legacy),
4632+
_ => false,
4633+
}
4634+
}
4635+
45994636
fn report_ambiguity_error(&self, ident: Ident, b1: &NameBinding, b2: &NameBinding) {
46004637
let participle = |is_import: bool| if is_import { "imported" } else { "defined" };
46014638
let msg1 =

src/librustc_resolve/macros.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -957,11 +957,13 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
957957
},
958958
(Some(legacy_binding), Ok((binding, FromPrelude(from_prelude))))
959959
if legacy_binding.def() != binding.def_ignoring_ambiguity() &&
960-
(!from_prelude ||
960+
(!from_prelude &&
961+
!self.disambiguate_legacy_vs_modern(legacy_binding, binding) ||
961962
legacy_binding.may_appear_after(parent_scope.expansion, binding)) => {
962963
self.report_ambiguity_error(ident, legacy_binding, binding);
963964
},
964965
// OK, non-macro-expanded legacy wins over prelude even if defs are different
966+
// Also, non-macro-expanded legacy wins over modern from the same module
965967
// Also, legacy and modern can co-exist if their defs are same
966968
(Some(legacy_binding), Ok(_)) |
967969
// OK, unambiguous resolution
@@ -1097,6 +1099,7 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
10971099
let def = Def::Macro(def_id, MacroKind::Bang);
10981100
let vis = ty::Visibility::Invisible; // Doesn't matter for legacy bindings
10991101
let binding = (def, vis, item.span, expansion).to_name_binding(self.arenas);
1102+
self.set_binding_parent_module(binding, self.current_module);
11001103
let legacy_binding = self.arenas.alloc_legacy_binding(LegacyBinding {
11011104
parent_legacy_scope: *current_legacy_scope, binding, ident
11021105
});

src/librustc_resolve/resolve_imports.rs

+1
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,7 @@ impl<'a, 'crateloader> Resolver<'a, 'crateloader> {
478478
binding: &'a NameBinding<'a>)
479479
-> Result<(), &'a NameBinding<'a>> {
480480
self.check_reserved_macro_name(ident, ns);
481+
self.set_binding_parent_module(binding, module);
481482
self.update_resolution(module, ident, ns, |this, resolution| {
482483
if let Some(old_binding) = resolution.binding {
483484
if binding.is_glob_import() {

src/test/ui/imports/macros.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ mod m3 {
4545
mod m4 {
4646
macro_rules! m { () => {} }
4747
use two_macros::m;
48-
m!(); //~ ERROR ambiguous
48+
m!();
4949
}
5050

5151
fn main() {}

src/test/ui/imports/macros.stderr

+1-18
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,3 @@
1-
error[E0659]: `m` is ambiguous
2-
--> $DIR/macros.rs:48:5
3-
|
4-
LL | m!(); //~ ERROR ambiguous
5-
| ^ ambiguous name
6-
|
7-
note: `m` could refer to the name defined here
8-
--> $DIR/macros.rs:46:5
9-
|
10-
LL | macro_rules! m { () => {} }
11-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
12-
note: `m` could also refer to the name imported here
13-
--> $DIR/macros.rs:47:9
14-
|
15-
LL | use two_macros::m;
16-
| ^^^^^^^^^^^^^
17-
181
error[E0659]: `m` is ambiguous
192
--> $DIR/macros.rs:26:5
203
|
@@ -51,6 +34,6 @@ LL | use two_macros::m;
5134
| ^^^^^^^^^^^^^
5235
= note: macro-expanded macro imports do not shadow
5336

54-
error: aborting due to 3 previous errors
37+
error: aborting due to 2 previous errors
5538

5639
For more information about this error, try `rustc --explain E0659`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Some non-controversial subset of ambiguities "modern macro name" vs "macro_rules"
2+
// is disambiguated to mitigate regressions from macro modularization.
3+
// Scoping for `macro_rules` behaves like scoping for `let` at module level, in general.
4+
5+
#![feature(decl_macro)]
6+
7+
fn same_unnamed_mod() {
8+
macro m() { 0 }
9+
10+
macro_rules! m { () => (()) }
11+
12+
m!() // OK
13+
}
14+
15+
fn nested_unnamed_mod() {
16+
macro m() { 0 }
17+
18+
{
19+
macro_rules! m { () => (()) }
20+
21+
m!() // OK
22+
}
23+
}
24+
25+
fn nested_unnamed_mod_fail() {
26+
macro_rules! m { () => (()) }
27+
28+
{
29+
macro m() { 0 }
30+
31+
m!() //~ ERROR `m` is ambiguous
32+
}
33+
}
34+
35+
fn nexted_named_mod_fail() {
36+
macro m() { 0 }
37+
38+
#[macro_use]
39+
mod inner {
40+
macro_rules! m { () => (()) }
41+
}
42+
43+
m!() //~ ERROR `m` is ambiguous
44+
}
45+
46+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
error[E0659]: `m` is ambiguous
2+
--> $DIR/ambiguity-legacy-vs-modern.rs:31:9
3+
|
4+
LL | m!() //~ ERROR `m` is ambiguous
5+
| ^ ambiguous name
6+
|
7+
note: `m` could refer to the name defined here
8+
--> $DIR/ambiguity-legacy-vs-modern.rs:26:5
9+
|
10+
LL | macro_rules! m { () => (()) }
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
note: `m` could also refer to the name defined here
13+
--> $DIR/ambiguity-legacy-vs-modern.rs:29:9
14+
|
15+
LL | macro m() { 0 }
16+
| ^^^^^^^^^^^^^^^
17+
18+
error[E0659]: `m` is ambiguous
19+
--> $DIR/ambiguity-legacy-vs-modern.rs:43:5
20+
|
21+
LL | m!() //~ ERROR `m` is ambiguous
22+
| ^ ambiguous name
23+
|
24+
note: `m` could refer to the name defined here
25+
--> $DIR/ambiguity-legacy-vs-modern.rs:40:9
26+
|
27+
LL | macro_rules! m { () => (()) }
28+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
29+
note: `m` could also refer to the name defined here
30+
--> $DIR/ambiguity-legacy-vs-modern.rs:36:5
31+
|
32+
LL | macro m() { 0 }
33+
| ^^^^^^^^^^^^^^^
34+
35+
error: aborting due to 2 previous errors
36+
37+
For more information about this error, try `rustc --explain E0659`.

0 commit comments

Comments
 (0)