-
Notifications
You must be signed in to change notification settings - Fork 12.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
resolve: Introduce two sub-namespaces in macro namespace #54069
Conversation
(rust_highfive has picked a reviewer for you, use r? to override) |
r? @alexcrichton |
Some alternatives:
|
Is this because derives are typically |
So, 2 questions:
I would expect that to be the reason. |
Most likely.
Yes, that's orthogonal and still worth doing because other issues (#53481) arise due to derive helper attributes being very low-priority right now, but this PR fixes #53583 as well, just in another way.
In this code the macro call looks unambiguously "bang" syntactically, and the choice of namespace during resolution is driven by syntax, so this case shouldn't cause any problems. my_attr! {
fn f() {}
} In theory, such call could can pass the input tokens to the attribute macro in the same way as |
cc @dtolnay |
I agree.
So I thought about this a bit and came up with the following example of where it would be useful: The proptest crate currently has a proptest! {
#[test]
fn my_sorting_algo_works(xs: Vec<u8>) {
...
}
} Currently, this is just a #[proptest]
fn my_sorting_algo_works(xs: Vec<u8>) {
...
} However, the I suppose that you could do this by putting the attribute on a module (particularly if it has no name, e.g. |
☔ The latest upstream changes (presumably #53778) made this pull request unmergeable. Please resolve the merge conflicts. |
This seems like a solid solution to me and a solid implementation, would someone from @rust-lang/lang like to fcp to merge? |
@rfcbot poll lang Please review this PR for merging prior to cutting beta |
Team member @aturon has asked teams: T-lang, for consensus on:
|
On the topic of using In particular, I imagine that -- had decorators existed -- this is how |
cc @djrenren - does this seem fine for CTFs? |
This looks great! The single macro namespace has been confusing and getting this landed with the stabilization of procmacro attributes seems like the right choice. |
Probably yes; proptest! {
#![proptest_config(ProptestConfig {
fork: true,
timeout: 1000,
.. ProptestConfig::default()
})]
fn first_test(n: u64) { ... }
fn second_test(n: u64) { ... }
} This reuses the same configuration for both tests; if you instead put With const CONFIG = ProptestConfig { fork: true, timeout: 1000, ..ProptestConfig::default() };
#[proptest(CONFIG)]
mod tests {
// Ideally we would be able to remove `tests` from here and just write `mod { .. }` instead.
fn first_test(n: u64) { ... }
fn second_test(n: u64) { ... }
} which seems just as, if not more ergonomic. All in all; I think this alternative is good enough to give up the ability to use |
I've updated #53913 with a mini-version of this PR that affects only the built-in attribute check and nothing else, so it is now unblocked. |
@petrochenkov it is a known issue, but file this at https://github.com/anp/rfcbot-rs please. :) |
I've checked my box above, but with the reservation that we need to remember to re-resolve this discussion when it comes time to stabilize |
I think this door is still open. #[this_is_attr]
macro mmm() { ... }
#[mmm] // OK
struct S; |
@petrochenkov The question, I suppose, is whether you'd ever want a macro that worked in both positions. |
@cramertj |
Wait, actually no. #[this_is_both_bang_and_attr]
macro mac() { ... }
#[mac] // OK
struct S;
mac!(); // OK In fact proc macro stubs behave exactly like this right now, because I was lazy and didn't refactor |
I'm a little concerned about doing this halfway, such that shadowing gets ignored but you can't actually have different macros of both types with the same name in the same module. Or have I misunderstood? |
Yes, that's correct.
I'd say it's half-bad vs bad in this case, since the primary motivation is compatibility and there's not much need in two new separate namespaces in isolation. |
As long as functional proc macros ( (Checked my box.) |
Discussed briefly in the @rust-lang/lang meeting: General consensus was that we ought to split the namespace for "attribute macros" (aka decorators) like However, if this is forwards compatible with that universe -- and we believe it is -- then we should land this now and we can revisit the rest of it later. |
@nikomatsakis @Centril |
@petrochenkov Yep! @bors r+ |
📌 Commit beb3b5d has been approved by |
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
☀️ Test successful - status-appveyor, status-travis |
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
inimpl 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 bymacro_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 macrothread_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
, evendeprecated
), 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