Skip to content

Commit 079fda9

Browse files
authored
Syntax Tree: fix return type info for let! / and! / use! (#19004)
1 parent fd28b12 commit 079fda9

34 files changed

+733
-434
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* Adjust conservative method-overload duplicate detection rules for nativeptr types ([PR #18911](https://github.com/dotnet/fsharp/pull/18911))
66
* Checking: Fix checking nested fields for records and anonymous ([PR #18964](https://github.com/dotnet/fsharp/pull/18964))
77
* Fix name is bound multiple times is not reported in 'as' pattern ([PR #18984](https://github.com/dotnet/fsharp/pull/18984))
8+
* Syntax Tree: fix return type info for let! / and! / use! ([PR #19004](https://github.com/dotnet/fsharp/pull/19004))
89
* Fix: warn FS0049 on upper union case label. ([PR #19003](https://github.com/dotnet/fsharp/pull/19003))
910
* Type relations cache: handle potentially "infinite" types ([PR #19010](https://github.com/dotnet/fsharp/pull/19010))
1011
* Disallow recursive structs with lifted type parameters ([Issue #18993](https://github.com/dotnet/fsharp/issues/18993), [PR #19031](https://github.com/dotnet/fsharp/pull/19031))

src/Compiler/Checking/Expressions/CheckComputationExpressions.fs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -853,6 +853,12 @@ let (|OptionalSequential|) e =
853853
| SynExpr.Sequential(debugPoint = _sp; isTrueSeq = true; expr1 = dataComp1; expr2 = dataComp2) -> (dataComp1, Some dataComp2)
854854
| _ -> (e, None)
855855

856+
let private mkTypedHeadPat (SynBinding(headPat = headPattern; returnInfo = returnInfo)) =
857+
match returnInfo with
858+
| None -> headPattern
859+
| Some(SynBindingReturnInfo(typeName = typeName; range = range)) ->
860+
SynPat.Typed(headPattern, typeName, unionRanges headPattern.Range range)
861+
856862
[<return: Struct>]
857863
let (|ExprAsUseBang|_|) expr =
858864
match expr with
@@ -864,7 +870,8 @@ let (|ExprAsUseBang|_|) expr =
864870
body = innerComp
865871
trivia = { LetOrUseKeyword = mBind }) ->
866872
match bindings with
867-
| SynBinding(debugPoint = spBind; headPat = pat; expr = rhsExpr) :: andBangs ->
873+
| SynBinding(debugPoint = spBind; expr = rhsExpr) as binding :: andBangs ->
874+
let pat = mkTypedHeadPat binding
868875
ValueSome(spBind, isFromSource, pat, rhsExpr, andBangs, innerComp, mBind)
869876
| _ -> ValueNone
870877
| _ -> ValueNone
@@ -880,7 +887,8 @@ let (|ExprAsLetBang|_|) expr =
880887
body = innerComp
881888
trivia = { LetOrUseKeyword = mBind }) ->
882889
match bindings with
883-
| SynBinding(debugPoint = spBind; headPat = letPat; expr = letRhsExpr) :: andBangBindings ->
890+
| SynBinding(debugPoint = spBind; expr = letRhsExpr) as binding :: andBangBindings ->
891+
let letPat = mkTypedHeadPat binding
884892
ValueSome(spBind, isFromSource, letPat, letRhsExpr, andBangBindings, innerComp, mBind)
885893
| _ -> ValueNone
886894
| _ -> ValueNone
@@ -2004,8 +2012,7 @@ let rec TryTranslateComputationExpression
20042012
(letRhsExpr :: [ for SynBinding(expr = andExpr) in andBangBindings -> andExpr ])
20052013
|> List.map (fun expr -> mkSourceExprConditional isFromSource expr ceenv.sourceMethInfo ceenv.builderValName)
20062014

2007-
let pats =
2008-
letPat :: [ for SynBinding(headPat = andPat) in andBangBindings -> andPat ]
2015+
let pats = letPat :: [ for binding in andBangBindings -> mkTypedHeadPat binding ]
20092016

20102017
let sourcesRange = sources |> List.map (fun e -> e.Range) |> List.reduce unionRanges
20112018

src/Compiler/SyntaxTree/ParseHelpers.fs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -868,7 +868,9 @@ let mkClassMemberLocalBindings
868868
SynMemberDefn.LetBindings(decls, isStatic, isRec, mWhole)
869869

870870
/// Creates a SynExprAndBang node for and! bindings in computation expressions
871-
let mkAndBang (mKeyword: range, pat: SynPat, rhs: SynExpr, mWhole: range, mEquals: range, mIn: range option) =
871+
let mkAndBang
872+
(mKeyword: range, pat: SynPat, returnInfo: SynBindingReturnInfo option, rhs: SynExpr, mWhole: range, mEquals: range, mIn: range option)
873+
=
872874
let spBind = DebugPointAtBinding.Yes(unionRanges mKeyword rhs.Range)
873875

874876
let trivia: SynBindingTrivia =
@@ -887,7 +889,7 @@ let mkAndBang (mKeyword: range, pat: SynPat, rhs: SynExpr, mWhole: range, mEqual
887889
xmlDoc = PreXmlDoc.Empty,
888890
valData = SynInfo.emptySynValData,
889891
headPat = pat,
890-
returnInfo = None,
892+
returnInfo = returnInfo,
891893
expr = rhs,
892894
range = mWhole,
893895
debugPoint = spBind,
@@ -1075,11 +1077,11 @@ let mkLetExpression
10751077
mWhole: range,
10761078
body: SynExpr,
10771079
bindingInfo: BindingSet option,
1078-
bangInfo: (SynPat * SynExpr * SynBinding list * range * range option * bool) option
1080+
bangInfo: (SynPat * SynBindingReturnInfo option * SynExpr * SynBinding list * range * range option * bool) option
10791081
) =
10801082
if isBang then
10811083
match bangInfo with
1082-
| Some(pat, rhs, andBangs, mKeyword, mEquals, isUse) ->
1084+
| Some(pat, returnInfo, rhs, andBangs, mKeyword, mEquals, isUse) ->
10831085
let spBind = DebugPointAtBinding.Yes(unionRanges mKeyword rhs.Range)
10841086

10851087
let trivia: SynBindingTrivia =
@@ -1103,7 +1105,7 @@ let mkLetExpression
11031105
xmlDoc = PreXmlDoc.Empty,
11041106
valData = SynInfo.emptySynValData,
11051107
headPat = pat,
1106-
returnInfo = None,
1108+
returnInfo = returnInfo,
11071109
expr = rhs,
11081110
range = unionRanges mKeyword rhs.Range,
11091111
debugPoint = spBind,

src/Compiler/SyntaxTree/ParseHelpers.fsi

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,11 +204,18 @@ val mkLetExpression:
204204
mWhole: range *
205205
body: SynExpr *
206206
bindingInfo: BindingSet option *
207-
bangInfo: (SynPat * SynExpr * SynBinding list * range * range option * bool) option ->
207+
bangInfo: (SynPat * SynBindingReturnInfo option * SynExpr * SynBinding list * range * range option * bool) option ->
208208
SynExpr
209209

210210
val mkAndBang:
211-
mKeyword: range * pat: SynPat * rhs: SynExpr * mWhole: range * mEquals: range * mIn: range option -> SynBinding
211+
mKeyword: range *
212+
pat: SynPat *
213+
returnInfo: SynBindingReturnInfo option *
214+
rhs: SynExpr *
215+
mWhole: range *
216+
mEquals: range *
217+
mIn: range option ->
218+
SynBinding
212219

213220
val mkDefnBindings:
214221
mWhole: range * BindingSet * attrs: SynAttributes * vis: SynAccess option * attrsm: range -> SynModuleDecl list

src/Compiler/pars.fsy

Lines changed: 24 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3523,32 +3523,23 @@ bindingPattern:
35233523
| headBindingPattern
35243524
{ $1, $1.Range }
35253525

3526-
// This rule unifies the pattern parsing for both regular 'let' bindings and (let!, use!, and!)
3527-
bindingPatternWithOptType:
3528-
| headBindingPattern opt_topReturnTypeWithTypeConstraints
3529-
{ // Pattern with optional type annotation
3530-
match $2 with
3531-
| None ->
3532-
// No type annotation
3533-
$1, $1.Range, None
3534-
| Some(colonRangeOpt, SynReturnInfo((ty, _), _)) ->
3535-
// Pattern with type annotation (e.g., x: int)
3536-
let mWhole = unionRanges $1.Range ty.Range
3537-
let typedPat = SynPat.Typed($1, ty, mWhole)
3538-
typedPat, mWhole, Some ty }
3539-
3540-
// Handles the pattern part of let!, use!, and! bindings
3526+
// Handles pattern and return type part of let!, use!, and! bindings
35413527
ceBindingCore:
3542-
| opt_inline opt_mutable bindingPatternWithOptType
3543-
{ let pat, mPat, tyOpt = $3
3528+
| opt_inline opt_mutable bindingPattern opt_topReturnTypeWithTypeConstraints
3529+
{ let pat, mPat = $3
3530+
let tyOpt = $4
35443531
let isInline = Option.isSome $1
35453532
let isMutable = Option.isSome $2
3546-
match tyOpt with
3547-
| Some ty ->
3548-
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetUseAndBang ty.Range
3549-
| None -> ()
3550-
3551-
pat, mPat, isInline, isMutable, tyOpt }
3533+
3534+
let returnInfo =
3535+
match tyOpt with
3536+
| Some(colonRangeOpt, SynReturnInfo((ty, _), tym)) ->
3537+
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetUseAndBang ty.Range
3538+
Some (SynBindingReturnInfo(ty, tym, [], { ColonRange = colonRangeOpt }))
3539+
| None ->
3540+
None
3541+
3542+
pat, mPat, isInline, isMutable, returnInfo }
35523543

35533544
opt_simplePatterns:
35543545
| simplePatterns
@@ -4144,7 +4135,7 @@ recover:
41444135

41454136
moreBinders:
41464137
| AND_BANG ceBindingCore EQUALS typedSequentialExprBlock IN moreBinders %prec expr_let
4147-
{ let pat, mPat, isInline, isMutable, tyOpt = $2
4138+
{ let pat, mPat, isInline, isMutable, returnInfo = $2
41484139

41494140
// and! bindings don't support inline or mutable modifiers
41504141
if isInline then errorR(Error(FSComp.SR.parsInvalidDeclarationSyntax(), rhs parseState 2))
@@ -4155,11 +4146,11 @@ moreBinders:
41554146
let m = unionRanges mKeyword $4.Range
41564147
let mIn = Some(rhs parseState 5)
41574148

4158-
mkAndBang(mKeyword, pat, $4, m, mEquals, mIn) :: $6 }
4149+
mkAndBang(mKeyword, pat, returnInfo, $4, m, mEquals, mIn) :: $6 }
41594150

41604151
| OAND_BANG ceBindingCore EQUALS typedSequentialExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP moreBinders %prec expr_let
41614152
{ // Offside-sensitive version of and! binding
4162-
let pat, mPat, isInline, isMutable, tyOpt = $2
4153+
let pat, mPat, isInline, isMutable, returnInfo = $2
41634154

41644155
// and! bindings don't support inline or mutable modifiers
41654156
if isInline then errorR(Error(FSComp.SR.parsInvalidDeclarationSyntax(), rhs parseState 2))
@@ -4171,7 +4162,7 @@ moreBinders:
41714162
let mEquals = rhs parseState 3
41724163
let m = unionRanges mKeyword $4.Range
41734164

4174-
mkAndBang(mKeyword, pat, $4, m, mEquals, mIn) :: $7 }
4165+
mkAndBang(mKeyword, pat, returnInfo, $4, m, mEquals, mIn) :: $7 }
41754166

41764167
| %prec prec_no_more_attr_bindings
41774168
{ [] }
@@ -4536,7 +4527,7 @@ declExpr:
45364527

45374528
| BINDER ceBindingCore EQUALS typedSequentialExprBlock IN opt_OBLOCKSEP moreBinders typedSequentialExprBlock %prec expr_let
45384529
{ // Handle let! and use! bindings with unified pattern parsing
4539-
let pat, mPat, isInline, isMutable, tyOpt = $2
4530+
let pat, mPat, isInline, isMutable, returnInfo = $2
45404531

45414532
// let! and use! bindings don't support inline or mutable modifiers
45424533
if isInline then errorR(Error(FSComp.SR.parsInvalidDeclarationSyntax(), rhs parseState 2))
@@ -4549,11 +4540,11 @@ declExpr:
45494540
// $1 contains the actual keyword ("let" or "use")
45504541
let isUse = ($1 = "use")
45514542

4552-
mkLetExpression(true, None, m, $8, None, Some(pat, $4, $7, mKeyword, mEquals, isUse)) }
4543+
mkLetExpression(true, None, m, $8, None, Some(pat, returnInfo, $4, $7, mKeyword, mEquals, isUse)) }
45534544

45544545
| OBINDER ceBindingCore EQUALS typedSequentialExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP moreBinders typedSequentialExprBlock %prec expr_let
45554546
{ // Offside-sensitive version of let!/use! binding
4556-
let pat, mPat, isInline, isMutable, tyOpt = $2
4547+
let pat, mPat, isInline, isMutable, returnInfo = $2
45574548

45584549
// let! and use! bindings don't support inline or mutable modifiers
45594550
if isInline then errorR(Error(FSComp.SR.parsInvalidDeclarationSyntax(), rhs parseState 2))
@@ -4567,12 +4558,12 @@ declExpr:
45674558

45684559
let isUse = ($1 = "use")
45694560

4570-
mkLetExpression(true, None, m, $8, None, Some(pat, $4, $7, mKeyword, mEquals, isUse)) }
4561+
mkLetExpression(true, None, m, $8, None, Some(pat, returnInfo, $4, $7, mKeyword, mEquals, isUse)) }
45714562

45724563
| OBINDER ceBindingCore EQUALS typedSequentialExprBlock hardwhiteDefnBindingsTerminator opt_OBLOCKSEP error %prec expr_let
45734564
{ // Error recovery for incomplete let!/use! bindings
45744565
// Allows intellisense to work when writing incomplete computation expressions
4575-
let pat, mPat, isInline, isMutable, tyOpt = $2
4566+
let pat, mPat, isInline, isMutable, returnInfo = $2
45764567

45774568
// Error checking for invalid modifiers
45784569
if isInline then errorR(Error(FSComp.SR.parsInvalidDeclarationSyntax(), rhs parseState 2))
@@ -4586,7 +4577,7 @@ declExpr:
45864577
let isUse = ($1 = "use")
45874578

45884579
// Use ImplicitZero as the continuation expression for error recovery
4589-
mkLetExpression(true, None, mAll, SynExpr.ImplicitZero m, None, Some(pat, $4, [], mKeyword, mEquals, isUse)) }
4580+
mkLetExpression(true, None, mAll, SynExpr.ImplicitZero m, None, Some(pat, returnInfo, $4, [], mKeyword, mEquals, isUse)) }
45904581

45914582
| DO_BANG typedSequentialExpr IN opt_OBLOCKSEP typedSequentialExprBlock %prec expr_let
45924583
{ let spBind = DebugPointAtBinding.NoneAtDo

0 commit comments

Comments
 (0)