-
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
Amend RFC 550 with misc. follow set corrections #1494
Conversation
RFC 550 introduced follow sets for future-proofing macro definitions. They have been tweaked since then to reflect the realities of extant Rust syntax, or to bail out crates that were too-big-to-fail. The idea (insofar as I understand it) is that the follow set for a fragment specifier `X` contains any tokens or symbols that could follow `X` in current (or planned) valid Rust syntax. In particular, `FOLLOW(X)` does _not_ contain anything that could be part of `X` (silly example: `+` is not in `FOLLOW(expr)` because obviously `+` may be in the middle of an expression). A macro may not accept syntax that has an `X` followed by something not in `FOLLOW(X)`. That way, if in the future we extend Rust syntax so that `X` can encompass more things (that were previously not in the follow set), the macro doesn't break. The upshot is that if there is something that can _currently_ follow `X` in valid Rust, but it is not in `FOLLOW(X)`, it is simply an unnecessary roadblock to macro writing, because a change that would break a macro would also break regular syntax. This RFC amendment proposes to remove two of those roadblocks. (If there are more that I missed, please suggest them.) Specifically: - Allow `ty` (and `path`) fragments to be followed by `block` fragments. Precedent is function and closure declarations, i.e. `fn foo() -> TYPE BLOCK`. Indeed you can already write `$t:ty { $($foo:tt)* }` in a macro rule, so `$t:ty $b:block` is natural. And `FOLLOW(path) = FOLLOW(ty)` because a path can name a type. - Allow `pat` fragments to be followed by `:`. Precedent is let-bindings and function/closure arguments, i.e. `let PAT: TYPE = EXPR`.
Implemented here. I found out that my notation |
I'd have to re-read the type ascription RFC to be sure, but I think the logic given in the description to justify adding colon to follow of pat does not quite hold up, In particular, we could change the grammar to say:
which would invalidate the logic given here to justify putting colon in pat's follow set. (This also demonstrates why the follow sets are not simply mechanically derivable from the grammar) Update: such a change to the grammar would also take it very far afield from being LL(1); I don't think that is as much of a concern as it used to be, but I would love to be proven wrong on that point. ) |
Good point. Though type ascription turns |
I read a bit of the context on type ascription, including the fact that type ascription on patterns was considered before 1.0 but postponed (and is still currently postponed). So it seems like |
While I continue to understand the motivation for not adding $(move)* |$($p:ident $(: $t:ty)*),*| $(-> $rt:ty)* { $($closure:tt)* } which simply disallows pattern arguments. Another alternative is $(move)* |$($p:pat $(=> $t:ty)*),*| $(-> $rt:ty)* { $($closure:tt)* } which allows patterns but bizarrely uses I briefly thought the upcoming Do you have any ideas for improving this situation? |
I just ran into the Is there some workaround for that? I tried Also, I think this RFC should be uncontroversial now that the |
@dgrunwald yeah we should move this forward, but |
@durka: Thanks, AST coercion does the trick. I now got a Python class in my Rust program. 😄 🎉 🎈 |
Nominating for discussion at Lang team mtg |
Just noting mostly to track progress/discussion: lang team currently thinks that the goal is good, but it may have concerns about whether the manner in which it is specified is quite right. Namely, is putting a construct like (I think the manner of specification here is okay, and properly matches the way that RFC 550 is specified, but the situation is worth reviewing carefully.) |
What's the next step here? The report from the lang team above is frustratingly ambiguous. |
@durka I'm actually going to re-nominate the RFC for consideration at the next lang team mtg, using the basic argument as follows: The lang team has pointed out that a more ambitious generalization of the future-proofing rules could be applied here.
The above idealized goal may be desirable; however, I argue: There is no actual proposed amendment to extend the future-proofing rules in the manner hypothesized above. Any such amendment that I can imagine would solely generalize the effects of this RFC amendment (#1494). Therefore, we can continue to consider making such a change in the future, but we can and should merge amendment #1494 in the meantime. |
Er, since when can a block start with a semicolon? |
Oops, fixed. Thanks! |
Your reasoning sounds good to me. I'm also interested in the lang team's ideas, if any, on how to make it possible to parse closure syntax. |
Note for Lang team: one problem with the hypothesized generalization is that we would have to define a peimitive FIRST(nt) to set of non-NT tokens (the same way this RFC defines FOLLOW(nt), and future language changes would have to be compatible with (all previous definitions of) FIRST. This is likely to be more restrictive to future grammar extensions than the current setup as used in the amendment as written |
💬 This RFC is entering its Final Comment Period! 💬 |
It's official! The @rust-lang/lang team has decided to accept this RFC. |
implement RFC amendment 1494 Adds `:block` to the follow set for `:ty` and `:path`. See rust-lang/rfcs#1494.
RFC 550 introduced follow sets for future-proofing macro definitions. They have been tweaked since then to reflect the realities of extant Rust syntax, or to bail out crates that were too-big-to-fail.
The idea (insofar as I understand it) is that the follow set for a fragment specifier
X
contains any tokens or symbols that could follow a completeX
in current (or planned) valid Rust syntax. In particular,FOLLOW(X)
does not contain anything that could be part ofX
(silly example:+
is not inFOLLOW(expr)
because obviously+
may be in the middle of an expression). A macro may not specify syntax that has anX
followed by something not inFOLLOW(X)
. That way, if in the future we extend Rust syntax so thatX
can encompass more things (that were previously not in the follow set), the macro doesn't break.The upshot is that if there is something that can currently follow
X
in valid Rust, but it is not inFOLLOW(X)
and this is unlikely to change in the future, it is simply an unnecessary roadblock to macro writing, because a change that would break a macro would also break regular syntax. This RFC amendment proposes to removetwoone of those roadblocks. (If there are more that I missed, please suggest them.)Specifically:
ty
(andpath
) fragments to be followed byblock
fragments. Precedent is function and closure declarations, i.e.fn foo() -> TYPE BLOCK
. Indeed you can already write$t:ty { $($foo:tt)* }
in a macro rule, so$t:ty $b:block
is natural. AndFOLLOW(path) = FOLLOW(ty)
because a path can name a type.Allowpat
fragments to be followed by:
. Precedent is let-bindings and function/closure arguments, i.e.let PAT: TYPE = EXPR
.