Skip to content

Commit f789b6b

Browse files
committed
Auto merge of #54069 - petrochenkov:subns, r=aturon
resolve: Introduce two sub-namespaces in macro namespace Two sub-namespaces are introduced in the macro namespace - one for bang macros and one for attribute-like macros (attributes, derives). "Sub-namespace" means this is not a newly introduced full namespace, the single macro namespace is still in place. I.e. you still can't define/import two macros with the same name in a single module, `use` imports still import only one name in macro namespace (from any sub-namespace) and not possibly two. However, when we are searching for a name used in a `!` macro call context (`my_macro!()`) we skip attribute names in scope, and when we are searching for a name used in attribute context (`#[my_macro]`/`#[derive(my_macro)]`) we are skipping bang macro names in scope. In other words, bang macros cannot shadow attribute macros and vice versa. For a non-macro analogy, we could e.g. skip non-traits when searching for `MyTrait` in `impl MyTrait for Type { ... }`. However we do not do it in non-macro namespaces because we don't have practical issues with e.g. non-traits shadowing traits with the same name, but with macros we do, especially after macro modularization. For `#[test]` and `#[bench]` we have a hack in the compiler right now preventing their shadowing by `macro_rules! test` and similar things. This hack was introduced after making `#[test]`/`#[bench]` built-in macros instead of built-in attributes (#53410), something that needed to be done from the start since they are "active" attributes transforming their inputs. Now they are passed through normal name resolution and can be shadowed, but that's a breaking change, so we have a special hack basically applying this PR for `#[test]` and `#[bench]` only. Soon all potentially built-in attributes will be passed through normal name resolution (#53913) and that uncovers even more cases where the strict "macro namespace is a single namespace" rule needs to be broken. For example, with strict rules, built-in macro `cfg!(...)` would shadow built-in attribute `#[cfg]` (they are different things), standard library macro `thread_local!(...)` would shadow built-in attribute `#[thread_local]` - both of these cases are covered by special hacks in #53913 as well. Crater run uncovered more cases of attributes being shadowed by user-defined macros (`warn`, `doc`, `main`, even `deprecated`), we cannot add exceptions in the compiler for all of them. Regressions with user-defined attributes like #53583 and #53898 also appeared after enabling macro modularization. People are also usually confused (#53205 (comment), #53583 (comment)) when they see conflicts between attributes and non-attribute macros for the first time. So my proposed solution is to solve this issue by introducing two sub-namespaces and thus skipping resolutions of the wrong kind and preventing more error-causing cases of shadowing. Fixes #53583
2 parents 2ab3eba + beb3b5d commit f789b6b

10 files changed

+64
-35
lines changed

src/librustc_resolve/macros.rs

+18-11
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,21 @@ pub struct ProcMacError {
118118
warn_msg: &'static str,
119119
}
120120

121-
// For compatibility bang macros are skipped when resolving potentially built-in attributes.
122-
fn macro_kind_mismatch(name: Name, requirement: Option<MacroKind>, candidate: Option<MacroKind>)
123-
-> bool {
124-
requirement == Some(MacroKind::Attr) && candidate == Some(MacroKind::Bang) &&
125-
(name == "test" || name == "bench" || is_builtin_attr_name(name))
121+
// Macro namespace is separated into two sub-namespaces, one for bang macros and
122+
// one for attribute-like macros (attributes, derives).
123+
// We ignore resolutions from one sub-namespace when searching names in scope for another.
124+
fn sub_namespace_mismatch(requirement: Option<MacroKind>, candidate: Option<MacroKind>) -> bool {
125+
#[derive(PartialEq)]
126+
enum SubNS { Bang, AttrLike }
127+
let sub_ns = |kind| match kind {
128+
MacroKind::Bang => Some(SubNS::Bang),
129+
MacroKind::Attr | MacroKind::Derive => Some(SubNS::AttrLike),
130+
MacroKind::ProcMacroStub => None,
131+
};
132+
let requirement = requirement.and_then(|kind| sub_ns(kind));
133+
let candidate = candidate.and_then(|kind| sub_ns(kind));
134+
// "No specific sub-namespace" means "matches anything" for both requirements and candidates.
135+
candidate.is_some() && requirement.is_some() && candidate != requirement
126136
}
127137

128138
impl<'a, 'crateloader: 'a> base::Resolver for Resolver<'a, 'crateloader> {
@@ -641,10 +651,7 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
641651
}
642652
}
643653
WhereToResolve::BuiltinAttrs => {
644-
// FIXME: Only built-in attributes are not considered as candidates for
645-
// non-attributes to fight off regressions on stable channel (#53205).
646-
// We need to come up with some more principled approach instead.
647-
if kind == Some(MacroKind::Attr) && is_builtin_attr_name(ident.name) {
654+
if is_builtin_attr_name(ident.name) {
648655
let binding = (Def::NonMacroAttr(NonMacroAttrKind::Builtin),
649656
ty::Visibility::Public, ident.span, Mark::root())
650657
.to_name_binding(self.arenas);
@@ -765,7 +772,7 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
765772

766773
match result {
767774
Ok(result) => {
768-
if macro_kind_mismatch(ident.name, kind, result.0.macro_kind()) {
775+
if sub_namespace_mismatch(kind, result.0.macro_kind()) {
769776
continue_search!();
770777
}
771778

@@ -829,7 +836,7 @@ impl<'a, 'cl> Resolver<'a, 'cl> {
829836
parent_scope: &ParentScope<'a>,
830837
record_used: bool,
831838
) -> Option<&'a NameBinding<'a>> {
832-
if macro_kind_mismatch(ident.name, kind, Some(MacroKind::Bang)) {
839+
if sub_namespace_mismatch(kind, Some(MacroKind::Bang)) {
833840
return None;
834841
}
835842

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#[macro_export]
2+
macro_rules! my_attr { () => () }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// no-prefer-dynamic
2+
3+
#![crate_type = "proc-macro"]
4+
5+
extern crate proc_macro;
6+
use proc_macro::*;
7+
8+
#[proc_macro_derive(MyTrait, attributes(my_attr))]
9+
pub fn foo(_: TokenStream) -> TokenStream {
10+
TokenStream::new()
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// compile-pass
2+
// aux-build:derive-helper-shadowed.rs
3+
// aux-build:derive-helper-shadowed-2.rs
4+
5+
#[macro_use]
6+
extern crate derive_helper_shadowed;
7+
#[macro_use(my_attr)]
8+
extern crate derive_helper_shadowed_2;
9+
10+
macro_rules! my_attr { () => () }
11+
12+
#[derive(MyTrait)]
13+
#[my_attr] // OK
14+
struct S;
15+
16+
fn main() {}

src/test/ui/issues/issue-11692-2.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,5 @@
99
// except according to those terms.
1010

1111
fn main() {
12-
concat!(test!()); //~ ERROR `test` can only be used in attributes
12+
concat!(test!()); //~ ERROR cannot find macro `test!` in this scope
1313
}

src/test/ui/issues/issue-11692-2.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
error: `test` can only be used in attributes
1+
error: cannot find macro `test!` in this scope
22
--> $DIR/issue-11692-2.rs:12:13
33
|
4-
LL | concat!(test!()); //~ ERROR `test` can only be used in attributes
4+
LL | concat!(test!()); //~ ERROR cannot find macro `test!` in this scope
55
| ^^^^
66

77
error: aborting due to previous error
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,3 @@
1-
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2-
// file at the top-level directory of this distribution and at
3-
// http://rust-lang.org/COPYRIGHT.
4-
//
5-
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6-
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7-
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8-
// option. This file may not be copied, modified, or distributed
9-
// except according to those terms.
10-
11-
#[derive(inline)] //~ ERROR cannot find derive macro `inline` in this scope
12-
struct S;
13-
141
fn main() {
152
inline!(); //~ ERROR cannot find macro `inline!` in this scope
163
}
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,8 @@
1-
error: cannot find derive macro `inline` in this scope
2-
--> $DIR/macro-path-prelude-fail-3.rs:11:10
3-
|
4-
LL | #[derive(inline)] //~ ERROR cannot find derive macro `inline` in this scope
5-
| ^^^^^^
6-
71
error: cannot find macro `inline!` in this scope
8-
--> $DIR/macro-path-prelude-fail-3.rs:15:5
2+
--> $DIR/macro-path-prelude-fail-3.rs:2:5
93
|
104
LL | inline!(); //~ ERROR cannot find macro `inline!` in this scope
115
| ^^^^^^ help: you could try the macro: `line`
126

13-
error: aborting due to 2 previous errors
7+
error: aborting due to previous error
148

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#[derive(inline)] //~ ERROR expected a macro, found built-in attribute
2+
struct S;
3+
4+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: expected a macro, found built-in attribute
2+
--> $DIR/macro-path-prelude-fail-4.rs:1:10
3+
|
4+
LL | #[derive(inline)] //~ ERROR expected a macro, found built-in attribute
5+
| ^^^^^^
6+
7+
error: aborting due to previous error
8+

0 commit comments

Comments
 (0)