Repository navigation
Built-in macros, derives and attributes have inconsistent interactions with macros of the same name. #52269
Description
Activity
I'm going to implement roughly the same scheme as described here, because it's currently used for non-macro namespaces (I described it previously in the tool attributes RFC - rust-lang/rfcs#2103 (comment)).
- User-defined names are, well, user-defined and modularized.
- Macros defined in the standard library (including perhaps procedural ones in the future, moved to the library from the compiler!) are introduced through standard library prelude, with lower priority than user-defined names during in-scope resolution (
#[macro_use] extern crate ...and preludes are the same mechanism). - Builtin macros (including derives) and attributes are introduced through language prelude, with even lower priority, in the same way as primitive types like
u8orbool.
If something currently diverges from this scheme, e.g. builtin something shadows user-defined something, then it's a bug and it should be reported (preferably in a separate focused issue with reproduction).
The only tricky case here is attributes that need to be inspected before name resolution, like
#[feature(...)]or, as you've mentioned,#[cfg(...)].
Perhaps a few attributes will need to be reserved and not resolved, or perhaps they'll be interpreted as tokens first, but then resolved normally, I think it'll become cleared during implementation.Reacted by Alexis Hunt and Aaron Turon- addedA-decl-macros-1-2Area: Declarative macros 1.2Area: Declarative macros 1.2
on Jul 17, 2018 Currently attributes exist in another namespace so if we implement overriding of attributes it'll be a breaking change.
Right now you can have both a
macro_rules! testand a#[test]attribute. Allowing overriding (which seems like the right call to me) would break any code that has both because they'd be in the same namespace. We even do this in the compiler: https://github.com/rust-lang/rust/blob/master/src/libsyntax_pos/analyze_filemap.rsMy plan was to implement the principled scheme first, then run it through crater and look how much code breaks on stable, what cases of breakage are common, and then make adjustments.
Reacted by djrenrenSounds like a good plan. I've run into this issue while working on custom test frameworks so mind if I take a crack at it?
@djrenren
Yes, it would be good if more people got familiar with this code.
However, I'm currently working on #52512 and #44690 which are closely related to this issue and are prerequisites in some sense, and hope to start working on this issue this weekend.There's one piece of work that's relatively independent though.
There's a number of locations that accept attributes, but cannot be inputs to macros, for example generic parametersfn f<#[my_attr] T>() {}(the full list can be found in #52367 (comment) or inast.rs).
We need to go through all these attributes, resolve them and report an error if resolution does not refer to an "inert" attribute (builtin, custom or tool attribute).Sweet I'll take a look at it.
Bumping to RC2 milestone.
@petrochenkov to check on on this issue, I think #53913 is evaluating the impact of one strategy for solving this, right? Otherwise I believe the built-in attributes cannot currently be shadowed, even if a macro of such a name is imported? (in that it looks like nightly accepts this:)
#![feature(decl_macro)] macro cfg() { } #[cfg(all())] fn main() {}
@alexcrichton
Yes, that's right.- added a commit that references this issue
on Sep 11, 2018 Status: future proofed in #53913.
There's still a single macro namespace, but it has two sub-namespaces now to avoid backward-incompatible conflicts. #54069 has more detailed description of how the sub-namespace scheme works.
When used, the builtin takes precedence and the macro is ignored.
After #53913 if potentially built-in attribute is ambiguous with any other macro name in scope (taking sub-namespacing into account), then error is reported.
Some very special names (cfg,cfg_attrandderive) are completely reserved from being defined in macro namespace (includinguse foo::bar as cfg;).It's possible to relax this restriction to achieve the end goal - user-defined attribute macros shadowing built-in attributes (or rather newly introduced built-in attributes not breaking user-defined macros), but it's not entirely trivial, see #53913 (comment) for more detailed explanation.
Defining a macro with proc_macro_derive that conflicts with the built-in gives an error at the definition.
This artificial error is still in place, but it can be easily removed now since it doesn't serve its purpose of preventing user-defined derives from shadowing built-in derives.
Future plans:
- Possibly relax the ambiguity errors and reservations for potentially built-in attributes.
- Remove the "cannot override a built-in #[derive] mode" error in
proc_macro_registrar.rs.
I think we can close this generic issue now and perhaps create new more focused issues for "future plans" (if those even need tracking).
- added a commit that references this issue
on Jul 26, 2019 - added a commit that references this issue
on Jul 26, 2019
Macros currently occupy a single namespace, regardless of whether they are used for attributes, derives, or function-like macros. This was a design decision for procedural macros, on the basis that it is easier to split them into separate namespaces later, but it leads to some confusing interactions with built-in attributes (like
cfgorderive), macros (likecompile_errororenv) or derives (likeEqorClone). The behaviour should be tightened up so that it is intuitive and straightforward to explain.Currently, the rules appear to be as follows:
derivecan be called, or an attribute namedcompile_errorcan be used). Raw identifiers can be used to get around the built-in.proc_macro_derive. If a macro is defined with the same name as a built-in derive, it takes precedence. Defining a macro withproc_macro_derivethat conflicts with the built-in gives an error at the definition.If
use_extern_macrois enabled, then you can freely rename imports. This lets you get around theproc_macro_deriverule:Note that this behaviour applies only to built-in macros. Macros defined in libstd or libcore (for
no_std) are imported in the prelude and can be overridden as one would expect for prelude names.I do not think we can get away with treating all builtins as if they are normal names defined in the prelude that can be overridden. While it might be possible to treat some of them that way, the
cfgattribute is an excellent example of one that cannot be safely modified, or else the following program would be problematic, because macro imports apply throughout the entire source file, not just after the import:At the same time, I don't see why built-in derives should be special. The traits they implement are not, after all; even the ones like
Copywhich are magical. And it is possible, though inadvisable, to shadow them independently:trait Copy {}won't prevent#[derive(Copy)]from working, for instance.This implies to me that we should have the following in the macro namespace:
This would imply the following behaviour changes:
proc_macro_derivecan declare derives with the same name as an intrinsic derive; these will simply shadow the intrinsic.This leaves an unresolved question around renaming imports (e.g.
use foo::bar as cfg;wherefoo::baris names both a function and macro; is this an error? warning? or it just silently hides the macro asr#cfg?) but would clean up the bulk of the issues with the situation.