-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Macros 2.0: Span::def_site() vs Span::call_site() #45934
Comments
Without knowing all the implications of hygiene, it sounds like we could use two macros:
The fist/unhygienic one is for "I want to paste this manufactured code into the user's namespace". Gnome-class does a lot of that for the GObject boilerplate. It creates a couple of modules too keep things contained and exports things from them. It creates user-visible structs with manufactured identifiers for names: e.g. the user types "class foo" and we make up a "FooClass" struct type. The second/hygienic one is for "this is an implementation detail of my macro, wherein I don't want names to clash with the user's". For example, internally gnome-class creates a
Imagine that Probably quote_hygienically! and quote_unhygienically!() are not the nicest names. I'm also not sure how to explain these concepts to implementors of procedural macros. Also, whether their implementations actually use |
@alexcrichton Thanks for the feedback!
Making it
For backwards compatibility with macros 1.1, the tokens from #![feature(decl_macro)]
macro m($i: ident) { // The token that substitutes `$i` resolves at the macro invocation
mod foo {
use $i::*;
}
}
m!(super); // When resolved here, "super" doesn't make sense since the crate root has no parent. In other words, since the module If you give the
This is actually "just" another manifestation of this: fn f() {
mod foo {
pub fn g();
}
use ??::foo::g; // How to import `g`?
} In your example*, *reproduced for convenience: struct A;
mod foo { use super::A; } I have seen pub fn g() {} // (1)
macro m() {
pub fn g() {} // (2)
mod bar {
pub fn g() {} // (3)
use self::g as g3; // Resolves to (3) today
use super::g as g1; // Resolves to (1) today
use macro::g as g2; // Would resolve to (2)
}
}
// ...
m!(); //^ The above statements are true wherever `m!()` is invoked. An alternative would be to allow
If a struct has a span at the macro definition, it will only be relevant to things that resolve at the macro definition. For example, macro m() {
pub struct A; // (1)
A; // resolves to (1)
}
fn f() {
m!();
struct A; // (2)
A; // resolves to (2)
} With declarative macros, a solution is to add an argument: macro m($i:ident) {
pub struct $i; // (1)
$i; // resolves to (1)
A; //~ ERROR A is not in scope
}
fn f() {
m!(A);
A; // resolves to (1)
} With procedural macros, a solution is to use a span at the macro invocation.
Since the only phase 1 items in a proc-macro crate (i.e. the only items usable when compiling for the target architecture) are proc macros, there aren't any You can use external crates, but you have to declare them in each macro expansion. If you want to // Say I want to use `extern crate foo` in this function without declaring it anywhere outside.
fn f() {
extern crate foo; // I could declare it here, but I can't `use` it (yet...)
mod foo {
extern crate foo; // If I want `use`, I need to declare it in a module.
}
} While The solution is to support adding "phase 1" (target architecture) items to proc macro crates. Then, #[phase_1] // With `extern crate` disappearing, we may never introduce this...
extern crate foo; // (1) This declares a target dependency `foo`
#[proc_macro]
fn m(input: TokenStream) -> TokenStream {
use foo; // This doesn't resolve
quote! { // n.b. `quote` creates tokens with `Span::def_site()`.
use foo; // This resolves to (1)
}
} In the near-near (day or two) term, we could add an implicit
Yeah, this is a limitation. That being said, ideally people would migrate declarative and procedural macros at the same time where possible to minimize macros 1.0/2.0 interaction. While I tried to make the interaction as nice as possible, backwards compatability and various "sanity-check" coherence conditions bound how nice it can be.
Generally speaking (and summarizing @federicomenaquintero), if a name is part of the "public API" of the macro (i.e. the macro defines the name for the invoker to use, the macro requires the name to be in scope at the invocation, etc.), the span should be at the macro invocation. Otherwise, the span should be at the macro definition.
Ideally Today, IIRC @SergioBenitez has proposed |
Thanks, @jseyfried, this is very enlightening. I like the idea of Maybe the following is more of a gnome-class issue than something directly for this issue, but anyway: In gnome-class we are given a name from the user, say
Then we use the generated However, Right now we depend on @alexcrichton's hacked If we had |
Perhaps, I think it could still be nice to have macro m($($input:tt)*) {
tokens ... $($input)* ...
}
// is equivalent to
#[proc_macro]
fn m(input: TokenStream) -> TokenStream {
quote! { tokens ... $input ... }
}
Good question, I forgot to mention this -- the span in This means we (I) should prioritize adding |
Thanks so much for the explanation @jseyfried! I definitely agree that |
@alexcrichton No prob! Yes, I agree that |
According to @jseyfried's epic comment it sounds like everything here is working as intended. As a result I'm going to classify this as a diagnostics bug as I think we can probably do better in terms of what diagnostics are printed by the compiler in these situations. In the meantime I've opened dtolnay/quote#55 for the |
Fixing this was more straightforward than I thought; implemented in #46551. @alexcrichton after #46551 lands, the example in your original comment will work. Generally speaking, if you invoke an unhygienic macro at a hygienic macro's def site (e.g. |
@jseyfried awesome! |
Triage: Closing as fixed because
|
Up until recently I've considered these two function calls in
proc_macro
,Span::default()
andSpan::call_site()
relatively different. I'm not realizing, however, that they're actually quite significantly different depending on what you're doing in a procedural macro!In working with the gnome-class macro we've ended up getting a good deal more experience with the procedural macro system. This macro is using dependencies like
quote
,syn
, andproc-macro2
to parse and generate code. The code itself contains a mixture of modules and macro_rules-like macro expansions.When we tried to enable the
unstable
feature inproc-macro2
, which switches it to use the "real"proc_macro
APIs and preserve span information, it turned out everything broke! When digging into this I found that everything we were experiencing was related to the distinction between thedefault
andcall_site
functions.So as a bit of background, the
gnome_class!
macro uses a few methods to manufacture aTokenStream
. Primarily it uses thequote!
macro from thequote
crate, which primarily at this time usesparse
for most tokens to generate aTokenTree
. Namely part of thequote!
macro will callstringify!
on each token to get reparsed at runtime and turned into a list of tokens. For delimiters and such thequote
crate currently creates adefault
span.Additionally both @federicomenaquintero and I were novices at the procedural macro/hygiene/span systems, so we didn't have a lot of info coming in! Now though we think we're a bit more up to speed :). The rest of this issue will be focused on "weird errors" that we had to work backwards to figure out how to solve. This all, to me at least, seems like a blocker for stabilization in the sense that I wouldn't wish this experience on others.
I'm not really sure what the conclusions from this would be though. The behavior below may be bugs in the compiler or bugs in the libraries we're using, I'm not sure! I'll write up some thoughts at the end though. In general though I wanted to just detail all that we went through in discovering this and showing how the current behavior that we have ends up being quite confusing.
Examples of odd errors
In any case, I've created an example project showcasing a number of the "surprises" (some bugs?) that we ran into. I'll try to recatalog them here:
Using
parse
breakssuper
The first bug that was found was related to generating a program that looked like:
It turns out that if you use
parse
to generate the tokensuper
it doesn't work! If you set the span ofsuper
todefault
, however, it does indeed work.I was pretty surprised by this (and the odd error messages). I'm not really sure why the
parse
span was not allowing it to resolve, but I imagine it was related to hygiene? I submitted dtolnay/quote#51 which I think might fix this but I wasn't sure if that was the right fix...Is that the right fix for
quote
? Should it be usingdefault
wherever it can? I originally though that but then ran into...Using
Span::default
means you can't import from yourselfThis second bug was found relating to the program that looks like:
Here we have a failing procedural macro despite the usage of
Span::default
on all tokens. This means that by default all modules generated viaquote!
, if we were to switch spans toSpan::default
, would not be able to import from one another it looks like? But maybe this is only related tosuper
? I'm not quite sure..It also turns out that this does indeed work if we use
Span::call_site
by default everywhere. I'm not really sure why, but it apparently works!Using
Span::default
means you can't import generated structsNext up we had a bug related to:
It turns out that if these tokens are using
Span::default
this can't actually be used! In this test case you get an error about an unresolved import.Like with before though if we use
call_site
as a span everywhere this case does indeed work.Is this expected? This means, I think, that all tokens with a
Default
span can't be improted outside of the procedural macro.Using
Span::default
means you can't use external cratesNext we took a look at a program like:
When generating these tokens with
Span::default
it turns out that this becomes an unresolved import! That is, the usage ofSpan::default
seems like it's putting it in an entirely new namespace without access to evenstd
at the root. Coming from the perspective of not knowing a lot about spans/hygiene I found this a little confusing :)As with the previous and remaining cases using
call_site
as a span does indeed get this working.Naturally the error message was pretty confusing here, but I guess this is expected? Hygiene I think necessitates this? Unsure...
Using
Span::default
means you can't import from yourselfNext up we have a program like
Here if we use
Span::default
everywhere this program will not compile with the import becoming unresolved. For us this seemed to imply that if we generated new modules in a macro we basically can't use imports!As per usual respanning with
call_site
everywhere fixes this but I'd imagine has different hygiene implications. I'm not sure if this behavior was intended, although it seemed like it may be a bug in rustc?Using
Span::default
precludes working with "non hygienic macros"This is a particularly interesting use case. The
gnome_class!
procedural macro internally delegates to theglib_wrapper!
macro_rules macro in the expanded tokens. Theglib_wrapper!
macro, however, in its current state does not work in an empty module but rather requires imports likestd::ptr
in the environment. WithSpan::default
, however, the generated tokens inglib_wrapper!
couldn't see the tokens we generated withgnome_class!
.For example if in one crate we have a macro like
(note that this requires
std::mem
to be imported to work)and then we're generating a token stream that looks like:
Note that the
extern crate
is necessary due to one of the above situations (we can't import from the top-levelextern crate
). Here though if we generated tokens withSpan::default
as with many other cases this doesn't work! As usual if we respan withcall_site
spans then this does indeed work.Is this a bug? Or maybe more hygiene?
Conclusions
Overall for our use case we found that 100% of the time we should be using
Span::call_site
instead ofSpan::default
to get things working. Whether or not that's what we wanted hygienically we're not sure! I couldn't really understand the hygiene implications here because tokens usingSpan::default
couldn't import from other modules defined next to it with the default span as well.Should
quote
andsyn
move to usingSpan::call_site
by default instead ofSpan::default
? Or maybeSpan::default
should be renamed to sound "less default" if it appears to not work most of the time? Or maybeSpan::default
has bugs that need fixing?I'm quite curious to hear what others think! Especially those that are particularly familiar with hygiene/macros, I'd love to hear ideas about whether this is expected behavior (and if so if we could maybe improve the error messages) or if we should perhaps be structuring the macro expansion differently. Similarly what would recommendations be for spanning tokens returned by
quote!
in an external crate? Orsyn
? (for example if I maufacture anIdent
, is there a "good default" for that?)In any case, curious to hear others' thoughts!
cc @jseyfried
cc @nrc
cc @nikomatsakis
cc @federicomenaquintero
cc @dtolnay
cc @mystor
The text was updated successfully, but these errors were encountered: