-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Inline ExprPrecedence::order into Expr::precedence #133140
Merged
Merged
+146
−225
Conversation
This file contains 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
rustbot
added
S-waiting-on-review
Status: Awaiting review from the assignee but also interested parties.
T-compiler
Relevant to the compiler team, which will review and decide on the PR/issue.
labels
Nov 17, 2024
dtolnay
added
A-pretty
Area: Pretty printing (including `-Z unpretty`)
A-parser
Area: The parsing of Rust source code to an AST
labels
Nov 17, 2024
This comment has been minimized.
This comment has been minimized.
Some changes occurred in src/tools/clippy cc @rust-lang/clippy |
fmease
approved these changes
Nov 25, 2024
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes sense, thank you! Looking forward to all the follow-up PRs :)
@bors r+ rollup (refactoring) |
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
Nov 25, 2024
jhpratt
added a commit
to jhpratt/rust
that referenced
this pull request
Nov 26, 2024
Inline ExprPrecedence::order into Expr::precedence The representation of expression precedence in rustc_ast has been an obstacle to further improvements in the pretty-printer (continuing from rust-lang#119105 and rust-lang#119427). Previously the operation of *"does this expression have lower precedence than that one"* (relevant for parenthesis insertion in macro-generated syntax trees) consisted of 3 steps: 1. Convert `Expr` to `ExprPrecedence` using `.precedence()` 2. Convert `ExprPrecedence` to `i8` using `.order()` 3. Compare using `<` As far as I can guess, the reason for the separation between `precedence()` and `order()` was so that both `rustc_ast::Expr` and `rustc_hir::Expr` could convert as straightforwardly as possible to the same `ExprPrecedence` enum, and then the more finicky logic performed by `order` could be present just once. The mapping between `Expr` and `ExprPrecedence` was intended to be as straightforward as possible: ```rust match self.kind { ExprKind::Closure(..) => ExprPrecedence::Closure, ... } ``` although there were exceptions of both many-to-one, and one-to-many: ```rust ExprKind::Underscore => ExprPrecedence::Path, ExprKind::Path(..) => ExprPrecedence::Path, ... ExprKind::Match(_, _, MatchKind::Prefix) => ExprPrecedence::Match, ExprKind::Match(_, _, MatchKind::Postfix) => ExprPrecedence::PostfixMatch, ``` Where the nature of `ExprPrecedence` becomes problematic is when a single expression kind might be associated with multiple different precedence levels depending on context (outside the expression) and contents (inside the expression). For example consider what is the precedence of an ExprKind::Closure `$closure`. Well, on the left-hand side of a binary operator it would need parentheses in order to avoid the trailing binary operator being absorbed into the closure body: `($closure) + Rhs`, so the precedence is something lower than that of `+`. But on the right-hand side of a binary operator, a closure is just a straightforward prefix expression like a unary op, which is a relatively high precedence level, higher than binops but lower than method calls: `Lhs + $closure` is fine without parens but `($closure).method()` needs them. But as a third case, if the closure contains an explicit return type, then the precedence is an even higher level than that, never needing parenthesization even in a binop left-hand side or method call: `|| -> bool { false } + Rhs` or `|| -> bool { false }.method()`. You can see that trying to capture all of this resolution about expressions into `ExprPrecedence` violates the intention of `ExprPrecedence` being a straightforward one-to-one correspondence from each AST and HIR `ExprKind` variant. It would be possible to attempt that by doing stuff like `ExprPrecedence::Closure(Side::Leading, ReturnType::No)`, but I don't foresee the original envisioned benefit of the `precedence()`/`order()` distinction being retained in this approach. Instead I want to move toward a model that Syn has been using successfully. In Syn, there is a Precedence enum but it differs from rustc in the following ways: - There are [relatively few variants](https://github.com/dtolnay/syn/blob/2.0.87/src/precedence.rs#L11-L47) compared to rustc's `ExprPrecedence`. For example there is no distinction at the precedence level between returns and closures, or between loops and method calls. - We distinguish between [leading](https://github.com/dtolnay/syn/blob/2.0.87/src/fixup.rs#L293) and [trailing](https://github.com/dtolnay/syn/blob/2.0.87/src/fixup.rs#L309) precedence, taking into account an expression's context such as what token follows it (for various syntactic bail-outs in Rust's grammar, like ambiguities around break-with-value) and how it relates to operators from the surrounding syntax tree. - There are no hardcoded mysterious integer quantities like rustc's `PREC_CLOSURE = -40`. All precedence comparisons are performed via PartialOrd on a C-like enum. This PR is just a first step in these changes. As you can tell from Syn, I definitely think there is value in having a dedicated type to represent precedence, instead of what `order()` is doing with `i8`. But that is a whole separate adventure because rustc_ast doesn't even agree consistently on `i8` being the type for precedence order; `AssocOp::precedence` instead uses `usize` and there are casts in both directions. It is likely that a type called `ExprPrecedence` will re-appear, but it will look substantially different from the one that existed before this PR.
bors
added a commit
to rust-lang-ci/rust
that referenced
this pull request
Nov 26, 2024
Rollup of 28 pull requests Successful merges: - rust-lang#132605 (CI: increase timeout from 4h to 6h) - rust-lang#133042 (btree: add `{Entry,VacantEntry}::insert_entry`) - rust-lang#133070 (Lexer tweaks) - rust-lang#133136 (Support ranges in `<[T]>::get_many_mut()`) - rust-lang#133140 (Inline ExprPrecedence::order into Expr::precedence) - rust-lang#133248 (CI: split x86_64-msvc-ext job) - rust-lang#133282 (Shorten the `MaybeUninit` `Debug` implementation) - rust-lang#133304 (Revert diagnostics hack to fix ICE 132920) - rust-lang#133326 (Remove the `DefinitelyInitializedPlaces` analysis.) - rust-lang#133362 (No need to re-sort existential preds in relate impl) - rust-lang#133367 (Simplify array length mismatch error reporting (to not try to turn consts into target usizes)) - rust-lang#133394 (Bail on more errors in dyn ty lowering) - rust-lang#133410 (target check_consistency: ensure target feature string makes some basic sense) - rust-lang#133411 (the emscripten OS no longer exists on non-wasm targets) - rust-lang#133419 (Added a doc test for std::path::strip_prefix) - rust-lang#133430 (Tweak parameter mismatch explanation to not say `{unknown}`) - rust-lang#133435 (miri: disable test_downgrade_observe test on macOS) - rust-lang#133443 (Remove dead code stemming from the old effects desugaring (II)) - rust-lang#133449 (std: expose `const_io_error!` as `const_error!`) - rust-lang#133450 (remove "onur-ozkan" from users_on_vacation) - rust-lang#133454 (Update test expectations to accept LLVM 'initializes' attribute) - rust-lang#133458 (Fix `Result` and `Option` not getting a jump to def link generated) - rust-lang#133462 (Use ReadCache for archive reading in bootstrap) - rust-lang#133464 (std::thread: avoid leading whitespace in some panic messages) - rust-lang#133467 (tests: Add recursive associated type bound regression tests) - rust-lang#133470 (Cleanup: delete `//@ pretty-expanded` directive) - rust-lang#133473 (tests: Add regression test for recursive enum with Cow and Clone) - rust-lang#133481 (Disable `avr-rjmp-offset` on Windows for now) r? `@ghost` `@rustbot` modify labels: rollup
bors
added a commit
to rust-lang-ci/rust
that referenced
this pull request
Nov 26, 2024
Rollup of 28 pull requests Successful merges: - rust-lang#132605 (CI: increase timeout from 4h to 6h) - rust-lang#133042 (btree: add `{Entry,VacantEntry}::insert_entry`) - rust-lang#133070 (Lexer tweaks) - rust-lang#133136 (Support ranges in `<[T]>::get_many_mut()`) - rust-lang#133140 (Inline ExprPrecedence::order into Expr::precedence) - rust-lang#133248 (CI: split x86_64-msvc-ext job) - rust-lang#133282 (Shorten the `MaybeUninit` `Debug` implementation) - rust-lang#133304 (Revert diagnostics hack to fix ICE 132920) - rust-lang#133326 (Remove the `DefinitelyInitializedPlaces` analysis.) - rust-lang#133362 (No need to re-sort existential preds in relate impl) - rust-lang#133367 (Simplify array length mismatch error reporting (to not try to turn consts into target usizes)) - rust-lang#133394 (Bail on more errors in dyn ty lowering) - rust-lang#133410 (target check_consistency: ensure target feature string makes some basic sense) - rust-lang#133411 (the emscripten OS no longer exists on non-wasm targets) - rust-lang#133419 (Added a doc test for std::path::strip_prefix) - rust-lang#133430 (Tweak parameter mismatch explanation to not say `{unknown}`) - rust-lang#133435 (miri: disable test_downgrade_observe test on macOS) - rust-lang#133443 (Remove dead code stemming from the old effects desugaring (II)) - rust-lang#133449 (std: expose `const_io_error!` as `const_error!`) - rust-lang#133450 (remove "onur-ozkan" from users_on_vacation) - rust-lang#133454 (Update test expectations to accept LLVM 'initializes' attribute) - rust-lang#133458 (Fix `Result` and `Option` not getting a jump to def link generated) - rust-lang#133462 (Use ReadCache for archive reading in bootstrap) - rust-lang#133464 (std::thread: avoid leading whitespace in some panic messages) - rust-lang#133467 (tests: Add recursive associated type bound regression tests) - rust-lang#133470 (Cleanup: delete `//@ pretty-expanded` directive) - rust-lang#133473 (tests: Add regression test for recursive enum with Cow and Clone) - rust-lang#133481 (Disable `avr-rjmp-offset` on Windows for now) r? `@ghost` `@rustbot` modify labels: rollup
bors
added a commit
to rust-lang-ci/rust
that referenced
this pull request
Nov 26, 2024
Rollup of 28 pull requests Successful merges: - rust-lang#132605 (CI: increase timeout from 4h to 6h) - rust-lang#133042 (btree: add `{Entry,VacantEntry}::insert_entry`) - rust-lang#133070 (Lexer tweaks) - rust-lang#133136 (Support ranges in `<[T]>::get_many_mut()`) - rust-lang#133140 (Inline ExprPrecedence::order into Expr::precedence) - rust-lang#133248 (CI: split x86_64-msvc-ext job) - rust-lang#133282 (Shorten the `MaybeUninit` `Debug` implementation) - rust-lang#133304 (Revert diagnostics hack to fix ICE 132920) - rust-lang#133326 (Remove the `DefinitelyInitializedPlaces` analysis.) - rust-lang#133362 (No need to re-sort existential preds in relate impl) - rust-lang#133367 (Simplify array length mismatch error reporting (to not try to turn consts into target usizes)) - rust-lang#133394 (Bail on more errors in dyn ty lowering) - rust-lang#133410 (target check_consistency: ensure target feature string makes some basic sense) - rust-lang#133411 (the emscripten OS no longer exists on non-wasm targets) - rust-lang#133419 (Added a doc test for std::path::strip_prefix) - rust-lang#133430 (Tweak parameter mismatch explanation to not say `{unknown}`) - rust-lang#133435 (miri: disable test_downgrade_observe test on macOS) - rust-lang#133443 (Remove dead code stemming from the old effects desugaring (II)) - rust-lang#133449 (std: expose `const_io_error!` as `const_error!`) - rust-lang#133450 (remove "onur-ozkan" from users_on_vacation) - rust-lang#133454 (Update test expectations to accept LLVM 'initializes' attribute) - rust-lang#133458 (Fix `Result` and `Option` not getting a jump to def link generated) - rust-lang#133462 (Use ReadCache for archive reading in bootstrap) - rust-lang#133464 (std::thread: avoid leading whitespace in some panic messages) - rust-lang#133467 (tests: Add recursive associated type bound regression tests) - rust-lang#133470 (Cleanup: delete `//@ pretty-expanded` directive) - rust-lang#133473 (tests: Add regression test for recursive enum with Cow and Clone) - rust-lang#133481 (Disable `avr-rjmp-offset` on Windows for now) r? `@ghost` `@rustbot` modify labels: rollup
bors
added a commit
to rust-lang-ci/rust
that referenced
this pull request
Nov 26, 2024
…mpiler-errors Rollup of 12 pull requests Successful merges: - rust-lang#133042 (btree: add `{Entry,VacantEntry}::insert_entry`) - rust-lang#133070 (Lexer tweaks) - rust-lang#133136 (Support ranges in `<[T]>::get_many_mut()`) - rust-lang#133140 (Inline ExprPrecedence::order into Expr::precedence) - rust-lang#133155 (Yet more `rustc_mir_dataflow` cleanups) - rust-lang#133282 (Shorten the `MaybeUninit` `Debug` implementation) - rust-lang#133326 (Remove the `DefinitelyInitializedPlaces` analysis.) - rust-lang#133362 (No need to re-sort existential preds in relate impl) - rust-lang#133367 (Simplify array length mismatch error reporting (to not try to turn consts into target usizes)) - rust-lang#133394 (Bail on more errors in dyn ty lowering) - rust-lang#133410 (target check_consistency: ensure target feature string makes some basic sense) - rust-lang#133435 (miri: disable test_downgrade_observe test on macOS) r? `@ghost` `@rustbot` modify labels: rollup
rust-timer
added a commit
to rust-lang-ci/rust
that referenced
this pull request
Nov 27, 2024
Rollup merge of rust-lang#133140 - dtolnay:precedence, r=fmease Inline ExprPrecedence::order into Expr::precedence The representation of expression precedence in rustc_ast has been an obstacle to further improvements in the pretty-printer (continuing from rust-lang#119105 and rust-lang#119427). Previously the operation of *"does this expression have lower precedence than that one"* (relevant for parenthesis insertion in macro-generated syntax trees) consisted of 3 steps: 1. Convert `Expr` to `ExprPrecedence` using `.precedence()` 2. Convert `ExprPrecedence` to `i8` using `.order()` 3. Compare using `<` As far as I can guess, the reason for the separation between `precedence()` and `order()` was so that both `rustc_ast::Expr` and `rustc_hir::Expr` could convert as straightforwardly as possible to the same `ExprPrecedence` enum, and then the more finicky logic performed by `order` could be present just once. The mapping between `Expr` and `ExprPrecedence` was intended to be as straightforward as possible: ```rust match self.kind { ExprKind::Closure(..) => ExprPrecedence::Closure, ... } ``` although there were exceptions of both many-to-one, and one-to-many: ```rust ExprKind::Underscore => ExprPrecedence::Path, ExprKind::Path(..) => ExprPrecedence::Path, ... ExprKind::Match(_, _, MatchKind::Prefix) => ExprPrecedence::Match, ExprKind::Match(_, _, MatchKind::Postfix) => ExprPrecedence::PostfixMatch, ``` Where the nature of `ExprPrecedence` becomes problematic is when a single expression kind might be associated with multiple different precedence levels depending on context (outside the expression) and contents (inside the expression). For example consider what is the precedence of an ExprKind::Closure `$closure`. Well, on the left-hand side of a binary operator it would need parentheses in order to avoid the trailing binary operator being absorbed into the closure body: `($closure) + Rhs`, so the precedence is something lower than that of `+`. But on the right-hand side of a binary operator, a closure is just a straightforward prefix expression like a unary op, which is a relatively high precedence level, higher than binops but lower than method calls: `Lhs + $closure` is fine without parens but `($closure).method()` needs them. But as a third case, if the closure contains an explicit return type, then the precedence is an even higher level than that, never needing parenthesization even in a binop left-hand side or method call: `|| -> bool { false } + Rhs` or `|| -> bool { false }.method()`. You can see that trying to capture all of this resolution about expressions into `ExprPrecedence` violates the intention of `ExprPrecedence` being a straightforward one-to-one correspondence from each AST and HIR `ExprKind` variant. It would be possible to attempt that by doing stuff like `ExprPrecedence::Closure(Side::Leading, ReturnType::No)`, but I don't foresee the original envisioned benefit of the `precedence()`/`order()` distinction being retained in this approach. Instead I want to move toward a model that Syn has been using successfully. In Syn, there is a Precedence enum but it differs from rustc in the following ways: - There are [relatively few variants](https://github.com/dtolnay/syn/blob/2.0.87/src/precedence.rs#L11-L47) compared to rustc's `ExprPrecedence`. For example there is no distinction at the precedence level between returns and closures, or between loops and method calls. - We distinguish between [leading](https://github.com/dtolnay/syn/blob/2.0.87/src/fixup.rs#L293) and [trailing](https://github.com/dtolnay/syn/blob/2.0.87/src/fixup.rs#L309) precedence, taking into account an expression's context such as what token follows it (for various syntactic bail-outs in Rust's grammar, like ambiguities around break-with-value) and how it relates to operators from the surrounding syntax tree. - There are no hardcoded mysterious integer quantities like rustc's `PREC_CLOSURE = -40`. All precedence comparisons are performed via PartialOrd on a C-like enum. This PR is just a first step in these changes. As you can tell from Syn, I definitely think there is value in having a dedicated type to represent precedence, instead of what `order()` is doing with `i8`. But that is a whole separate adventure because rustc_ast doesn't even agree consistently on `i8` being the type for precedence order; `AssocOp::precedence` instead uses `usize` and there are casts in both directions. It is likely that a type called `ExprPrecedence` will re-appear, but it will look substantially different from the one that existed before this PR.
flip1995
pushed a commit
to flip1995/rust
that referenced
this pull request
Nov 28, 2024
Inline ExprPrecedence::order into Expr::precedence The representation of expression precedence in rustc_ast has been an obstacle to further improvements in the pretty-printer (continuing from rust-lang#119105 and rust-lang#119427). Previously the operation of *"does this expression have lower precedence than that one"* (relevant for parenthesis insertion in macro-generated syntax trees) consisted of 3 steps: 1. Convert `Expr` to `ExprPrecedence` using `.precedence()` 2. Convert `ExprPrecedence` to `i8` using `.order()` 3. Compare using `<` As far as I can guess, the reason for the separation between `precedence()` and `order()` was so that both `rustc_ast::Expr` and `rustc_hir::Expr` could convert as straightforwardly as possible to the same `ExprPrecedence` enum, and then the more finicky logic performed by `order` could be present just once. The mapping between `Expr` and `ExprPrecedence` was intended to be as straightforward as possible: ```rust match self.kind { ExprKind::Closure(..) => ExprPrecedence::Closure, ... } ``` although there were exceptions of both many-to-one, and one-to-many: ```rust ExprKind::Underscore => ExprPrecedence::Path, ExprKind::Path(..) => ExprPrecedence::Path, ... ExprKind::Match(_, _, MatchKind::Prefix) => ExprPrecedence::Match, ExprKind::Match(_, _, MatchKind::Postfix) => ExprPrecedence::PostfixMatch, ``` Where the nature of `ExprPrecedence` becomes problematic is when a single expression kind might be associated with multiple different precedence levels depending on context (outside the expression) and contents (inside the expression). For example consider what is the precedence of an ExprKind::Closure `$closure`. Well, on the left-hand side of a binary operator it would need parentheses in order to avoid the trailing binary operator being absorbed into the closure body: `($closure) + Rhs`, so the precedence is something lower than that of `+`. But on the right-hand side of a binary operator, a closure is just a straightforward prefix expression like a unary op, which is a relatively high precedence level, higher than binops but lower than method calls: `Lhs + $closure` is fine without parens but `($closure).method()` needs them. But as a third case, if the closure contains an explicit return type, then the precedence is an even higher level than that, never needing parenthesization even in a binop left-hand side or method call: `|| -> bool { false } + Rhs` or `|| -> bool { false }.method()`. You can see that trying to capture all of this resolution about expressions into `ExprPrecedence` violates the intention of `ExprPrecedence` being a straightforward one-to-one correspondence from each AST and HIR `ExprKind` variant. It would be possible to attempt that by doing stuff like `ExprPrecedence::Closure(Side::Leading, ReturnType::No)`, but I don't foresee the original envisioned benefit of the `precedence()`/`order()` distinction being retained in this approach. Instead I want to move toward a model that Syn has been using successfully. In Syn, there is a Precedence enum but it differs from rustc in the following ways: - There are [relatively few variants](https://github.com/dtolnay/syn/blob/2.0.87/src/precedence.rs#L11-L47) compared to rustc's `ExprPrecedence`. For example there is no distinction at the precedence level between returns and closures, or between loops and method calls. - We distinguish between [leading](https://github.com/dtolnay/syn/blob/2.0.87/src/fixup.rs#L293) and [trailing](https://github.com/dtolnay/syn/blob/2.0.87/src/fixup.rs#L309) precedence, taking into account an expression's context such as what token follows it (for various syntactic bail-outs in Rust's grammar, like ambiguities around break-with-value) and how it relates to operators from the surrounding syntax tree. - There are no hardcoded mysterious integer quantities like rustc's `PREC_CLOSURE = -40`. All precedence comparisons are performed via PartialOrd on a C-like enum. This PR is just a first step in these changes. As you can tell from Syn, I definitely think there is value in having a dedicated type to represent precedence, instead of what `order()` is doing with `i8`. But that is a whole separate adventure because rustc_ast doesn't even agree consistently on `i8` being the type for precedence order; `AssocOp::precedence` instead uses `usize` and there are casts in both directions. It is likely that a type called `ExprPrecedence` will re-appear, but it will look substantially different from the one that existed before this PR.
GuillaumeGomez
added a commit
to GuillaumeGomez/rust
that referenced
this pull request
Dec 2, 2024
Eliminate magic numbers from expression precedence Context: see rust-lang#133140. This PR continues on backporting Syn's expression precedence design into rustc. Rustc's design used mysterious integer quantities represented variously as `i8` or `usize` (e.g. `PREC_CLOSURE = -40i8`), a special significance around `0` that is never named, and an extra `PREC_FORCE_PAREN` precedence level that does not correspond to any expression. Syn's design uses a C-like enum with variants that clearly correspond to specific sets of expression kinds. This PR is a refactoring that has no intended behavior change on its own, but it unblocks other precedence work that rustc's precedence design was poorly suited to accommodate. - Asymmetrical precedence, so that a pretty-printer can tell `(return 1) + 1` needs parens but `1 + return 1` does not. - Squashing the `Closure` and `Jump` cases into a single precedence level. - Numerous remaining false positives and false negatives in rustc pretty-printer's parenthesization of macro metavariables, for example in `$e < rhs` where $e is `lhs as Thing<T>`. FYI `@fmease` — you don't need to review if rustbot picks someone else, but you mentioned being interested in the followup PRs.
rust-timer
added a commit
to rust-lang-ci/rust
that referenced
this pull request
Dec 2, 2024
Rollup merge of rust-lang#133603 - dtolnay:precedence, r=lcnr Eliminate magic numbers from expression precedence Context: see rust-lang#133140. This PR continues on backporting Syn's expression precedence design into rustc. Rustc's design used mysterious integer quantities represented variously as `i8` or `usize` (e.g. `PREC_CLOSURE = -40i8`), a special significance around `0` that is never named, and an extra `PREC_FORCE_PAREN` precedence level that does not correspond to any expression. Syn's design uses a C-like enum with variants that clearly correspond to specific sets of expression kinds. This PR is a refactoring that has no intended behavior change on its own, but it unblocks other precedence work that rustc's precedence design was poorly suited to accommodate. - Asymmetrical precedence, so that a pretty-printer can tell `(return 1) + 1` needs parens but `1 + return 1` does not. - Squashing the `Closure` and `Jump` cases into a single precedence level. - Numerous remaining false positives and false negatives in rustc pretty-printer's parenthesization of macro metavariables, for example in `$e < rhs` where $e is `lhs as Thing<T>`. FYI `@fmease` — you don't need to review if rustbot picks someone else, but you mentioned being interested in the followup PRs.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Labels
A-parser
Area: The parsing of Rust source code to an AST
A-pretty
Area: Pretty printing (including `-Z unpretty`)
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.
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.
The representation of expression precedence in rustc_ast has been an obstacle to further improvements in the pretty-printer (continuing from #119105 and #119427).
Previously the operation of "does this expression have lower precedence than that one" (relevant for parenthesis insertion in macro-generated syntax trees) consisted of 3 steps:
Expr
toExprPrecedence
using.precedence()
ExprPrecedence
toi8
using.order()
<
As far as I can guess, the reason for the separation between
precedence()
andorder()
was so that bothrustc_ast::Expr
andrustc_hir::Expr
could convert as straightforwardly as possible to the sameExprPrecedence
enum, and then the more finicky logic performed byorder
could be present just once.The mapping between
Expr
andExprPrecedence
was intended to be as straightforward as possible:although there were exceptions of both many-to-one, and one-to-many:
Where the nature of
ExprPrecedence
becomes problematic is when a single expression kind might be associated with multiple different precedence levels depending on context (outside the expression) and contents (inside the expression). For example consider what is the precedence of an ExprKind::Closure$closure
. Well, on the left-hand side of a binary operator it would need parentheses in order to avoid the trailing binary operator being absorbed into the closure body:($closure) + Rhs
, so the precedence is something lower than that of+
. But on the right-hand side of a binary operator, a closure is just a straightforward prefix expression like a unary op, which is a relatively high precedence level, higher than binops but lower than method calls:Lhs + $closure
is fine without parens but($closure).method()
needs them. But as a third case, if the closure contains an explicit return type, then the precedence is an even higher level than that, never needing parenthesization even in a binop left-hand side or method call:|| -> bool { false } + Rhs
or|| -> bool { false }.method()
.You can see that trying to capture all of this resolution about expressions into
ExprPrecedence
violates the intention ofExprPrecedence
being a straightforward one-to-one correspondence from each AST and HIRExprKind
variant. It would be possible to attempt that by doing stuff likeExprPrecedence::Closure(Side::Leading, ReturnType::No)
, but I don't foresee the original envisioned benefit of theprecedence()
/order()
distinction being retained in this approach. Instead I want to move toward a model that Syn has been using successfully. In Syn, there is a Precedence enum but it differs from rustc in the following ways:There are relatively few variants compared to rustc's
ExprPrecedence
. For example there is no distinction at the precedence level between returns and closures, or between loops and method calls.We distinguish between leading and trailing precedence, taking into account an expression's context such as what token follows it (for various syntactic bail-outs in Rust's grammar, like ambiguities around break-with-value) and how it relates to operators from the surrounding syntax tree.
There are no hardcoded mysterious integer quantities like rustc's
PREC_CLOSURE = -40
. All precedence comparisons are performed via PartialOrd on a C-like enum.This PR is just a first step in these changes. As you can tell from Syn, I definitely think there is value in having a dedicated type to represent precedence, instead of what
order()
is doing withi8
. But that is a whole separate adventure because rustc_ast doesn't even agree consistently oni8
being the type for precedence order;AssocOp::precedence
instead usesusize
and there are casts in both directions. It is likely that a type calledExprPrecedence
will re-appear, but it will look substantially different from the one that existed before this PR.