-
Notifications
You must be signed in to change notification settings - Fork 13.6k
minimally implement is
(RFC 3573), sans parsing
#144174
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
base: master
Are you sure you want to change the base?
Conversation
|
Some changes occurred in src/tools/clippy cc @rust-lang/clippy |
This comment has been minimized.
This comment has been minimized.
Some changes occurred in src/tools/rustfmt cc @rust-lang/rustfmt |
☔ The latest upstream changes (presumably #143897) made this pull request unmergeable. Please resolve the merge conflicts. |
Until it can be parsed properly, `expr is pat` is written `builtin # is(expr is pat)`. To be more concise, the included tests use a macro wrapper around it.
Outside of the top level of an `if`/`while` condition, `... && expr is pat && ...` becomes `if ... && let pat = expr && ... { true } else { false }`.
@dianne Thank you for working on this! I'm sad that Regarding scope: The desugaring and scoping rule you have seems fine. I wonder if, rather than translating the containing I think your logic for when I've added a bunch of additional test cases, which may help if further playing with the parsing and scoping. |
If all that's needed is placeholder syntax for trying it out, I think there's some options. I'm admittedly not too familiar with the lexer or parser yet, but at a glance, reserved prefixes should make infix syntax with For stabilizable syntax on current Editions, I couldn't say if there's a way to do it without the raw keywords RFC or equivalent.
Unfortunately, I don't think this works. IIUC,
Thanks! I'll add those to the PR. |
Co-authored-by: Josh Triplett <[email protected]>
This PR partially implements rust-lang/rfcs#3573 for experimentation. It's not yet suitable for general use and may not yet reflect the intended design, but this should hopefully make it easier to explore its design space. r? @joshtriplett for design concerns
Placeholder syntax
Since
is
isn't parsed as an infix operator, I'm using permanently-unstablebuiltin#
syntax as a placeholder. Instead ofexpr is pat
, writebuiltin # is(expr is pat)
or define a macro expanding to that. One possible improvement if raw keywords (RFC 3098) were implemented would be to usek#is
as an infix operator. My understanding is that this syntax is already reserved by RFC 3101.Scoping
I've opted for a simple and restrictive interpretation of the scoping rules in the RFC, based on the rules for
let
-chains:I don't think this is necessarily the intended scope for
is
(or the ideal scope forlet
expressions in that first case, honestly), but my hope is that it will be easier to refine given a concrete implementation1. Lints or errors to prevent shadowing mistakes are left for future work.Desugaring
builtin # is(expr is pat)
is desugared as follows when lowering to HIR:let
expression would be permitted,expr is pat
desugars tolet pat = expr
.&&
-chain containing theis
is wrapped in anif
condition, thenis
is desugared tolet
:... && expr is pat &&...
becomesif ... && let pat = expr && ... { true } else { false }
.This results in non-ideal MIR in some cases, but jump-threading optimizations should hopefully clean it up. I haven't included any tests for that, since this implementation isn't meant for production use. This also doesn't currently enforce Rust 2024 scoping rules for
if
s in its desugaring; how to handle older editions is left as an open question.Behavior in macro expansions
I couldn't find discussion of macros on the RFC, so I've taken the simplest approach: it doesn't really work yet. Similar to
let
statements but unlikelet
expressions, you can putbuiltin # is($e is $p)
in a macro and it will introduce its pattern's bindings into scope as if it were inlined into its expansion site. This doesn't work forlet
expressions because of how macros are parsed: whetherlet
is allowed is determined when parsing, and the macro doesn't have the context of its expansion site to work with, so it doesn't know if it's being expanded into a condition or not. There's one catch though: because of this, most parts of the compiler that work withlet
expressions assume&&
operators associate to the left. Since macro expansions sites aren't re-parsed (cc #61733 (comment)), macros expanding to&&
-chains containingis
operators will break that assumption: putting one of those expansions on the right-hand-side of an&&
will cause this implementation to panic incheck_match
(and also likely make some incorrect assumptions inregion_scope_tree
).I've also tried to handle attributes on
is
correctly. There's no tests since it would be impossible to put an attribute directly on anis
operator expression currently without parentheses (cc #127436), but it may eventually be possible if attributes can apply to macro expansions (cc #63221).Feature gate and tracking issue
None yet. I can add those if there's interest in merging these changes. Since the current
builtin # is(expr is pat)
syntax is permanently unstable, this PR does not stabilize anything.Footnotes
Since
let
-chains already exist, I'd be curious if it'd make sense to restrict the scope ofis
further, to avoid its scope depending on whether it appears in a condition. Possibly some of the questions around shadowing would be easier to resolve too. But it raises some questions around temporary lifetimes and how mixingis
andlet
in&&
-chains should work. Alternatively, I'd be happy with shortening the lifetime oflet
expressions' temporaries by default and keepingis
consistent withlet
. I've been running into some trouble withlet
temporary lifetimes in designingif let
guard patterns too. ↩