Skip to content

Commit d82b8df

Browse files
authored
Unify let, let!, use, use! LetOrUse AST representation. (#18825)
1 parent 9cec56d commit d82b8df

File tree

93 files changed

+1849
-1089
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

93 files changed

+1849
-1089
lines changed

docs/release-notes/.FSharp.Compiler.Service/10.0.100.md

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,135 @@
3030
* Mark `Range.Zero` as obsolete in favor of `Range.range0` ([PR #18664](https://github.com/dotnet/fsharp/pull/18664))
3131
* Use `Synbinding` to model `and!` ([PR #18805](https://github.com/dotnet/fsharp/pull/18805))
3232
* Redesign #line processing. The original positions (unaffected by #line directives) are now kept in the AST, and `__LINE__` and `__SOURCE_LINE__` show the original line numbers / file names. However, all diagnostics and debug information stays the same (shows the position transformed by the #line directives). ([Issue #18553](https://github.com/dotnet/fsharp/issues/18553), [PR #18699](https://github.com/dotnet/fsharp/pull/18699))
33+
* Unify `let`, `let!`, `use` and `use!` AST representation. ([PR #18825](https://github.com/dotnet/fsharp/pull/18825))[^1]
34+
35+
### Migration Guidance for AST Users
36+
37+
**Note:** The unified AST introduces two new boolean fields:
38+
- `isFromSource`: Indicates if the binding comes from user-written code (`true`) or is compiler-generated (`false`)
39+
- `isBang`: Distinguishes computation expression bindings (`let!`/`use!` = `true`) from regular bindings (`let`/`use` = `false`)
40+
41+
### 1. Pattern Matching Updates
42+
43+
**Before:**
44+
```fsharp
45+
match expr with
46+
| SynExpr.LetOrUse(isRec, isUse, bindings, body, range, trivia) ->
47+
// Handle regular let/use
48+
| SynExpr.LetOrUseBang(spBind, isUse, isFromSource, pat, rhs, andBangs, body, range, trivia) ->
49+
// Handle let!/use!
50+
```
51+
52+
**After:**
53+
```fsharp
54+
match expr with
55+
| SynExpr.LetOrUse(isRec, isUse, isFromSource, isBang, bindings, body, range, trivia) ->
56+
if isBang then
57+
// This is a let!/use! expression
58+
match bindings with
59+
| firstBinding :: andBangs ->
60+
match firstBinding with
61+
| SynBinding(headPat = pat; expr = rhs) ->
62+
// pat and rhs extracted from first binding
63+
// andBangs contains the and! bindings
64+
| [] -> // error case
65+
else
66+
// This is a regular let/use expression
67+
```
68+
69+
### 2. Construction Updates
70+
71+
**Before:**
72+
```fsharp
73+
// Creating a let! expression
74+
SynExpr.LetOrUseBang(
75+
bindDebugPoint,
76+
false, // isUse
77+
true, // isFromSource
78+
pat,
79+
rhsExpr,
80+
andBangs,
81+
bodyExpr,
82+
range,
83+
trivia
84+
)
85+
```
86+
87+
**After:**
88+
```fsharp
89+
// Creating a let! expression
90+
let firstBinding = SynBinding(
91+
accessibility = None,
92+
kind = SynBindingKind.Normal,
93+
isInline = false,
94+
isMutable = false,
95+
attributes = [],
96+
xmlDoc = PreXmlDoc.Empty,
97+
valData = SynInfo.emptySynValData,
98+
headPat = pat, // Pattern moved here
99+
returnInfo = None,
100+
expr = rhsExpr, // RHS moved here
101+
range = range,
102+
debugPoint = bindDebugPoint, // Debug point moved here
103+
trivia = bindingTrivia
104+
)
105+
SynExpr.LetOrUse(
106+
false, // isRecursive
107+
false, // isUse
108+
true, // isFromSource
109+
true, // isBang (indicates let!)
110+
firstBinding :: andBangs, // All bindings in single list
111+
bodyExpr,
112+
range,
113+
trivia
114+
)
115+
```
116+
117+
### 3. Common Migration Patterns
118+
119+
**Checking for computation expressions:**
120+
```fsharp
121+
// Before
122+
match expr with
123+
| SynExpr.LetOrUseBang _ -> true
124+
| _ -> false
125+
126+
// After
127+
match expr with
128+
| SynExpr.LetOrUse(isBang = true) -> true
129+
| _ -> false
130+
```
131+
132+
**Extracting pattern and expression from let!:**
133+
```fsharp
134+
// Before
135+
| SynExpr.LetOrUseBang(_, _, _, pat, rhs, _, _, _, _) ->
136+
processBinding pat rhs
137+
138+
// After
139+
| SynExpr.LetOrUse(isBang = true; bindings = binding :: _) ->
140+
match binding with
141+
| SynBinding(headPat = pat; expr = rhs) ->
142+
processBinding pat rhs
143+
| _ -> // error
144+
```
145+
146+
**Processing and! bindings:**
147+
```fsharp
148+
// Before
149+
| SynExpr.LetOrUseBang(_, _, _, firstPat, firstRhs, andBangs, _, _, _) ->
150+
processFirst firstPat firstRhs
151+
for andBang in andBangs do
152+
processAndBang andBang
153+
154+
// After
155+
| SynExpr.LetOrUse(isBang = true; bindings = bindings) ->
156+
match bindings with
157+
| first :: rest ->
158+
processBinding first
159+
for andBang in rest do
160+
processAndBang andBang
161+
| [] -> // error
162+
```
163+
164+
[^1]: See [Migration Guidance for AST Users](#migration-guidance-for-ast-users) section for detailed information on how to update your code to work with the unified AST representation.

src/Compiler/Checking/CheckRecordSyntaxHelpers.fs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,4 +180,13 @@ let BindOriginalRecdExpr (withExpr: SynExpr * BlockSeparator) mkRecdExpr =
180180
None,
181181
SynBindingTrivia.Zero)
182182

183-
SynExpr.LetOrUse(false, false, [ binding ], mkRecdExpr (Some withExpr), mOrigExprSynth, SynExprLetOrUseTrivia.Zero)
183+
SynExpr.LetOrUse(
184+
isRecursive = false,
185+
isUse = false,
186+
isFromSource = false, // compiler generated during desugaring
187+
isBang = false,
188+
bindings = [ binding ],
189+
body = mkRecdExpr (Some withExpr),
190+
range = mOrigExprSynth,
191+
trivia = SynExprLetOrUseTrivia.Zero
192+
)

0 commit comments

Comments
 (0)