Skip to content
Merged
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
6 changes: 5 additions & 1 deletion src/Compiler/Checking/CheckComputationExpressions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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) ->
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
|> ignore

[<Theory>]
[<InlineData("preview","BindReturn")>]
[<InlineData("preview","BindReturn")>]
[<InlineData("preview","WithoutBindReturn")>]
[<InlineData("4.7","BindReturn")>]
[<InlineData("4.7","WithoutBindReturn")>]
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

[<Fact>]
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