From 215b63cb242b7d8e6cf35cf441b29a35fe60c72c Mon Sep 17 00:00:00 2001 From: Tomas Grosup Date: Thu, 29 Dec 2022 20:21:48 +0100 Subject: [PATCH] After many attempts, not making .BindReturn work when wrapped in if-then without else. Now trying with fallback to regular Bind+Return instead --- .../Checking/CheckComputationExpressions.fs | 6 ++- .../Language/ComputationExpressionTests.fs | 45 ++++++++++++++++++- 2 files changed, 49 insertions(+), 2 deletions(-) diff --git a/src/Compiler/Checking/CheckComputationExpressions.fs b/src/Compiler/Checking/CheckComputationExpressions.fs index db8d307381f..a1c89f61dc7 100644 --- a/src/Compiler/Checking/CheckComputationExpressions.fs +++ b/src/Compiler/Checking/CheckComputationExpressions.fs @@ -1672,6 +1672,9 @@ let TcComputationExpression (cenv: cenv) env (overallTy: OverallTy) tpenv (mWhol let bindCall = mkSynCall bindName bindRange (bindArgs @ [consumeExpr]) translatedCtxt (bindCall |> addBindDebugPoint)) + /// This function is for desugaring into .Bind{N}Return calls if possible + /// The outer option indicates if .BindReturn is possible. When it returns None, .BindReturn cannot be used + /// The inner option indicates if a custom operation is involved inside and convertSimpleReturnToExpr varSpace innerComp = match innerComp with | SynExpr.YieldOrReturn ((false, _), returnExpr, m) -> @@ -1697,7 +1700,8 @@ let TcComputationExpression (cenv: cenv) env (overallTy: OverallTy) tpenv (mWhol | Some (thenExpr, None) -> let elseExprOptOpt = match elseCompOpt with - | None -> Some None + // When we are missing an 'else' part alltogether in case of 'if cond then return exp', we fallback from BindReturn into regular Bind+Return + | None -> None | Some elseComp -> match convertSimpleReturnToExpr varSpace elseComp with | None -> None // failure diff --git a/tests/FSharp.Compiler.ComponentTests/Language/ComputationExpressionTests.fs b/tests/FSharp.Compiler.ComponentTests/Language/ComputationExpressionTests.fs index 52a4ccabe4c..fa1c8c49b42 100644 --- a/tests/FSharp.Compiler.ComponentTests/Language/ComputationExpressionTests.fs +++ b/tests/FSharp.Compiler.ComponentTests/Language/ComputationExpressionTests.fs @@ -92,4 +92,47 @@ let x = lb {1; 2; if true then 3;} |> compile |> shouldFail |> withSingleDiagnostic (Error 708, Line 10, Col 19, Line 10, Col 31, "This control construct may only be used if the computation expression builder defines a 'Zero' method") - |> ignore \ No newline at end of file + |> ignore + + [] + [] + [] + [] + [] + [] + let ``A CE with BindReturn and Zero can omit else in an if-then return`` (langVersion, bindReturnName) = + let code = $""" +type Builder () = + member inline __.Return (x: 'T) = Seq.singleton x + member inline __.Bind (p: seq<'T>, rest: 'T->seq<'U>) = Seq.collect rest p + member inline __.Zero () = Seq.empty + member inline __.%s{bindReturnName} (x : seq<'T>, f: 'T -> 'U) = Seq.map f x + +let seqbuilder= new Builder () + +let _pythags = seqbuilder {{ + let! z = seq [5;10] + if (z > 6) then return (z,z) }} """ + code + |> FSharp + |> withLangVersion langVersion + |> typecheck + |> shouldSucceed + + [] + let ``A CE with BindReturn and Zero can work without Return if flow control is not used`` () = + let code = $""" +type Builder () = + member inline __.Bind (p: seq<'T>, rest: 'T->seq<'U>) = Seq.collect rest p + //member inline __.Zero () = Seq.empty + member inline __.BindReturn (x : seq<'T>, f: 'T -> 'U) = Seq.map f x + +let seqbuilder= new Builder () + +let _pythags = seqbuilder {{ + let! z = seq [5;10] + return (z,z) }} """ + code + |> FSharp + |> typecheck + |> shouldSucceed \ No newline at end of file