Skip to content

Sidestep bad precedence of closures in let bindings #64277

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

Closed
wants to merge 2 commits into from

Conversation

estebank
Copy link
Contributor

@estebank estebank commented Sep 8, 2019

Avoid adding unnecessary parentheses around closures in
if let _ = || ().

CC #51635.

r? @petrochenkov

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Sep 8, 2019
@estebank
Copy link
Contributor Author

estebank commented Sep 8, 2019

Tested locally on project with the issue:

error[E0308]: mismatched types
  --> src/main.rs:12:12
   |
12 |     if let Some(_) = || Some(()) {
   |            ^^^^^^^ expected closure, found enum `std::option::Option`
   |
   = note: expected type `[closure@src/main.rs:12:22: 12:33]`
              found type `std::option::Option<_>`

error[E0308]: mismatched types
  --> src/main.rs:17:5
   |
5  | fn problem() -> String {
   |                 ------ expected `std::string::String` because of return type
...
17 |     1
   |     ^
   |     |
   |     expected struct `std::string::String`, found integer
   |     help: try using a conversion method: `1.to_string()`
   |
   = note: expected type `std::string::String`
              found type `{integer}`

error: aborting due to 2 previous errors

|| parser::needs_par_as_let_scrutinee(scrutinee.precedence().order())
|| parser::needs_par_as_let_scrutinee(scrutinee.precedence().order())
// #51635: handle closures correctly, even though they have a really low
// precedence, they *don't* need parens for assignment 🤷.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heh; would be good to replace 🤷 with the actual reason if you have a sec to figure it out.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm waiting on insight from @petrochenkov, I believe the precedence order between let and closures is incorrectly, but I didn't want to affect all other relative precedences accidentally so I did this, but the underlying reason would require going over every Precedence and double checking them. Clearly the Parser disagrees with what we have encoded :-|

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The alternatives would of course be to either change the parser to conform to the encoded precedence (potentially breaking working code) or changing the precedence order (potentially breaking working code) :-|

@petrochenkov
Copy link
Contributor

I don't know right away whether this change is correct or not and don't have time to investigate.
r? @Centril who implemented let expressions including pretty-printing and has more context.

@rust-highfive rust-highfive assigned Centril and unassigned petrochenkov Sep 9, 2019
@Centril
Copy link
Contributor

Centril commented Sep 9, 2019

So the precedences here are a bit tricky. This may take some time to think through but I'll try to find some of that soon.

@Centril
Copy link
Contributor

Centril commented Sep 13, 2019

Update for wg-triage: I will set aside time to review this tomorrow or declare reviewer bankruptcy if it takes a lot more. :)

@estebank
Copy link
Contributor Author

@Centril did you get a chance to take a look at this?

@Centril
Copy link
Contributor

Centril commented Sep 17, 2019

Here's my investigation thus far:

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=1032dc3984e4673a1d4f3405c255342b

There seems to be more wrong with the current code than this PR intends to fix but it doesn't seem like the diff in the PR is the right fix either.

It could be that the parser is incorrect, but if not, then pprust should do something more like:

    /// Print a `let pat = scrutinee` expression.
    crate fn print_let(&mut self, pat: &ast::Pat, scrutinee: &ast::Expr) {
        self.s.word("let ");

        self.print_pat(pat);
        self.s.space();

        self.word_space("=");
        let order = scrutinee.precedence().order();
        self.print_expr_cond_paren(
            scrutinee,
            parser::contains_exterior_struct_lit(scrutinee)
            || parser::needs_par_as_let_scrutinee(order) && order > PREC_JUMP
        )
    }

I think we'll also need to add appropriate tests to the PR. The playground above might be reusable for that.

@Centril Centril added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Sep 17, 2019
Avoid adding unnecessary parentheses around closures in
`if let _ = || ()`.
@estebank
Copy link
Contributor Author

estebank commented Sep 19, 2019

@Centril the pretty printer actually has some of these weird parses correct:

    if let _ = (A{}) + A {
    } // `contains_exterior_struct_lit` says this should require parens!
    { }

The above gets interpreted as the addition of A {} and A assigned to _ in if let and with an empty body, followed by an empty block. Should we be catching cases like these in the parser? I believe we are reliant on the following passes failing, like for the above:

error[E0423]: expected value, found struct `A`
  --> src/main.rs:62:25
   |
62 |     if let _ = (A {}) + A {} {} // `contains_exterior_struct_lit` says this should require parens!
   |                         ^
help: a local variable with a similar name exists
   |
62 |     if let _ = (A {}) + x {} {} // `contains_exterior_struct_lit` says this should require parens!
   |                         ^
help: surround the struct literal with parenthesis
   |
62 |     if let _ = (A {}) + (A {}) {} // `contains_exterior_struct_lit` says this should require parens!
   |                         ^^^^^^
help: possible better candidates are found in other modules, you can import them into scope
   |
5  | use futures::future::Either::A;
   |
5  | use tokio::prelude::future::Either::A;

@Centril
Copy link
Contributor

Centril commented Sep 19, 2019

Should we be catching cases like these in the parser? I believe we are reliant on the following passes failing, like for the above:

I'm not sure. Technically it's a breaking change because of conditional compilation and macros to move the error to the parser. Also, I don't have an intuition for why this should and should not parse without a formally encoded grammar and wg-grammar is nowhere near the ability to make such assertions. cc @rust-lang/wg-grammar tho.

It's maybe best to only adjust pprust conservatively (in the way I showed above) for now and leave FIXMEs where appropriate?

@Centril
Copy link
Contributor

Centril commented Sep 19, 2019

@estebank Ideally we would add some tests also with FIXMEs where appropriate. =P

@JohnCSimon
Copy link
Member

Ping from triage.
@Centril @estebank This PR has sat idle for a week. Thanks!

@JohnCSimon JohnCSimon added S-inactive Status: Inactive and waiting on the author. This is often applied to closed PRs. and removed S-inactive Status: Inactive and waiting on the author. This is often applied to closed PRs. labels Oct 5, 2019
@JohnCSimon
Copy link
Member

JohnCSimon commented Oct 5, 2019

Hello from triage.
@estebank Closing this out due to inactivity - please feel free to reopen the PR when you have time to address the comments from @Centril

Thanks!

@JohnCSimon JohnCSimon closed this Oct 5, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-inactive Status: Inactive and waiting on the author. This is often applied to closed PRs. S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants