-
Notifications
You must be signed in to change notification settings - Fork 14k
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: main
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
isisn'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#isas 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 forletexpressions 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:letexpression would be permitted,expr is patdesugars tolet pat = expr.&&-chain containing theisis wrapped in anifcondition, thenisis 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
ifs 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
letstatements but unlikeletexpressions, 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 forletexpressions because of how macros are parsed: whetherletis 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 withletexpressions assume&&operators associate to the left. Since macro expansions sites aren't re-parsed (cc #61733 (comment)), macros expanding to&&-chains containingisoperators 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
iscorrectly. There's no tests since it would be impossible to put an attribute directly on anisoperator 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 ofisfurther, 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 mixingisandletin&&-chains should work. Alternatively, I'd be happy with shortening the lifetime ofletexpressions' temporaries by default and keepingisconsistent withlet. I've been running into some trouble withlettemporary lifetimes in designingif letguard patterns too. ↩