rust-lang / rfcs

RFCs for changes to Rust
https://rust-lang.github.io/rfcs/
Apache License 2.0
5.92k stars 1.57k forks source link

Add macro_rules fragment class for "expressions that can be followed by `{`" #1463

Open pnkfelix opened 8 years ago

pnkfelix commented 8 years ago

(spawned off of https://github.com/rust-lang/rfcs/pull/1384#issuecomment-160165525 )

I've been leaving links to the above comment in all sorts of places, so I finally decided it was time to open an RFC issue for this idea.

Namely, to add some fragment class, which I'll call pred for now, that represents "the subset of expressions that do not have { in their follow set, and thus the presence of a { as the next token tells the parser that it is definitely done parsing the expression."

In particular, this is the subset of expressions that we use for the test in an if test { then } else { other }

Such an fragment class would be useful for writing macro_rules! that naturally want a { to come after them in the input pattern.

We used to superficially "allow" such macros, but they didn't actually work in general; see https://github.com/rust-lang/rust/issues/26739

Stebalien commented 8 years ago

FYI, this covers more than predicates:

// Does it cover `let x = y` (a predicate?) or just `y`?
while let x = y {
}

// no predicate in sight
for a in b {
}
pnkfelix commented 8 years ago

@Stebalien I am not quite sure how to interpret your comment.

For example, while let x = y { ... } is not the result of composing while <expr> { ... } with some internal let x = y ... let x = y is not an expression. Instead, while let is its own expression construct in Rust's grammar.

Stebalien commented 8 years ago

Sorry, I meant to write "covers more than predicates" instead of "covers more than pred". My point was that neither y nor b are predicates (although let x = y technically acts like a predicate but that's not really that important) so we'll need to think of some better name. However, after re-reading your post, you clearly realized this so I'm just going to shut up now...

pnkfelix commented 8 years ago

@Stebalien ah yes; the name pred may not be ideal, since in cases like the ones you name, its not that the expression in question returns a boolean, but rather e.g. for while let returns an enum (and we're matching a particular variant), or for for a in b, the b needs to return something that implements IntoIterator -- so the connection to pred there is certainly tenuous at best.

Really the most attractive thing about the token pred to me is just that it is short. I don't know if it really has all that much else going for it...

durka commented 8 years ago

I might be having a brain fart, but what situations in the current grammar have expressions followed by {? I guess struct literals are path { ... }, and a path is an expr. Is that it?

In terms of bikeshedding the name:

Another crazy idea is "silent adaptation" -- if you put $e:expr { in a macro rule, it becomes a matcher for a moelarry instead of a full expr. This is probably a bad idea.

pnkfelix commented 8 years ago

if you put $e:expr { in a macro rule, it becomes a matcher for a moelarry instead of a full expr. This is probably a bad idea

Yeah, it probably is bad; consider e.g. the macro pattern $a:expr $($b:expr)* { stuff }*. Is the $b still an nbe? If so, how about $a ?

Stebalien commented 8 years ago

@pnkfelix IMO, that's unambiguous. $a is an nbe because it might be followed by an expression (which could start with {). $b is an nbe because it's repeated.

pnkfelix commented 8 years ago

@Stebalien in that case I think we would be better served by a separately named fragment class. Having the potentially matched forms of a NT depend on patterns potentially arbitrarily far ahead in the macro LHS seems bad

Stebalien commented 8 years ago

@pnkfelix

potentially arbitrarily far ahead

Unless I'm mistaken, it only depends on the next pattern. However, I'm certainly not opposed to making it explicit (I just can't think of a name).

pnkfelix commented 8 years ago

Sorry I think my example failed to express what I'm concerned about; look e.g. at:

$a:expr $(, $b:expr )* { stuff }

(Where I have now added a comma before $b so that we don't worry the two exprs right next to each other)

In this case, a { may still come right after the $a, but the reader must look at the whole of the LHS to see it. That's what I am concerned with.

In this case, a `

Stebalien commented 8 years ago

Ah. I see. Yes, that's a problem.

Steven Allen

durka commented 8 years ago

As part of this RFC (or maybe a separate one), can we expand a couple of follow sets with things they should logically already include, for macro writing convenience?

Did I miss any?

pnkfelix commented 8 years ago

@durka those sounds like good things, but I would put them in a separate RFC. Actually I'd put them into an amendment of the macro-future proofing RFC -- that is likely to ensure that it gets approved faster.

durka commented 8 years ago

See #1494.