Skip to content
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

Fix last let_chains blocker #98633

Merged
merged 1 commit into from
Jul 12, 2022
Merged

Conversation

c410-f3r
Copy link
Contributor

@c410-f3r c410-f3r commented Jun 28, 2022

In order to forbid things like let x = (let y = 1); or if let a = 1 && { let x = let y = 1; } {}, the parser HAS to know the context of let.

This context thing is not a surprise in the parser because you can see a lot of ad hoc fixes mixing parsing logic with validation logic creating code that looks more like spaghetti with tomato sauce.

To make things even greater, a new ad hoc fix was added to only allow lets in a valid let_chains context by checking the previously processed token. This was the only solution I could think of and believe me, I thought about it for a long time 👍

In the long term, it should be preferable to segregate different responsibilities or create a more robust and cleaner parser framework.

cc #94927
cc #53667

@rustbot rustbot added the T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. label Jun 28, 2022
@rust-highfive
Copy link
Collaborator

r? @petrochenkov

(rust-highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jun 28, 2022
@c410-f3r c410-f3r force-pushed the yet-another-let-chain branch 2 times, most recently from 5ad965c to 16b3d40 Compare June 28, 2022 19:19
@c410-f3r
Copy link
Contributor Author

c410-f3r commented Jul 6, 2022

Look like @petrochenkov is not available right not.

Perhaps another reviewer? It would be a shame to miss yet another release.

r? @rust-lang/compiler

Comment on lines 2361 to 2374
// Catches things like `if let Some(_) = _opt && [1, 2, 3][let _ = ()] = 1`
let is_in_a_let_chains_context_but_nested_in_other_expr = self.let_expr_allowed
&& !matches!(
self.prev_token.kind,
TokenKind::AndAnd
| TokenKind::CloseDelim(Delimiter::Brace)
| TokenKind::Ident(kw::If, _)
| TokenKind::Ident(kw::While, _)
);
Copy link
Contributor

Choose a reason for hiding this comment

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

As you already mention, this is a bit of a hack, but seems safe enough (although I feel like it might also not trigger with things like let _ = &&let Some(x) = Some(42); where the intent might be &&bool, can we test that?).

Having said that, I'm wondering if you explored adding a visit_pat function to https://github.com/rust-lang/rust/blob/master/compiler/rustc_ast_passes/src/ast_validation.rs to catch the nested pattern cases (like parens around the let expression).

It feels like cases like
https://github.com/rust-lang/rust/pull/98633/files#diff-211c1d6e2f4ff5f2af12400a49c018c5ecebd9e0ec9e8a15049a9e55521692bbR366-R370
in line 368 ([][let () = ()]) should be able to be caught by the current code (but that test has that expression cfgd away, which is likely why it wasn't triggering?).

If the reason for this code is to trigger even in the face of cfgd code, and modifying the visitor to be more exhaustive isn't possible for good reason, then I'm ok with the code as is. It isn't great, but worst case scenario this is overly restrictive, which is better than the alternative.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As you already mention, this is a bit of a hack, but seems safe enough (although I feel like it might also not trigger with things like let _ = &&let Some(x) = Some(42); where the intent might be &&bool, can we test that?).

Added test in a let-chain context as well as a let statement.

Having said that, I'm wondering if you explored adding a visit_pat function to https://github.com/rust-lang/rust/blob/master/compiler/rustc_ast_passes/src/ast_validation.rs to catch the nested pattern cases (like parens around the let expression).

Nested parenthesis in the pattern? Like?

if let Some(value) = opt && let &[(1)] = value { ... }
if let Some(value) = opt && let Some((1)) = value { ... }

If so, they are currently denied.
Parenthesis in nested expressions are also forbidden.

if let Some(value) = opt && let Some(1) = {
    if let 1 = 1 && (let 1 = 1) {}
    value
} {}

/// A let chain with invalid parentheses
///
/// For exemple, `let 1 = 1 && (expr && expr)` is allowed
/// but `(let 1 = 1 && (let 1 = 1 && (let 1 = 1))) && let a = 1` is not
NotSupportedParentheses(Span),

It feels like cases like
https://github.com/rust-lang/rust/pull/98633/files#diff-211c1d6e2f4ff5f2af12400a49c018c5ecebd9e0ec9e8a15049a9e55521692bbR366-R370
in line 368 ([][let () = ()]) should be able to be caught by the current code (but that test has that expression cfgd away, which is likely why it wasn't triggering?).

Couldn't reach the link. Do you mean?

https://github.com/rust-lang/rust/blob/16b3d404d2267972d770e239c6e7ac68628f7a4a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs#L368

If so, then the invalid code is indeed caught and issued as expected expression, found 'let' statement. If cfg is removed, then ast_validation.rs also warns 'let' expressions are not supported here.

If the reason for this code is to trigger even in the face of cfgd code, and modifying the visitor to be more exhaustive isn't possible for good reason, then I'm ok with the code as is. It isn't great, but worst case scenario this is overly restrictive, which is better than the alternative.

Yes, every invalid let location is already handled in ast_validation.rs but people are worried about cfg usage thus the reason of this PR.

#94927 (comment)
#94927 (comment)

@c410-f3r c410-f3r force-pushed the yet-another-let-chain branch from 16b3d40 to 32a5417 Compare July 6, 2022 20:19
@c410-f3r c410-f3r force-pushed the yet-another-let-chain branch from 32a5417 to 9d2a9d9 Compare July 7, 2022 11:12
@estebank
Copy link
Contributor

estebank commented Jul 7, 2022

FWIW, a lot of the spaghetti code in the parser is mainly about error recovery, the underlying parser should always be "clean" and with strict short lookahead (we break these rules for error recovery, including with spooky action at a distance, because we're dealing with code that is already non-regular). In this case I can make the case that the "N=1 lookback" is just an implementation detail and the Grammar remains "N=1 lookahead". The only case that I am somewhat scared a about is }, but due to this being in the worst case overly restrictive, I think we can land this until we refactor this codepath.

@bors r+

@bors
Copy link
Contributor

bors commented Jul 7, 2022

📌 Commit 9d2a9d9 has been approved by estebank

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 7, 2022
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request Jul 8, 2022
…stebank

Fix last `let_chains` blocker

In order to forbid things like `let x = (let y = 1);` or `if let a = 1 && { let x = let y = 1; } {}`, the parser **HAS** to know the context of `let`.

This context thing is not a surprise in the parser because you can see **a lot** of ad hoc fixes mixing parsing logic with validation logic creating code that looks more like spaghetti with tomato sauce.

To make things even greater, a new ad hoc fix was added to only allow `let`s in a valid `let_chains` context by checking the previously processed token. This was the only solution I could think of and believe me, I thought about it for a long time 👍

In the long term, it should be preferable to segregate different responsibilities or create a more robust and cleaner parser framework.

cc rust-lang#94927
cc rust-lang#53667
Dylan-DPC added a commit to Dylan-DPC/rust that referenced this pull request Jul 8, 2022
…stebank

Fix last `let_chains` blocker

In order to forbid things like `let x = (let y = 1);` or `if let a = 1 && { let x = let y = 1; } {}`, the parser **HAS** to know the context of `let`.

This context thing is not a surprise in the parser because you can see **a lot** of ad hoc fixes mixing parsing logic with validation logic creating code that looks more like spaghetti with tomato sauce.

To make things even greater, a new ad hoc fix was added to only allow `let`s in a valid `let_chains` context by checking the previously processed token. This was the only solution I could think of and believe me, I thought about it for a long time 👍

In the long term, it should be preferable to segregate different responsibilities or create a more robust and cleaner parser framework.

cc rust-lang#94927
cc rust-lang#53667
@bors
Copy link
Contributor

bors commented Jul 8, 2022

☔ The latest upstream changes (presumably #98482) made this pull request unmergeable. Please resolve the merge conflicts.

@bors bors 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-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. labels Jul 8, 2022
@c410-f3r c410-f3r force-pushed the yet-another-let-chain branch from 9d2a9d9 to 1c3bab2 Compare July 8, 2022 10:26
@c410-f3r
Copy link
Contributor Author

c410-f3r commented Jul 9, 2022

@rustbot label +S-waiting-on-review -S-waiting-on-author

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. and removed S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Jul 9, 2022
@c410-f3r
Copy link
Contributor Author

c410-f3r commented Jul 11, 2022

Further work on reference documentation depends on this PR that was already re-based three days ago due to code changes.

It is already approved so I ask anyone to r+ again to unblock let_chains.

@tmiasko
Copy link
Contributor

tmiasko commented Jul 11, 2022

@bors r=estebank

@bors
Copy link
Contributor

bors commented Jul 11, 2022

📌 Commit 1c3bab2 has been approved by estebank

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 11, 2022
@c410-f3r
Copy link
Contributor Author

Thank you

Dylan-DPC added a commit to Dylan-DPC/rust that referenced this pull request Jul 12, 2022
…stebank

Fix last `let_chains` blocker

In order to forbid things like `let x = (let y = 1);` or `if let a = 1 && { let x = let y = 1; } {}`, the parser **HAS** to know the context of `let`.

This context thing is not a surprise in the parser because you can see **a lot** of ad hoc fixes mixing parsing logic with validation logic creating code that looks more like spaghetti with tomato sauce.

To make things even greater, a new ad hoc fix was added to only allow `let`s in a valid `let_chains` context by checking the previously processed token. This was the only solution I could think of and believe me, I thought about it for a long time 👍

In the long term, it should be preferable to segregate different responsibilities or create a more robust and cleaner parser framework.

cc rust-lang#94927
cc rust-lang#53667
Dylan-DPC added a commit to Dylan-DPC/rust that referenced this pull request Jul 12, 2022
…stebank

Fix last `let_chains` blocker

In order to forbid things like `let x = (let y = 1);` or `if let a = 1 && { let x = let y = 1; } {}`, the parser **HAS** to know the context of `let`.

This context thing is not a surprise in the parser because you can see **a lot** of ad hoc fixes mixing parsing logic with validation logic creating code that looks more like spaghetti with tomato sauce.

To make things even greater, a new ad hoc fix was added to only allow `let`s in a valid `let_chains` context by checking the previously processed token. This was the only solution I could think of and believe me, I thought about it for a long time 👍

In the long term, it should be preferable to segregate different responsibilities or create a more robust and cleaner parser framework.

cc rust-lang#94927
cc rust-lang#53667
bors added a commit to rust-lang-ci/rust that referenced this pull request Jul 12, 2022
Rollup of 6 pull requests

Successful merges:

 - rust-lang#98622 (rustc_target: Flip the default for `TargetOptions::executables` to true)
 - rust-lang#98633 (Fix last `let_chains` blocker)
 - rust-lang#98972 (Suggest adding a missing zero to a floating point number)
 - rust-lang#99038 (Some more `EarlyBinder` cleanups)
 - rust-lang#99154 (use PlaceRef::iter_projections to fix old FIXME)
 - rust-lang#99171 (Put back UI test regex)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit 9997c51 into rust-lang:master Jul 12, 2022
@rustbot rustbot added this to the 1.64.0 milestone Jul 12, 2022
bors added a commit to rust-lang-ci/rust that referenced this pull request Jul 27, 2022
…iler-errors

Remove let-chain close brace check.

rust-lang#98633 added some checks to forbid let-expressions that aren't in a let chain. This check looks at the preceding token to determine if it is a valid let-chain position. One of those tokens it checks is the close brace `}`. However, to my understanding, it is not possible for a let chain to be preceded by a close brace. This PR removes the check to avoid any confusion.

This is a followup to the discussion at rust-lang#98633 (review). It wasn't clear what issues the original PR ran into, but I have run the full set of CI tests and nothing failed.  I also can't conceive of a situation where this would be possible.  This doesn't reject any valid code, I'm just removing it to avoid confusion to anyone looking at this code in the future.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants