-
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
RFC: Macro reform #453
RFC: Macro reform #453
Conversation
|
||
## Rename | ||
|
||
Rename `macro_rules!` to `macro!`, analogous to `fn`, `struct`, etc. |
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.
As I stated on #440, I don't like taking this nice name for this imperfect macro system. It seems unfortunate to me that one would have to explicitly opt-in to some new improved macro system if/when we create a parallel one that incorporates the backwards-incompatible improvements to macro_rules!
. (That is, it is extremely unfortunate that the obvious macro! foo { ... }
invocation would give the deprecated form.)
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.
Let's rename trait
to crappy_trait
because we don't have HKT yet.
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.
Okay, maybe that's an unfair comparison because HKT are supposed to be back-compat. But every macro system we ever have in Rust is going to be "imperfect" in some way. At some point we have to live with the imperfections and improve them in non-breaking ways, which is what 1.0 is all about, but why back away from that idea just in the case of macros?
The practical outcome of your alternative is that Rust programmers are stuck with the ugly name macro_rules!
for perhaps years and the unstable alternative will be something not descriptively different, like macro_case!
.
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.
With macros, we already know before 1.0 that the ways we want to improve them in the future are not backwards compatible, and the stabilization of the current system of macros is itself a compromise. I don't know of another Rust language feature in a similar situation, but maybe there is an example?
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.
Basically I see it as
- force most people to write
macro_rules!
, for years - force some people to write
#![feature(improved_macros)]
or#[version(2)] macro!
, if and when such back-incompat enhancements are developed
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.
Long term,
improved_macros
are what people are going to use,
But long term, they'll be on Rust 2.x or Rust 3.x, which gives us another chance to make breaking changes, especially fairly superficial ones like naming.
I'm not sure how long 1.x is meant to last but it would seem relevant to this discussion.
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.
One thing to note: some of the ways that macros will change, like hygiene, will be (a) a change to the entire system, preventing backwards compatibility from even being possible (i.e., define_a_macro_in_the_old_way!
won't recover the old semantics), and (b) make things almost strictly better (i.e., the old semantics mainly gets in people's way).
However, the reason it is called macro_rules!
is that it's not able to define all kinds of macros, only rule-based ones. A construct named macro!
should be capable of defining procedural macros, and I think that the arrival of procedural macros is an appropriate time to make the name change.
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.
I was expecting that we would have two essentially independent hygiene systems inside the compiler (i.e. identifiers would go from storing just ctxt: SyntaxContext
to storing ctxt: SyntaxContext, improved_ctxt: SyntaxContext
); it's pretty sad, but it seems to me to be the only way we can possibly aim to improve hygiene without breaking backwards compatibility.
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.
@huonw this is horrifying! I was expecting that we would be able to have a single hygiene system in rustc and any macro system could do what they like - i.e., rustc::resolve would expect all lexical context ids to be 'correct'. macro! would output correct lexical contexts, macro_rules! would output lexical contexts that correspond to the current level of hygiene, etc. I'm not entirely sure if this is possible, because I don't exactly know how hygiene is currently broken.
As far as I see it, being able to do this backwards compatibly after 1.0 is the most important issue for macros pre-1.0.
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.
@nick29581 I agree 100% that it is not nice, but I believe that it is an existence proof that we can have a macro system with improved hygiene post-1.0 without being forced to break what we already have. (Your approach does sound better, if it works.)
Great, it's awesome to see an RFC on this! The lack of name-spacing is something that I find particularly jarring about macros - it makes them seem a little wild and pollutant. One thing I'd love to see is macros abiding by the module system. @huonw explained to me here that macro expansion runs prior to "the module system", which makes sense! However I don't see why we still can't use the same syntax for cohesiveness with the rest of the language. Perhaps we could add a pub mod! module { ... }
use! module::my_macro; I feel like this would be a lot cleaner and a lot less ad-hoc than using |
Although this RFC gives ways of avoiding many of the current problems with macros, the system is still not ideal:
Those are some pretty big problems in my opinion, and although this RFC provides workarounds for those problems ( That said, this RFC is still a huge step forward. If we have to stabilise macros for 1.0, and if solving the problems above is unlikely to happen for the 1.0 timeframe due to implementation difficulty, then I think that this RFC is at the very least a good solution to go with until, if ever, something better is implemented. If something better does come, then we can make them opt-in ‘version 2 macros’ or something until the next breaking release, when the old macros can be removed. |
Weird, but I like it at a glance. |
sigh The point of this is not to make an "ideal" system, it's to find improvements we can feasibly land in 1 or 2 months. I really don't know how I could have been more clear about that.
Noooooooooobody is suggesting ignoring these problems forever. Why are you strawman-ing my RFC? |
While most of us (I think including @P1start) are aware of the compromise that has been chosen of stabilizing the macro system we have now for 1.0, and then improving (perhaps redoing) it afterward, I think that that context is not evident in this RFC, so I think it's understandable that people may see it and come to the conclusion that this is a proposal for the final macro system. From that perspective, I don't think @P1start was intending to "straw man" your RFC. |
Yes, I understand that, and as I noted at the end of my comment I agree that this is a good solution for now. I was merely attempting to outline what this RFC does not address. (I understand that this RFC is not supposed to fully address those problems due to the time restrictions on 1.0.) On reflection I see that I did not come across that way.
Sorry, I didn’t mean to come across that way. I was merely trying to point out that doing so would be a bad idea (without implying that this RFC stated that), but I suppose that was unnecessary. |
I think it would be useful to make it explicit in the RFC that the macro system is intended to be reformed more comprehensively after 1.0, and that this is intended as a stop-gap measure (perhaps in preparation for that). This is alluded to but not explicitly mentioned. |
Fair enough, I guess that was more clear in the various discussion threads than in the actual RFC. |
|
||
## `$crate` | ||
|
||
Add a special metavar `$crate` which expands to `::foo` when the macro was |
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.
Bikeshed: I would prefer something like $_crate
, or $$crate
.
Reading quickly over this, these seem like reasonable stop gap measures, but it might be helpful to describe possible post 1.0 transition paths towards the ultimate RustMacroSystem™. |
Minor syntactic detail: is it possible to change the delimiter between |
How about using the normal namespacing mechanism for macros too? That means that macro expansion and name resolution need to be done in the same pass, but it avoids all the namespace reinvention in this RFC. |
@bill-myers I would ultimately love to see something like |
do | ||
|
||
```rust | ||
#![reexport_macros(collections::vec)] |
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.
Could this expand to something like pub use collections::vec!;
post 1.0?
This is basically what I suggested on reddit, but how hard would it be to:
Importantly, I think that just because these use the same syntax as name resolution and import for items, that doesn't mean they also necessarily have to be implemented using the same mechanisms. We could do some kind of ugly hacks under the hood where e.g. (I'm 99% oblivious to the current implementation, so I'm probably getting the details wrong) (We could even require The third bullet point above corresponds to an alternative mentioned in the RFC. I personally think that having to "pollute" |
@bill-myers: Yeah, this is a massive change to rustc and not really in scope for 1.0. |
I don't think we want to actually support invoking macros that way, because it will involve changes to the parsing of exprs, items, patterns, etc. in ways that probably have unintended negative consequences. My plan was to
That syntax would be nice, but I hesitate to introduce two different mechanisms (in two different crates even!) that interpret I might warm to this approach, though, because the syntax would be really nice and should be back-compat with whatever better approach we take post-1.0. Also I'm not sure about using
I see the virtue in this, but I'd like to think of ways to avoid it. If we're already introducing a syntax for macro re-export, perhaps we should make it work on modules within a crate, and then require that exported macros are either defined or re-exported at the crate root. We could also just say "all exported macros also appear at the crate root, forever" which makes things less nice in the future, but not hugely so. |
Yeah, I guess you could go even further and disallow paths-to-macros entirely, and require |
FWIW, we already support parsing |
(One issue with |
@bill-myers @bjz I guess that this isn't all that relevant, because this RFC is about near-future stuff, but I'd like to clear something up: The normal namespacing mechanism requires being able to see the whole module system at once. But the macro resolver must be able to run when only the code above (higher up in the tree or earlier in the file) has been expanded. Therefore, the macro system needs its own resolver with its own resolution rules. To minimize confusion, I think it would be prudent to write something like |
@comex While |
In general, I like this RFC a lot. It proposes to provide more power and flexibility to the user, and it should soften the transition to the Way Things Will Work In The Beautiful Future. I have a couple of quibbles, though.
(As a bonus, |
Regarding $crate or $$crate, I'm going to propose a mini rfc to disallow bindings of the $ or $$ type (haven't decided which) so that we can later use them for macro magic without breaking backcompat. -----Original Message----- In general, I like this RFC a lot. It proposes to provide more power and flexibility to the user, and it should soften the transition to the Way Things Will Work In The Beautiful Future. |
@kmcallister I think keeping |
I think there are a lot of good ideas here, but I think that this proposal is trying to do too much at once. The original vision here was to do a kind of "maximally minimal" set of extensions that can correct issues around macro scoping, import/export, and cross-crate macros. Trying to do more than that for 1.0 doesn't seem right to me; I'd rather tackle bigger redesigns when we have more space to implement and consider more radical changes. In particular, I think we can separate out macro item sugar and the I can see the desire to use
To be clear, I think that the underlying system you proposed is a reasonably good idea and seems like a simple extension to macros that will make them usable and easy to understand. However, I think that packing these semantics in "normal" syntax like I preferred the original attribute version, since that at least kept the macro syntax as clearly different and confined into the "meta world" of macros and attributes. I think this makes sense. If we want something that looks more like a That said, I would prefer to avoid the need to define name resolution rules for crate names, and hence think that perhaps we ought to consider attaching attributes to
|
I'm counting on people to learn, through documentation and error messages, that
It would make sense if we refuse to import macros from an extern crate "foo" as bar; is introducing paths of the form Because of the difficulties presented by #[use_macros(foo, bar)] // use macros for this module and all submodules
extern crate some_crate;
I'm fine with this, as long as we get another chance to address the
because the syntax for the definition body can change in backwards-compatible ways. For example, I described two ways to integrate procedural macros. For that matter, an improved macro system could be implemented as a library providing an |
Note that (I'm contemplating an RfC that changes |
I removed some inessential parts of the proposal, and clarified the way that crate identifiers are used. |
I updated this RFC to account for decisions made in the macro meeting last Thursday. Niko's proposal to make macro argument consumption more greedy should be a separate RFC, I think. |
|
||
`#[phase(plugin)]` becomes simply `#[plugin]` and is still feature-gated. It | ||
only controls whether to search for and run a plugin registrar function. The | ||
plugin itself will decide whether it's to be linked at runtime, by calling a |
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.
Why would a syntax extension want to link itself in at runtime? It injects a runtime dependency on librustc, so most (all?) syntax extensions have a separate crate for runtime support (e.g. regex and regex_macros, or phf and phf_mac).
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.
Yeah, I don't expect it to be a common situation. One example would be quasiquoting as a library. quote_foo!()
expands to calls into syntax::ext::quote::rt::...
. And there's no need for this to live in a separate crate from the quote macro, because the user of quasiquoting is also going to link librustc, or libsyntax anyway.
Basically it's a capability we have today with #[phase(plugin, link)]
and I didn't want to throw it away. Though, the Registry
API is unstable and we'd only be adding capabilities to it, so maybe we should throw this out for the time being and see if anyone complains. It might be good to nudge people towards doing thing the two-crates way.
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.
My gut says that #[phase(plugin, link)]
is only ever used for imports of macro_rules
plugins, so I'd vote for axing the functionality until people start complaining :).
The current system's lexical order dependence is also a pain, but probably not worth worrying about before 1.0. |
Yeah, I thought a bit about how to fix the lexical order thing, and I think it's pretty hard. You'd have to move away from the model where It's not a huge annoyance in practice because
|
I think it's doable - possibly by making expand a bit more like resolve, where it repeatedly folds the AST. In any case, it's annoying but not hugely so like you said :) |
@sfackler The current side-effect-based system has to be replaced by a proper scope system, but removing order dependence is not possible; see the second paragraph of this comment. If we completely broke evaluation order for the side-effects of procedural macros, we could make it resemble the behavior of resolve more, but I suspect that accurately recreating the behavior of resolve is completely impossible, because resolve was designed to see the entire tree at once, and macro resolution necessarily has to happen while parts of the tree are still unexpanded. (I don't know the exact behavior of resolve, but I remember that getting it right turned out to be surprisingly tricky and subtle!) The best approach (and the only one that I know of being used in any other language's macro system) is to expand macros in lexical order and to resolve macros against only already-expanded parts of the AST. I recognize that this isn't really relevant at the moment, but judging by the number of times that I've heard this suggestion, I'm worried that macro-names-that-resolve-like-function-names is going wind in the conventional wisdom about the future of Rust's macro system, and stay there until someone tries to implement it. |
I am more optimistic than @paulstansifer about resolve for macros, but I agree that it's not something we can or should count on ever having. |
This RFC has been accepted and merged as https://github.com/rust-lang/rfcs/blob/master/text/0453-macro-reform.md. Thanks @kmcallister for your hard work in preparing it. |
Rendered view