Precedence improvements: closures and jumps#133782
Merged
bors merged 5 commits intorust-lang:masterfrom Dec 21, 2024
Merged
Conversation
Collaborator
|
r? @spastorino rustbot has assigned @spastorino. Use |
spastorino
approved these changes
Dec 20, 2024
Member
|
Sorry that it took very long for me to get into this. I think this is good and makes sense in general and the code looks perfect. |
Member
Author
Contributor
|
This all sounds right to me. Agreed there's nothing lang needs to rule on here (but it is interesting work, so cc @rust-lang/lang). |
Contributor
|
@bors r=spastorino,traviscross rollup |
Collaborator
bors
added a commit
to rust-lang-ci/rust
that referenced
this pull request
Dec 21, 2024
…iaskrgr Rollup of 7 pull requests Successful merges: - rust-lang#123604 (Abstract `ProcThreadAttributeList` into its own struct) - rust-lang#128780 (Add `--doctest-compilation-args` option to add compilation flags to doctest compilation) - rust-lang#133782 (Precedence improvements: closures and jumps) - rust-lang#134509 (Arbitrary self types v2: niche deshadowing test) - rust-lang#134524 (Arbitrary self types v2: no deshadow pre feature.) - rust-lang#134539 (Restrict `#[non_exaustive]` on structs with default field values) - rust-lang#134586 (Also lint on option of function pointer comparisons) r? `@ghost` `@rustbot` modify labels: rollup
rust-timer
added a commit
to rust-lang-ci/rust
that referenced
this pull request
Dec 21, 2024
Rollup merge of rust-lang#133782 - dtolnay:closuresjumps, r=spastorino,traviscross Precedence improvements: closures and jumps This PR fixes some cases where rustc's pretty printers would redundantly parenthesize expressions that didn't need it. <table> <tr><th>Before</th><th>After</th></tr> <tr><td><code>return (|x: i32| x)</code></td><td><code>return |x: i32| x</code></td></tr> <tr><td><code>(|| -> &mut () { std::process::abort() }).clone()</code></td><td><code>|| -> &mut () { std::process::abort() }.clone()</code></td></tr> <tr><td><code>(continue) + 1</code></td><td><code>continue + 1</code></td></tr> </table> Tested by `echo "fn main() { let _ = $AFTER; }" | rustc -Zunpretty=expanded /dev/stdin`. The pretty-printer aims to render the syntax tree as it actually exists in rustc, as faithfully as possible, in Rust syntax. It can insert parentheses where forced by Rust's grammar in order to preserve the meaning of a macro-generated syntax tree, for example in the case of `a * $rhs` where $rhs is `b + c`. But for any expression parsed from source code, without a macro involved, there should never be a reason for inserting additional parentheses not present in the original. For closures and jumps (return, break, continue, yield, do yeet, become) the unneeded parentheses came from the precedence of some of these expressions being misidentified. In the same order as the table above: - Jumps and closures are supposed to have equal precedence. The [Rust Reference](https://doc.rust-lang.org/1.83.0/reference/expressions.html#expression-precedence) says so, and in Syn they do. There is no Rust syntax that would require making a precedence distinction between jumps and closures. But in rustc these were previously 2 distinct levels with the closure being lower, hence the parentheses around a closure inside a jump (but not a jump inside a closure). - When a closure is written with an explicit return type, the grammar [requires](https://doc.rust-lang.org/1.83.0/reference/expressions/closure-expr.html) that the closure body consists of exactly one block expression, not any other arbitrary expression as usual for closures. Parsing of the closure body does not continue after the block expression. So in `|| { 0 }.clone()` the clone is inside the closure body and applies to `{ 0 }`, whereas in `|| -> _ { 0 }.clone()` the clone is outside and applies to the closure as a whole. - Continue never needs parentheses. It was previously marked as having the lowest possible precedence but it should have been the highest, next to paths and loops and function calls, not next to jumps.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR fixes some cases where rustc's pretty printers would redundantly parenthesize expressions that didn't need it.
return (|x: i32| x)return |x: i32| x(|| -> &mut () { std::process::abort() }).clone()|| -> &mut () { std::process::abort() }.clone()(continue) + 1continue + 1Tested by
echo "fn main() { let _ = $AFTER; }" | rustc -Zunpretty=expanded /dev/stdin.The pretty-printer aims to render the syntax tree as it actually exists in rustc, as faithfully as possible, in Rust syntax. It can insert parentheses where forced by Rust's grammar in order to preserve the meaning of a macro-generated syntax tree, for example in the case of
a * $rhswhere $rhs isb + c. But for any expression parsed from source code, without a macro involved, there should never be a reason for inserting additional parentheses not present in the original.For closures and jumps (return, break, continue, yield, do yeet, become) the unneeded parentheses came from the precedence of some of these expressions being misidentified. In the same order as the table above:
Jumps and closures are supposed to have equal precedence. The Rust Reference says so, and in Syn they do. There is no Rust syntax that would require making a precedence distinction between jumps and closures. But in rustc these were previously 2 distinct levels with the closure being lower, hence the parentheses around a closure inside a jump (but not a jump inside a closure).
When a closure is written with an explicit return type, the grammar requires that the closure body consists of exactly one block expression, not any other arbitrary expression as usual for closures. Parsing of the closure body does not continue after the block expression. So in
|| { 0 }.clone()the clone is inside the closure body and applies to{ 0 }, whereas in|| -> _ { 0 }.clone()the clone is outside and applies to the closure as a whole.Continue never needs parentheses. It was previously marked as having the lowest possible precedence but it should have been the highest, next to paths and loops and function calls, not next to jumps.