Skip to content

Commit d7c00c9

Browse files
authored
Merge pull request #9431 from dotnet/merges/master-to-release/dev16.7
Merge master to release/dev16.7
2 parents 3c3ccaf + 19c8cbc commit d7c00c9

File tree

8 files changed

+176
-31
lines changed

8 files changed

+176
-31
lines changed

src/fsharp/PatternMatchCompilation.fs

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@ open FSharp.Compiler
77
open FSharp.Compiler.AbstractIL.IL
88
open FSharp.Compiler.AbstractIL.Internal.Library
99
open FSharp.Compiler.AbstractIL.Diagnostics
10+
open FSharp.Compiler.AccessibilityLogic
1011
open FSharp.Compiler.CompilerGlobalState
1112
open FSharp.Compiler.ErrorLogger
13+
open FSharp.Compiler.InfoReader
1214
open FSharp.Compiler.Lib
15+
open FSharp.Compiler.MethodCalls
1316
open FSharp.Compiler.PrettyNaming
1417
open FSharp.Compiler.Range
1518
open FSharp.Compiler.SyntaxTree
@@ -746,7 +749,7 @@ let getDiscrim (EdgeDiscrim(_, discrim, _)) = discrim
746749

747750

748751
let CompilePatternBasic
749-
g denv amap exprm matchm
752+
(g: TcGlobals) denv amap tcVal infoReader exprm matchm
750753
warnOnUnused
751754
warnOnIncomplete
752755
actionOnFailure
@@ -793,10 +796,47 @@ let CompilePatternBasic
793796
mkReraise matchm resultTy
794797

795798
| Throw ->
796-
// We throw instead of rethrow on unmatched try-catch in a computation expression. But why?
797-
// Because this isn't a real .NET exception filter/handler but just a function we're passing
799+
let findMethInfo ty isInstance name (sigTys: TType list) =
800+
TryFindIntrinsicMethInfo infoReader matchm (AccessorDomain.AccessibleFromEverywhere) name ty
801+
|> List.tryFind (fun methInfo ->
802+
methInfo.IsInstance = isInstance &&
803+
(
804+
match methInfo.GetParamTypes(amap, matchm, []) with
805+
| [] -> false
806+
| argTysList ->
807+
let argTys = (argTysList |> List.reduce (@)) @ [ methInfo.GetFSharpReturnTy (amap, matchm, []) ]
808+
if argTys.Length <> sigTys.Length then
809+
false
810+
else
811+
(argTys, sigTys)
812+
||> List.forall2 (typeEquiv g)
813+
)
814+
)
815+
816+
// We use throw, or EDI.Capture(exn).Throw() when EDI is supported, instead of rethrow on unmatched try-catch in a computation expression.
817+
// But why? Because this isn't a real .NET exception filter/handler but just a function we're passing
798818
// to a computation expression builder to simulate one.
799-
mkThrow matchm resultTy (exprForVal matchm origInputVal)
819+
let ediCaptureMethInfo, ediThrowMethInfo =
820+
// EDI.Capture: exn -> EDI
821+
g.system_ExceptionDispatchInfo_ty
822+
|> Option.bind (fun ty -> findMethInfo ty false "Capture" [ g.exn_ty; ty ]),
823+
// edi.Throw: unit -> unit
824+
g.system_ExceptionDispatchInfo_ty
825+
|> Option.bind (fun ty -> findMethInfo ty true "Throw" [ g.unit_ty ])
826+
827+
match Option.map2 (fun x y -> x,y) ediCaptureMethInfo ediThrowMethInfo with
828+
| None ->
829+
mkThrow matchm resultTy (exprForVal matchm origInputVal)
830+
| Some (ediCaptureMethInfo, ediThrowMethInfo) ->
831+
let (edi, _) =
832+
BuildMethodCall tcVal g amap NeverMutates matchm false
833+
ediCaptureMethInfo ValUseFlag.NormalValUse [] [] [ (exprForVal matchm origInputVal) ]
834+
835+
let (e, _) =
836+
BuildMethodCall tcVal g amap NeverMutates matchm false
837+
ediThrowMethInfo ValUseFlag.NormalValUse [] [edi] [ ]
838+
839+
mkCompGenSequential matchm e (mkDefault (matchm, resultTy))
800840

801841
| ThrowIncompleteMatchException ->
802842
mkThrow matchm resultTy
@@ -1335,7 +1375,7 @@ let CompilePatternBasic
13351375
let isPartialOrWhenClause (c: TypedMatchClause) = isPatternPartial c.Pattern || c.GuardExpr.IsSome
13361376

13371377

