Unify let, let!, use, use! LetOrUse AST representation.#18816
Unify let, let!, use, use! LetOrUse AST representation.#18816edgarfgp wants to merge 38 commits intodotnet:mainfrom
let, let!, use, use! LetOrUse AST representation.#18816Conversation
❗ Release notes requiredCaution No release notes found for the changed paths (see table below). Please make sure to add an entry with an informative description of the change as well as link to this pull request, issue and language suggestion if applicable. Release notes for this repository are based on Keep A Changelog format. The following format is recommended for this repository:
If you believe that release notes are not necessary for this PR, please add NO_RELEASE_NOTES label to the pull request. You can open this PR in browser to add release notes: open in github.dev
|
|
@auduchinok Can you please have a look at the AST changes.? Once we are happy with these will double check the type checking files. |
9f41ed1 to
d380ef0
Compare
8872d00 to
b3db637
Compare
There was a problem hiding this comment.
Pull Request Overview
This PR unifies the AST representation for let, let!, use, and use! expressions by consolidating them into a single LetOrUse variant. Previously, F# had separate AST nodes for regular let/use expressions and bang-form let!/use! expressions (LetOrUseBang), which led to code duplication and complexity in handling these constructs.
- Removes the
SynExpr.LetOrUseBangAST variant entirely - Extends
SynExpr.LetOrUsewith additional flags (isFromSourceandisComputed) to handle all cases - Updates all related parsing, checking, and analysis code to work with the unified representation
Reviewed Changes
Copilot reviewed 85 out of 85 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Compiler/SyntaxTree/SyntaxTree.fs | Removes LetOrUseBang variant and extends LetOrUse with additional parameters |
| src/Compiler/SyntaxTree/ParseHelpers.fs | Updates mkLetExpression to create unified LetOrUse nodes for all cases |
| src/Compiler/Checking/Expressions/CheckComputationExpressions.fs | Major refactoring to work with unified AST representation |
| src/Compiler/Service/*.fs | Updates various service modules to handle unified representation |
| tests/service/data/SyntaxTree/SynType/*.bsl | Updates baseline test files to reflect new AST structure |
| tests/FSharp.Compiler.Service.Tests/*.bsl | Updates surface area baselines for API changes |
src/Compiler/Checking/Expressions/CheckComputationExpressions.fs
Outdated
Show resolved
Hide resolved
src/Compiler/Checking/Expressions/CheckComputationExpressions.fs
Outdated
Show resolved
Hide resolved
b8f7ad6 to
f960746
Compare
auduchinok
left a comment
There was a problem hiding this comment.
@edgarfgp This is really good work 🙂
I think we can remove a lot of computed let handling from the tree traversal and similar utilities. I'd expect the normal let logic to work as expected there.
| | SynExpr.LetOrUseBang(rhs = synExpr1; andBangs = synExprAndBangs; body = synExpr2) -> | ||
| [ | ||
| yield synExpr1 | ||
| for SynBinding(expr = eAndBang) in synExprAndBangs do | ||
| yield eAndBang | ||
| yield synExpr2 | ||
| ] | ||
| |> List.tryPick walkExpr | ||
|
|
There was a problem hiding this comment.
Not a suggestion in this particular case, but we could in theory match
| SynExpr.LetOrUse(isComputed = true; bindings = synBindingList; body = synExpr) ->to minimize the diff in cases like this.
| if isComputed then | ||
| [ | ||
| for SynBinding(expr = bindingExpr) in synBindingList do | ||
| yield bindingExpr | ||
| yield synExpr | ||
| ] | ||
| |> List.tryPick walkExpr | ||
| else | ||
| Option.orElse (List.tryPick walkBinding synBindingList) (walkExpr synExpr) |
There was a problem hiding this comment.
I wonder if different logic is actually needed here. Would it work as expected if we just reuse the LetOrUse logic for the computed variants? It seems the tree traversal should be the same.
| for SynBinding(debugPoint = andBangSpBind; expr = eAndBang) in andBangs do | ||
| yield! walkBindSeqPt andBangSpBind | ||
| yield! walkExpr true eAndBang | ||
| | SynExpr.LetOrUse(isComputed = true; bindings = bindings; body = bodyExpr) -> |
There was a problem hiding this comment.
Isn't it covered on line 700?
| visit expr (fun exprNodes -> exprNodes @ List.collect visitSynType typeArgs |> continuation) | ||
| | SynExpr.LetOrUse(bindings = bindings; body = body) -> | ||
| visit body (fun nodes -> List.collect visitBinding bindings @ nodes |> continuation) | ||
| | SynExpr.LetOrUse(isComputed = isComputed; bindings = bindings; body = body) -> |
There was a problem hiding this comment.
Likewise, do we need to distinguish the computed variant here? I'd expect the logic from LetOrUse to be reused.
| true, // isFromSource is true for user-written code | ||
| false, // isComputed is false for let/use |
There was a problem hiding this comment.
Please move these comments to SyntaxTree.fsi.
|
@auduchinok Thanks for the review. I trying making this compile locally but as soon I removed the I would love to unify both constructs. but not luck so far. Edit: This issue is when trying to build locally FsharpCore with the newly updated parser. @T-Gro have you seen this before by any chance ? |
|
No, have not seen it. |
Yes. usual stuff , but something seems to be happening to FSharpCore and I can not figure it out. |
|
I tried to build the branch from scratch and the first error I got was It looks like it encountered a |
|
@majocha Thanks, You were on to something. It build now locally. |
Description
Fixes # (issue, if applicable)
Checklist