Description
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 cfg
or derive
), macros (like compile_error
or env
) or derives (like Eq
or Clone
). The behaviour should be tightened up so that it is intuitive and straightforward to explain.
Currently, the rules appear to be as follows:
- Macros can be defined that have the same name as a built-in attribute or macro. When used, the builtin takes precedence and the macro is ignored. If the macro is used in a different context, it is found and works normally (e.g. a function-like macro named
derive
can be called, or an attribute namedcompile_error
can be used). Raw identifiers can be used to get around the built-in. - Macros can be defined that have the same name as a built-in derive, except with
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_derive
that conflicts with the built-in gives an error at the definition.
If use_extern_macro
is enabled, then you can freely rename imports. This lets you get around the proc_macro_derive
rule:
#![feature(use_extern_macro)]
use failure::Fail as Clone;
use std::fmt::{Display, Formatter};
#[derive(Debug, Clone)]
struct Foo {
}
impl Display for Foo {
fn fmt(&self, fmt : &mut Formatter) -> Result<(), std::fmt::Error> {
write!(fmt, "{:?}", self)
}
}
fn main() {
let _ = Foo{}.cause();
}
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 cfg
attribute 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:
#[cfg(target_os = "linux")]
use some_crate::some_macro as cfg;
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 Copy
which 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:
- Built-ins which are truly and deeply magical, and cannot safely be overridden. Their names are treated like keywords in the macro namespace, in that the same non-raw identifier cannot be used at all.
- Intrinsics which are implemented by the compiler, but defined and named normally, like existing lang items and intrinsic functions.
This would imply the following behaviour changes:
proc_macro_derive
can declare derives with the same name as an intrinsic derive; these will simply shadow the intrinsic.- Built-in function-like macros and attributes are always referred to when their names are used in the macro namespace, even in the wrong context, unless a raw identifier is used.
- Macros of any kind cannot be declared with the same name as a built-in macro or attribute unless a raw identifier is used.
- Going forward, we could possibly look at converting some built-in attributes and macros into intrinsics so that their names are freed up.
This leaves an unresolved question around renaming imports (e.g. use foo::bar as cfg;
where foo::bar
is names both a function and macro; is this an error? warning? or it just silently hides the macro as r#cfg
?) but would clean up the bulk of the issues with the situation.