-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Eager expansion of macros #1628
Conversation
…s providing a mechanism for creating new identifiers in macros (e.g., by concatenating identifiers). It is not currently practical to do this because macros cannot be used in identifier position.
Is it a "macro 2.0"-only feature or will it be backported to |
I think it's kind of cute that |
@kennytm primarily a macro 2.0 feature. If it is not too much implementation work and doesn't break backwards compatibility (which I don't think it does), then we could backport. |
Can anyone explain the advantage of not always eagerly expanding macros? Alternately, why only allow this inside macro definitions? concat_idents is not the only use case for having a macro expand to something other than a full item/expression/etc. (And the limit could be trivially circumvented by putting the entire file inside a macro definition, if necessary!) |
(Just asking about the semantics, not that this feature is important :: ) Would this be valid? macro a {
(0) => { 1, 2, 3 };
(1) => { [$!a(0)] };
}
fn main() {
let m = a!(1);
} Would this be valid? macro b {
(0) => { , };
(1) => { [1 $!b(0) 2 $!b(0) 3] };
}
fn main() {
let m = b!(1);
} |
The two other syntax choices I've seen for this feature are |
not be obvious when to use either flavour. | ||
|
||
|
||
# Alternatives |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternative: macro-level $let which lets you do things like $$let $x = concat_idents(...); foo!($x)
. This does the same thing as eager expansion, but is more readable.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Binding is orthogonal to eager expansion, though, e.g. you might want to use $let
for a handful of (unexpanded) tokens to reuse them a few times.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This ultimately generalizes to quasi-quoting, just saying.
Ooh, doubling the |
As long as this can only be used inside macros, |
Getting into the syntax bikeshed - I like |
@kennytm I think both uses would be valid. I should add a note to the RFC about recursive use. I guess we would bail when we detect a cycle of expansion, rather than a cycle of names. |
@comex I don't think there are technical reasons for not eagerly expanding all macros. But regular expansion just feels more natural. There are some subtle differences with hygiene, I wonder if they might cause issues if all macros were done that way. To allow macros in any position macro expansion would have to happen on tokens, rather than on the AST. That would prohibit modularisation for macros and make hygiene much more complex, if not impossible. |
So from @kennytm's examples, eager expansion drops the requirement that macros expand to a full expr/item/type/etc. |
@nrc Hmm... I reread the RFC again and read your blog post about scopes... still a bit confused. The limitations you mention seem like they would also apply to the contents of a macro definition. Does eager expansion happen on tokens? I guess you sidestep the issue by not allowing names defined within a token list that gets eagerly expanded to affect macro resolution from that same eager expansion... (Which also means that performing the same process on the entire file first would be mostly useless, as no non-builtin macros could be in scope yet.) What happens if you try, though? macro bar { () => { macro baz { ... } } }
macro foo { () => { $!bar() } } What scopes does Is there a way to 'escape' expansions, so that in this example macro bar { ($mname:ident) => { macro $mname { $!test() } } } (Or if it's always part of the inner macro definition, then what if the word If eager expansions are nested, does the whole thing look up macros based on the innermost non-eager expansion's scope? Getting into pedantry a bit... Is it possible for an eager expansion to have unbalanced braces/parens? e.g. what happens if you write $!foo($!include("parens.rs") $!bar()) will (on second thought, allowing that would kind of break the world anyway... since then you could exit the scope of the macro definition you're in) |
I would like to see This is pretty much the only way I see forward for being able to define macros from macros (modulo some name resolution magic around |
@comex The question would be whether eager expansion should be done before lexing or after it. If expansion is done after lexing, |
If Or, since this is "macro 2.0", we would assume #1566 is implemented already, and all syntax extensions (including those in libstd) are migrated to the libmacro API, which produces TokenStream. |
Has any consensus been reached on this? For me, this would be an extremely desirable feature that would go a long way towards uncrippling the current macro system. Macros 2.0 will be great, but a long way off, of course. |
@alexreg The prerequisites will be reached for macros 1.1 (cc @alexcrichton and @nrc) so eager expansion should be doable then, although it might remain unstable for a while. |
Thanks for the update. That’s good to hear. Any timeframe for that?
|
Hear ye, hear ye! This RFC is now entering final comment period with an inclination to accept. Summary of discussion follows:
@rust-lang/lang members, please check off your name to signal agreement. Leave a comment with concerns or objections. Others, please leave comments. Thanks!
|
re syntax, I still prefer my original proposal, but could also live with re scoping,
This is a hygiene question somewhat orothogonal to this RFC and the answer hasn't been completely nailed down anywhere yet. My current understanding is that, as written,
There is not. For nested macros, there is no proposal for escaping variables, there should be and when that happens, the same mechanism should apply to eager expansions too.
No, never, macros operate on token trees and they do not admit unbalanced delimiters. |
Can we formalize this? Perhaps we could say say something like "an eager macro invocation expands to a single token tree which is merged with the node containing the invocation", à la unquote-splicing. edit upon more thought, it's probably just regular unquote. |
@nrc: given the relative lack of bandwidth around macros, I think we should consider postponing this RFC (until someone has the time to take up the mantle). What do you think? |
I think we should postpone until proc-macros have the API to eagerly expand token streams and then see if a third-party |
@rfcbot fcp postpone I agree with @jseyfried and @aturon that it makes sense to wait here. I remain pretty conflicted about this proposal in any case, as has been previously discussed on thread. @nrc -- feel free to object, however. =) |
Team member @nikomatsakis has proposed to postpone this. The next step is review by the rest of the tagged teams: No concerns currently listed. Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
So much for my dream of one day being able to concatenate idents in declarative macros... |
I agree we can postpone, I still think this is the best solution though. I think that if someone wanted to implement this (behind a flag, obvs) then that should be fine too and might offer some insight into the details (plus of course usage experience). |
Also, why does GitHub not offer me a sadface emoji? |
I've checked the review box for @pnkfelix, who is on PTO for an extended period. |
🔔 This is now entering its final comment period, as per the review above. 🔔 |
The final comment period is now complete. |
Closing as postponed. We'll definitely be revisiting this topic! |
Hey @nrc. Per our discussion on Discord, could we get this reopened, with a view to merging it soon? I put my hand up to implement this, of course, and feel I'm in a good position to do so given my experience discussing this over many months, and my previous work with the macro system. I'll also draft a separate RFC for some new libstd macros at the same time (to do with hygiene manipulation). |
My read of this thread is that the lang team is divided on the design here; its not implementation bandwidth that led it to be postponed. (I myself am totally undecided!) |
@withoutboats Okay, that certainly wasn't clear from the above, but I'm glad that's made plain now, thanks. So, it sounds like more discussion is needed then. I had some conservations with @nrc lately... a few points seemed to come up in discussion:
Shall we reopen so we can at least discuss it more? Maybe address some of the above points, and your present concerns, would be the best thing. |
Both of these options are perfectly acceptable to me. |
Is easer expansion a good idea not for only working around some missing feature (ident-position), but also as a tool to push DRY further, when repetitions happen inside a foreign macro call? |
Good to know. Personally I'm also relatively happy with either. Would like to get some other @rust-lang/lang members' feedback on these points too though. |
@vi Not sure what you mean... |
@alexreg, I mean implementing macros to work within other non-cooperating macros. |
There's been no momentum on this since people expressed a desire to reopen it in 2018... is it feasible to revisit this at this point? |
This RFC proposes eager expansion of macros. The primary motivation is providing a mechanism for creating new identifiers in macros (e.g., by concatenating identifiers). It is not currently practical to do this because macros cannot be used in identifier position.