Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/11.0.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* Fix excessive StackGuard thread jumping ([PR #18971](https://github.com/dotnet/fsharp/pull/18971))
* Checking: Fix checking nested fields for records and anonymous ([PR #18964](https://github.com/dotnet/fsharp/pull/18964))
* Fix name is bound multiple times is not reported in 'as' pattern ([PR #18984](https://github.com/dotnet/fsharp/pull/18984))
* Syntax Tree: fix return type info for let! / and! / use! ([PR #19004](https://github.com/dotnet/fsharp/pull/19004))
* Fix: warn FS0049 on upper union case label. ([PR #19003](https://github.com/dotnet/fsharp/pull/19003))
* Type relations cache: handle potentially "infinite" types ([PR #19010](https://github.com/dotnet/fsharp/pull/19010))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
/// with generalization at appropriate points.
module internal FSharp.Compiler.CheckComputationExpressions

open FSharp.Compiler.TcGlobals
open Internal.Utilities.Library
open FSharp.Compiler.AccessibilityLogic
open FSharp.Compiler.AttributeChecking
Expand Down Expand Up @@ -854,6 +853,12 @@ let (|OptionalSequential|) e =
| SynExpr.Sequential(debugPoint = _sp; isTrueSeq = true; expr1 = dataComp1; expr2 = dataComp2) -> (dataComp1, Some dataComp2)
| _ -> (e, None)

let private mkTypedHeadPat (SynBinding(headPat = headPattern; returnInfo = returnInfo)) =
match returnInfo with
| None -> headPattern
| Some(SynBindingReturnInfo(typeName = typeName; range = range)) ->
SynPat.Typed(headPattern, typeName, unionRanges headPattern.Range range)

[<return: Struct>]
let (|ExprAsUseBang|_|) expr =
match expr with
Expand All @@ -865,7 +870,8 @@ let (|ExprAsUseBang|_|) expr =
body = innerComp
trivia = { LetOrUseKeyword = mBind }) ->
match bindings with
| SynBinding(debugPoint = spBind; headPat = pat; expr = rhsExpr) :: andBangs ->
| SynBinding(debugPoint = spBind; expr = rhsExpr) as binding :: andBangs ->
let pat = mkTypedHeadPat binding
ValueSome(spBind, isFromSource, pat, rhsExpr, andBangs, innerComp, mBind)
| _ -> ValueNone
| _ -> ValueNone
Expand All @@ -881,7 +887,8 @@ let (|ExprAsLetBang|_|) expr =
body = innerComp
trivia = { LetOrUseKeyword = mBind }) ->
match bindings with
| SynBinding(debugPoint = spBind; headPat = letPat; expr = letRhsExpr) :: andBangBindings ->
| SynBinding(debugPoint = spBind; expr = letRhsExpr) as binding :: andBangBindings ->
let letPat = mkTypedHeadPat binding
ValueSome(spBind, isFromSource, letPat, letRhsExpr, andBangBindings, innerComp, mBind)
| _ -> ValueNone
| _ -> ValueNone
Expand Down Expand Up @@ -2003,8 +2010,7 @@ let rec TryTranslateComputationExpression
(letRhsExpr :: [ for SynBinding(expr = andExpr) in andBangBindings -> andExpr ])
|> List.map (fun expr -> mkSourceExprConditional isFromSource expr ceenv.sourceMethInfo ceenv.builderValName)

let pats =
letPat :: [ for SynBinding(headPat = andPat) in andBangBindings -> andPat ]
let pats = letPat :: [ for binding in andBangBindings -> mkTypedHeadPat binding ]

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

Expand Down
12 changes: 7 additions & 5 deletions src/Compiler/SyntaxTree/ParseHelpers.fs
Original file line number Diff line number Diff line change
Expand Up @@ -868,7 +868,9 @@ let mkClassMemberLocalBindings
SynMemberDefn.LetBindings(decls, isStatic, isRec, mWhole)

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

let trivia: SynBindingTrivia =
Expand All @@ -887,7 +889,7 @@ let mkAndBang (mKeyword: range, pat: SynPat, rhs: SynExpr, mWhole: range, mEqual
xmlDoc = PreXmlDoc.Empty,
valData = SynInfo.emptySynValData,
headPat = pat,
returnInfo = None,
returnInfo = returnInfo,
expr = rhs,
range = mWhole,
debugPoint = spBind,
Expand Down Expand Up @@ -1075,11 +1077,11 @@ let mkLetExpression
mWhole: range,
body: SynExpr,
bindingInfo: BindingSet option,
bangInfo: (SynPat * SynExpr * SynBinding list * range * range option * bool) option
bangInfo: (SynPat * SynBindingReturnInfo option * SynExpr * SynBinding list * range * range option * bool) option
) =
if isBang then
match bangInfo with
| Some(pat, rhs, andBangs, mKeyword, mEquals, isUse) ->
| Some(pat, returnInfo, rhs, andBangs, mKeyword, mEquals, isUse) ->
let spBind = DebugPointAtBinding.Yes(unionRanges mKeyword rhs.Range)

let trivia: SynBindingTrivia =
Expand All @@ -1103,7 +1105,7 @@ let mkLetExpression
xmlDoc = PreXmlDoc.Empty,
valData = SynInfo.emptySynValData,
headPat = pat,
returnInfo = None,
returnInfo = returnInfo,
expr = rhs,
range = unionRanges mKeyword rhs.Range,
debugPoint = spBind,
Expand Down
11 changes: 9 additions & 2 deletions src/Compiler/SyntaxTree/ParseHelpers.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -205,11 +205,18 @@ val mkLetExpression:
mWhole: range *
body: SynExpr *
bindingInfo: BindingSet option *
bangInfo: (SynPat * SynExpr * SynBinding list * range * range option * bool) option ->
bangInfo: (SynPat * SynBindingReturnInfo option * SynExpr * SynBinding list * range * range option * bool) option ->
SynExpr

val mkAndBang:
mKeyword: range * pat: SynPat * rhs: SynExpr * mWhole: range * mEquals: range * mIn: range option -> SynBinding
mKeyword: range *
pat: SynPat *
returnInfo: SynBindingReturnInfo option *
rhs: SynExpr *
mWhole: range *
mEquals: range *
mIn: range option ->
SynBinding

val mkDefnBindings:
mWhole: range * BindingSet * attrs: SynAttributes * vis: SynAccess option * attrsm: range -> SynModuleDecl list
Expand Down
57 changes: 24 additions & 33 deletions src/Compiler/pars.fsy
Original file line number Diff line number Diff line change
Expand Up @@ -3523,32 +3523,23 @@ bindingPattern:
| headBindingPattern
{ $1, $1.Range }

// This rule unifies the pattern parsing for both regular 'let' bindings and (let!, use!, and!)
bindingPatternWithOptType:
| headBindingPattern opt_topReturnTypeWithTypeConstraints
{ // Pattern with optional type annotation
match $2 with
| None ->
// No type annotation
$1, $1.Range, None
| Some(colonRangeOpt, SynReturnInfo((ty, _), _)) ->
// Pattern with type annotation (e.g., x: int)
let mWhole = unionRanges $1.Range ty.Range
let typedPat = SynPat.Typed($1, ty, mWhole)
typedPat, mWhole, Some ty }

// Handles the pattern part of let!, use!, and! bindings
// Handles pattern and return type part of let!, use!, and! bindings
ceBindingCore:
| opt_inline opt_mutable bindingPatternWithOptType
{ let pat, mPat, tyOpt = $3
| opt_inline opt_mutable bindingPattern opt_topReturnTypeWithTypeConstraints
{ let pat, mPat = $3
let tyOpt = $4
let isInline = Option.isSome $1
let isMutable = Option.isSome $2
match tyOpt with
| Some ty ->
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetUseAndBang ty.Range
| None -> ()

pat, mPat, isInline, isMutable, tyOpt }

let returnInfo =
match tyOpt with
| Some(colonRangeOpt, SynReturnInfo((ty, _), tym)) ->
parseState.LexBuffer.CheckLanguageFeatureAndRecover LanguageFeature.AllowTypedLetUseAndBang ty.Range
Some (SynBindingReturnInfo(ty, tym, [], { ColonRange = colonRangeOpt }))
| None ->
None

pat, mPat, isInline, isMutable, returnInfo }

opt_simplePatterns:
| simplePatterns
Expand Down Expand Up @@ -4144,7 +4135,7 @@ recover:

moreBinders:
| AND_BANG ceBindingCore EQUALS typedSequentialExprBlock IN moreBinders %prec expr_let
{ let pat, mPat, isInline, isMutable, tyOpt = $2
{ let pat, mPat, isInline, isMutable, returnInfo = $2

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

mkAndBang(mKeyword, pat, $4, m, mEquals, mIn) :: $6 }
mkAndBang(mKeyword, pat, returnInfo, $4, m, mEquals, mIn) :: $6 }

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

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

mkAndBang(mKeyword, pat, $4, m, mEquals, mIn) :: $7 }
mkAndBang(mKeyword, pat, returnInfo, $4, m, mEquals, mIn) :: $7 }

| %prec prec_no_more_attr_bindings
{ [] }
Expand Down Expand Up @@ -4536,7 +4527,7 @@ declExpr:

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

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

mkLetExpression(true, None, m, $8, None, Some(pat, $4, $7, mKeyword, mEquals, isUse)) }
mkLetExpression(true, None, m, $8, None, Some(pat, returnInfo, $4, $7, mKeyword, mEquals, isUse)) }

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

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

let isUse = ($1 = "use")

mkLetExpression(true, None, m, $8, None, Some(pat, $4, $7, mKeyword, mEquals, isUse)) }
mkLetExpression(true, None, m, $8, None, Some(pat, returnInfo, $4, $7, mKeyword, mEquals, isUse)) }

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

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

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

| DO_BANG typedSequentialExpr IN opt_OBLOCKSEP typedSequentialExprBlock %prec expr_let
{ let spBind = DebugPointAtBinding.NoneAtDo
Expand Down
Loading
Loading