rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
98.24k stars 12.71k forks source link

Fails to parse boolean expression of nested match expressions #88727

Open alexmaco opened 3 years ago

alexmaco commented 3 years ago

I tried this code (reduced to minimal triggering case):

fn main() {
    let (a, b, c) = (3, 4, 5);
    let r: bool = match c {
        _ => match a {
            _ => true
        } && match b {
            _ => true,
        },
    };
}

(Failing in playground)

Fails with the following error:

error: expected identifier, found keyword `match`
 --> src/main.rs:7:14
  |
7 |         } && match b {
  |              ^^^^^ expected identifier, found keyword

error: expected one of `=>`, `@`, `if`, or `|`, found `b`
 --> src/main.rs:7:20
  |
7 |         } && match b {
  |                    ^ expected one of `=>`, `@`, `if`, or `|`

error: aborting due to 2 previous errors

However, the following works as expected:

fn main() {
    let (a, b, c) = (3, 4, 5);
    let r: bool = match a {
        _ => true,
    } && match b {
        _ => true,
    };
}

Given that the second example compiles, we should expect the first one to compile as well.

Meta

I've found this issues on all rust versions 1.54.0 to 1.57 nightly inclusive. It doesn't look like a regression, since the same code is rejected as far back as 1.30, just with a different error message.

Noble-Mushtak commented 3 years ago

I played around with your code and found a smaller minimal example (here's the playground):

fn main() {
    match () { _ => true } && true;
}

However, this outputs a typechecking error rather than a syntax error:

error[E0308]: mismatched types
 --> src/main.rs:2:5
  |
2 |     match () { _ => true } && true;
  |     ^^^^^^^^^^^^^^^^^^^^^^- help: consider using a semicolon here
  |     |
  |     expected `()`, found `bool`

error: aborting due to previous error

After discussing this issue with someone else, I realized that if you add the semicolon like it suggests, then the error goes away, because && true is a double reference (i.e. &(&true)).

(Interestingly, if you run match () { _ => () } && true, it actually compiles and evaluates to true. Even though this looks like a typechecking error because it seems to be doing () && true, it is actually doing (); &(&true). Here's the playground.)

I think the same thing is happening in your example, where it is interpreting the && as a double reference. It seems to be parsing the && as part of a pattern, but then it outputs an error when it sees you are trying to put a match expression inside of a pattern. If you do,

fn main() {
    let (a, b, c) = (3, 4, 5);
    let r: bool = match c {
        _ => match a {
            _ => true
        } && d => false
    };
}

Then the _ => match a { _ => true } is interpreted as one match arm while the &&d => false is interpreted as another match arm. But this does not typecheck because c is not a double reference. However, if you replace match c with match &&c, then it does typecheck and outputs true. Here's the playground.

estebank commented 8 months ago

Triage: no change.