1338-
let rec CompilePattern g denv amap exprm matchm warnOnUnused actionOnFailure (origInputVal, origInputValTypars, origInputExprOpt) (clausesL: TypedMatchClause list) inputTy resultTy =
1378+
let rec CompilePattern g denv amap tcVal infoReader exprm matchm warnOnUnused actionOnFailure (origInputVal, origInputValTypars, origInputExprOpt) (clausesL: TypedMatchClause list) inputTy resultTy =
13391379
match clausesL with
13401380
| _ when List.exists isPartialOrWhenClause clausesL ->
13411381
// Partial clauses cause major code explosion if treated naively
@@ -1345,13 +1385,13 @@ let rec CompilePattern g denv amap exprm matchm warnOnUnused actionOnFailure (o
13451385
let warnOnUnused = false // we can't turn this on since we're pretending all partials fail in order to control the complexity of this.
13461386
let warnOnIncomplete = true
13471387
let clausesPretendAllPartialFail = List.collect (fun (TClause(p, whenOpt, tg, m)) -> [TClause(erasePartialPatterns p, whenOpt, tg, m)]) clausesL
1348-
let _ = CompilePatternBasic g denv amap exprm matchm warnOnUnused warnOnIncomplete actionOnFailure (origInputVal, origInputValTypars, origInputExprOpt) clausesPretendAllPartialFail inputTy resultTy
1388+
let _ = CompilePatternBasic g denv amap tcVal infoReader exprm matchm warnOnUnused warnOnIncomplete actionOnFailure (origInputVal, origInputValTypars, origInputExprOpt) clausesPretendAllPartialFail inputTy resultTy
13491389
let warnOnIncomplete = false
13501390

13511391
let rec atMostOnePartialAtATime clauses =
13521392
match List.takeUntil isPartialOrWhenClause clauses with
13531393
| l, [] ->
1354-
CompilePatternBasic g denv amap exprm matchm warnOnUnused warnOnIncomplete actionOnFailure (origInputVal, origInputValTypars, origInputExprOpt) l inputTy resultTy
1394+
CompilePatternBasic g denv amap tcVal infoReader exprm matchm warnOnUnused warnOnIncomplete actionOnFailure (origInputVal, origInputValTypars, origInputExprOpt) l inputTy resultTy
13551395
| l, (h :: t) ->
13561396
// Add the partial clause.
13571397
doGroupWithAtMostOnePartial (l @ [h]) t
@@ -1372,10 +1412,10 @@ let rec CompilePattern g denv amap exprm matchm warnOnUnused actionOnFailure (o
13721412
// Make the clause that represents the remaining cases of the pattern match
13731413
let clauseForRestOfMatch = TClause(TPat_wild matchm, None, TTarget(List.empty, expr, spTarget), matchm)
13741414

1375-
CompilePatternBasic g denv amap exprm matchm warnOnUnused warnOnIncomplete actionOnFailure (origInputVal, origInputValTypars, origInputExprOpt) (group @ [clauseForRestOfMatch]) inputTy resultTy
1415+
CompilePatternBasic g denv amap tcVal infoReader exprm matchm warnOnUnused warnOnIncomplete actionOnFailure (origInputVal, origInputValTypars, origInputExprOpt) (group @ [clauseForRestOfMatch]) inputTy resultTy
13761416

13771417

13781418
atMostOnePartialAtATime clausesL
13791419

13801420
| _ ->
1381-
CompilePatternBasic g denv amap exprm matchm warnOnUnused true actionOnFailure (origInputVal, origInputValTypars, origInputExprOpt) clausesL inputTy resultTy
1421+
CompilePatternBasic g denv amap tcVal infoReader exprm matchm warnOnUnused true actionOnFailure (origInputVal, origInputValTypars, origInputExprOpt) clausesL inputTy resultTy

src/fsharp/PatternMatchCompilation.fsi

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ open FSharp.Compiler.TypedTree
88
open FSharp.Compiler.TypedTreeOps
99
open FSharp.Compiler.TcGlobals
1010
open FSharp.Compiler.Range
11+
open FSharp.Compiler.InfoReader
1112

1213
/// What should the decision tree contain for any incomplete match?
1314
type ActionOnFailure =
@@ -50,7 +51,10 @@ val ilFieldToTastConst: ILFieldInit -> Const
5051
val internal CompilePattern:
5152
TcGlobals ->
5253
DisplayEnv ->
53-
Import.ImportMap ->
54+
Import.ImportMap ->
55+
// tcVal
56+
(ValRef -> ValUseFlag -> TTypes -> range -> Expr * TType) ->
57+
InfoReader ->
5458
// range of the expression we are matching on
5559
range ->
5660
// range to report "incomplete match" on

src/fsharp/TcGlobals.fs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,6 +1062,9 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d
10621062
member val system_MarshalByRefObject_tcref = tryFindSysTyconRef sys "MarshalByRefObject"
10631063
member val system_MarshalByRefObject_ty = tryMkSysNonGenericTy sys "MarshalByRefObject"
10641064

1065+
member val system_ExceptionDispatchInfo_ty =
1066+
tryMkSysNonGenericTy ["System"; "Runtime"; "ExceptionServices"] "ExceptionDispatchInfo"
1067+
10651068
member __.system_Reflection_MethodInfo_ty = v_system_Reflection_MethodInfo_ty
10661069

10671070
member val system_Array_tcref = findSysTyconRef sys "Array"

src/fsharp/TypeChecker.fs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3249,7 +3249,7 @@ let GetMethodArgs arg =
32493249
//-------------------------------------------------------------------------
32503250

32513251
let CompilePatternForMatch cenv (env: TcEnv) mExpr matchm warnOnUnused actionOnFailure (inputVal, generalizedTypars, inputExprOpt) clauses inputTy resultTy =
3252-
let dtree, targets = CompilePattern cenv.g env.DisplayEnv cenv.amap mExpr matchm warnOnUnused actionOnFailure (inputVal, generalizedTypars, inputExprOpt) clauses inputTy resultTy
3252+
let dtree, targets = CompilePattern cenv.g env.DisplayEnv cenv.amap (LightweightTcValForUsingInBuildMethodCall cenv.g) cenv.infoReader mExpr matchm warnOnUnused actionOnFailure (inputVal, generalizedTypars, inputExprOpt) clauses inputTy resultTy
32533253
mkAndSimplifyMatch NoDebugPointAtInvisibleBinding mExpr matchm resultTy dtree targets
32543254

32553255
/// Compile a pattern
@@ -5535,7 +5535,6 @@ and TcPat warnOnUpper cenv env topValInfo vFlags (tpenv, names, takenNames) ty p
55355535
errorR (Error (FSComp.SR.tcUnionCaseDoesNotTakeArguments (), m))
55365536
[], args
55375537

5538-
55395538
| arg :: rest when numArgTys = 1 ->
55405539
if numArgTys = 1 && not (List.isEmpty rest) then
55415540
errorR (Error (FSComp.SR.tcUnionCaseRequiresOneArgument (), m))
@@ -5544,23 +5543,24 @@ and TcPat warnOnUpper cenv env topValInfo vFlags (tpenv, names, takenNames) ty p
55445543
| [arg] -> [arg], []
55455544

55465545
| args ->
5547-
errorR (Error (FSComp.SR.tcUnionCaseExpectsTupledArguments numArgTys, m))
55485546
[], args
55495547

55505548
let args, extraPatterns =
55515549
let numArgs = args.Length
55525550
if numArgs = numArgTys then
55535551
args, extraPatterns
5552+
elif numArgs < numArgTys then
5553+
if numArgTys > 1 then
5554+
// Expects tuple without enough args
5555+
errorR (Error (FSComp.SR.tcUnionCaseExpectsTupledArguments numArgTys, m))
5556+
else
5557+
errorR (UnionCaseWrongArguments (env.DisplayEnv, numArgTys, numArgs, m))
5558+
args @ (List.init (numArgTys - numArgs) (fun _ -> SynPat.Wild (m.MakeSynthetic()))), extraPatterns
55545559
else
5555-
if numArgs < numArgTys then
5556-
if numArgs <> 0 && numArgTys <> 0 then
5557-
errorR (UnionCaseWrongArguments (env.DisplayEnv, numArgTys, numArgs, m))
5558-
args @ (List.init (numArgTys - numArgs) (fun _ -> SynPat.Wild (m.MakeSynthetic()))), extraPatterns
5559-
else
5560-
let args, remaining = args |> List.splitAt numArgTys
5561-
for remainingArg in remaining do
5562-
errorR (UnionCaseWrongArguments (env.DisplayEnv, numArgTys, numArgs, remainingArg.Range))
5563-
args, extraPatterns @ remaining
5560+
let args, remaining = args |> List.splitAt numArgTys
5561+
for remainingArg in remaining do
5562+
errorR (UnionCaseWrongArguments (env.DisplayEnv, numArgTys, numArgs, remainingArg.Range))
5563+
args, extraPatterns @ remaining
55645564

55655565
let extraPatterns = extraPatterns @ extraPatternsFromNames
55665566
let args', acc = TcPatterns warnOnUpper cenv env vFlags (tpenv, names, takenNames) argTys args
@@ -8571,6 +8571,7 @@ and TcComputationExpression cenv env overallTy mWhole (interpExpr: Expr) builder
85718571

85728572
// 'match! expr with pats ...' --> build.Bind(e1, (function pats ...))
85738573
| SynExpr.MatchBang (spMatch, expr, clauses, m) ->
8574+
let matchExpr = mkSourceExpr expr
85748575
let mMatch = match spMatch with DebugPointAtBinding mMatch -> mMatch | _ -> m
85758576
if isQuery then error(Error(FSComp.SR.tcMatchMayNotBeUsedWithQuery(), mMatch))
85768577

@@ -8581,7 +8582,7 @@ and TcComputationExpression cenv env overallTy mWhole (interpExpr: Expr) builder
85818582
let consumeExpr = SynExpr.MatchLambda (false, mMatch, clauses, spMatch, mMatch)
85828583

85838584
// TODO: consider allowing translation to BindReturn
8584-
Some(translatedCtxt (mkSynCall "Bind" mMatch [expr; consumeExpr]))
8585+
Some(translatedCtxt (mkSynCall "Bind" mMatch [matchExpr; consumeExpr]))
85858586

85868587
| SynExpr.TryWith (innerComp, _mTryToWith, clauses, _mWithToLast, mTryToLast, spTry, _spWith) ->
85878588
let mTry = match spTry with DebugPointAtTry.Yes m -> m | _ -> mTryToLast
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information.
2+
namespace FSharp.Compiler.UnitTests.CodeGen.EmittedIL
3+
4+
open FSharp.Compiler.UnitTests
5+
open NUnit.Framework
6+
open FSharp.TestHelpers
7+
8+
[<TestFixture>]
9+
module CeEdiThrow =
10+
11+
[<Test>]
12+
let ``Emits EDI.Throw``() =
13+
CompilerAssert.CompileLibraryAndVerifyIL
14+
"""
15+
module CE
16+
17+
open System
18+
type Try() =
19+
member _.Return i = i
20+
member _.Delay f = f
21+
member _.Run f = f()
22+
member _.TryWith(body : unit -> int, catch : exn -> int) =
23+
try body() with ex -> catch ex
24+
25+
let foo = Try(){
26+
try return invalidOp "Ex"
27+
with :? ArgumentException -> return 1
28+
}
29+
"""
30+
(fun verifier -> verifier.VerifyIL [
31+
"""
32+
.method public strict virtual instance int32
33+
Invoke(class [runtime]System.Exception _arg1) cil managed
34+
{
35+
36+
.maxstack 5
37+
.locals init (class [runtime]System.ArgumentException V_0)
38+
IL_0000: ldarg.1
39+
IL_0001: isinst [runtime]System.ArgumentException
40+
IL_0006: stloc.0
41+
IL_0007: ldloc.0
42+
IL_0008: brfalse.s IL_000c
43+
44+
IL_000a: ldc.i4.1
45+
IL_000b: ret
46+
47+
IL_000c: ldarg.1
48+
IL_000d: call class [runtime]System.Runtime.ExceptionServices.ExceptionDispatchInfo [runtime]System.Runtime.ExceptionServices.ExceptionDispatchInfo::Capture(class [runtime]System.Exception)
49+
IL_0012: callvirt instance void [runtime]System.Runtime.ExceptionServices.ExceptionDispatchInfo::Throw()
50+
IL_0017: ldc.i4.0
51+
IL_0018: ret
52+
}
53+
"""
54+
])

tests/fsharp/Compiler/Language/ComputationExpressionTests.fs

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@ open FSharp.Compiler.SourceCodeServices
77
[<TestFixture>]
88
module ComputationExpressionTests =
99

10-
[<Test>]
11-
let ``do-bang can be used with nested CE expressions``() =
12-
let code = """
10+
let ``complex CE with source member and applicatives`` ceUsage =
11+
sprintf """
1312
module Code
1413
type ResultBuilder() =
1514
member __.Return value = Ok value
@@ -29,10 +28,12 @@ module Result =
2928
| Ok x1res, Ok x2res -> Ok (x1res, x2res)
3029
| Error e, _ -> Error e
3130
| _, Error e -> Error e
31+
3232
let ofChoice c =
3333
match c with
3434
| Choice1Of2 x -> Ok x
3535
| Choice2Of2 x -> Error x
36+
3637
let fold onOk onError r =
3738
match r with
3839
| Ok x -> onOk x
@@ -49,9 +50,10 @@ module Async =
4950
}
5051
5152
module AsyncResult =
52-
let zip x1 x2 =
53+
let zip x1 x2 =
5354
Async.zip x1 x2
5455
|> Async.map(fun (r1, r2) -> Result.zip r1 r2)
56+
5557
let foldResult onSuccess onError ar =
5658
Async.map (Result.fold onSuccess onError) ar
5759
@@ -101,7 +103,7 @@ type AsyncResultBuilder() =
101103
compensation: unit -> unit)
102104
: Async<Result<'T, 'TError>> =
103105
async.TryFinally(computation, compensation)
104-
106+
105107
member __.Using
106108
(resource: 'T when 'T :> System.IDisposable,
107109
binder: 'T -> Async<Result<'U, 'TError>>)
@@ -127,6 +129,7 @@ type AsyncResultBuilder() =
127129
128130
member inline _.Source(result : Async<Result<_,_>>) : Async<Result<_,_>> = result
129131
132+
[<AutoOpen>]
130133
module ARExts =
131134
type AsyncResultBuilder with
132135
/// <summary>
@@ -151,9 +154,14 @@ module ARExts =
151154
/// Method lets us transform data types into our internal representation.
152155
/// </summary>
153156
member inline __.Source(asyncComputation : Async<_>) : Async<Result<_,_>> = asyncComputation |> Async.map Ok
154-
157+
155158
let asyncResult = AsyncResultBuilder()
156159
160+
%s""" ceUsage
161+
162+
[<Test>]
163+
let ``do-bang can be used with nested CE expressions``() =
164+
let code = ``complex CE with source member and applicatives`` """
157165
asyncResult {
158166
let! something = asyncResult { return 5 }
159167
do! asyncResult {
@@ -165,4 +173,21 @@ asyncResult {
165173
|> Async.RunSynchronously
166174
|> printfn "%d"
167175
"""
168-
CompilerAssert.Pass code
176+
CompilerAssert.Pass code
177+
178+
[<Test>]
179+
let ``match-bang should apply source transformations to its inputs`` () =
180+
let code = ``complex CE with source member and applicatives`` """
181+
asyncResult {
182+
// if the source transformation is not applied, the match will not work,
183+
// because match! is only defined in terms of let!, and the only
184+
// bind overload provided takes AsyncResult as its input.
185+
match! Ok 5 with
186+
| 5 -> return "ok"
187+
| n -> return! (Error (sprintf "boo %d" n))
188+
}
189+
|> AsyncResult.foldResult id (fun (err: string) -> err)
190+
|> Async.RunSynchronously
191+
|> printfn "%s"
192+
"""
193+
CompilerAssert.Pass code

tests/fsharp/FSharpSuite.Tests.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
<Compile Include="Compiler\CodeGen\EmittedIL\LiteralValue.fs" />
3333
<Compile Include="Compiler\CodeGen\EmittedIL\Mutation.fs" />
3434
<Compile Include="Compiler\CodeGen\EmittedIL\TailCalls.fs" />
35+
<Compile Include="Compiler\CodeGen\EmittedIL\CeEdiThrow.fs" />
3536
<Compile Include="Compiler\Conformance\DataExpressions\ComputationExpressions.fs" />
3637
<Compile Include="Compiler\Conformance\BasicGrammarElements\BasicConstants.fs" />
3738
<Compile Include="Compiler\ErrorMessages\ConstructorTests.fs" />

tests/service/PatternMatchCompilationTests.fs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ match A with
8080
"""
8181
assertHasSymbolUsages ["x"; "y"] checkResults
8282
dumpErrors checkResults |> shouldEqual [
83-
"(7,2--7,10): This constructor is applied to 2 argument(s) but expects 3"
83+
"(7,2--7,10): This union case expects 3 arguments in tupled form"
8484
"(6,6--6,7): Incomplete pattern matches on this expression. For example, the value 'A' may indicate a case not covered by the pattern(s)."
8585
]
8686

@@ -257,6 +257,23 @@ match TraceLevel.Off with
257257
]
258258

259259

260+
[<Test>]
261+
let ``Caseless DU`` () =
262+
let _, checkResults = getParseAndCheckResults """
263+
type DU = Case of int
264+
265+
let f du =
266+
match du with
267+
| Case -> ()
268+
269+
let dowork () =
270+
f (Case 1)
271+
0 // return an integer exit code"""
272+
assertHasSymbolUsages ["DU"; "dowork"; "du"; "f"] checkResults
273+
dumpErrors checkResults |> shouldEqual [
274+
"(6,6--6,10): This constructor is applied to 0 argument(s) but expects 1"
275+
]
276+
260277
[<Test>]
261278
let ``Or 01 - No errors`` () =
262279
let _, checkResults = getParseAndCheckResults """

0 commit comments

Comments
 (0)