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
8 changes: 7 additions & 1 deletion src/fsharp/CheckComputationExpressions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -949,8 +949,14 @@ let TcComputationExpression cenv env overallTy tpenv (mWhole, interpExpr: Expr,
| SynExpr.Paren (_, _, _, m) ->
error(Error(FSComp.SR.tcConstructIsAmbiguousInComputationExpression(), m))

// In some cases the node produced by `mkSynCall "Zero" m []` may be discarded in the case
// of implicit yields - for example "list { 1; 2 }" when each expression checks as an implicit yield.
// If it is not discarded, the syntax node will later be checked and the existence/non-existence of the Zero method
// will be checked/reported appropriately (though the error message won't mention computation expressions
// like our other error messages for missing methods).
| SynExpr.ImplicitZero m ->
if isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env m ad "Zero" builderTy) then error(Error(FSComp.SR.tcRequireBuilderMethod("Zero"), m))
if (not enableImplicitYield) &&
isNil (TryFindIntrinsicOrExtensionMethInfo ResultCollectionSettings.AtMostOneResult cenv env m ad "Zero" builderTy) then error(Error(FSComp.SR.tcRequireBuilderMethod("Zero"), m))
Some (translatedCtxt (mkSynCall "Zero" m []))

| OptionalSequential (JoinOrGroupJoinOrZipClause (_, _, _, _, _, mClause), _)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
<Compile Include="Language\XmlComments.fs" />
<Compile Include="Language\CompilerDirectiveTests.fs" />
<Compile Include="Language\CodeQuotationTests.fs" />
<Compile Include="Language\ComputationExpressionTests.fs" />
<Compile Include="ConstraintSolver\PrimitiveConstraints.fs" />
<Compile Include="ConstraintSolver\MemberConstraints.fs" />
<Compile Include="Interop\SimpleInteropTests.fs" />
Expand All @@ -57,8 +58,4 @@
<ProjectReference Include="$(FSharpSourcesRoot)\fsharp\FSharp.Core\FSharp.Core.fsproj" />
<ProjectReference Include="$(FSharpTestsRoot)\FSharp.Test.Utilities\FSharp.Test.Utilities.fsproj" />
</ItemGroup>

<ItemGroup>
<Folder Include="NewFolder\" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.

namespace FSharp.Compiler.ComponentTests.Language

open Xunit
open FSharp.Test.Utilities.Compiler

module ComputationExpressionTests =
[<Fact>]
let ``A CE not using Zero does not require Zero``() =
FSharp """
module ComputationExpressionTests
type ListBuilder () =
member __.Combine (a: List<'T>, b) = a @ b
member __.Yield x = List.singleton x
member __.Delay expr = expr () : List<'T>

let lb = ListBuilder ()

let x = lb {1; 2;}
"""
|> compile
|> shouldSucceed
|> ignore

[<Fact>]
let ``A CE explicitly using Zero fails without a defined Zero``() =
FSharp """
module ComputationExpressionTests
type ListBuilder () =
member __.Combine (a: List<'T>, b) = a @ b
member __.Yield x = List.singleton x
member __.Delay expr = expr () : List<'T>

let lb = ListBuilder ()

let x = lb {1; 2;()}
"""
|> compile
|> shouldFail
|> withSingleDiagnostic (Error 39, Line 10, Col 18, Line 10, Col 20, "The type 'ListBuilder' does not define the field, constructor or member 'Zero'.")
|> ignore

[<Fact>]
let ``A CE explicitly using Zero succeeds with a defined Zero``() =
FSharp """
module ComputationExpressionTests
type ListBuilder () =
member __.Zero () = []
member __.Combine (a: List<'T>, b) = a @ b
member __.Yield x = List.singleton x
member __.Delay expr = expr () : List<'T>

let lb = ListBuilder ()

let x = lb {1; 2;()}
"""
|> compile
|> shouldSucceed
|> ignore

[<Fact>]
let ``A CE with a complete if-then expression does not require Zero``() =
FSharp """
module ComputationExpressionTests
type ListBuilder () =
member __.Combine (a: List<'T>, b) = a @ b
member __.Yield x = List.singleton x
member __.Delay expr = expr () : List<'T>

let lb = ListBuilder ()

let x = lb {1; 2; if true then 3 else 4;}
"""
|> compile
|> shouldSucceed
|> ignore

[<Fact>]
let ``A CE with a missing/empty else branch implicitly requires Zero``() =
FSharp """
module ComputationExpressionTests
type ListBuilder () =
member __.Combine (a: List<'T>, b) = a @ b
member __.Yield x = List.singleton x
member __.Delay expr = expr () : List<'T>

let lb = ListBuilder ()

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