Skip to content

Commit 66cd512

Browse files
committed
Merge remote-tracking branch 'upstream/master' into new-testing-proposal
2 parents 34d43b2 + 4dd0741 commit 66cd512

15 files changed

Lines changed: 434 additions & 177 deletions

File tree

fcs/FSharp.Compiler.Service/FSharp.Compiler.Service.fsproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<Project Sdk="Microsoft.NET.Sdk">
33
<Import Project="..\netfx.props" />
44
<Import Project="..\..\src\buildtools\buildtools.targets" />
5+
<Import Project="..\..\eng\Versions.props" />
56
<PropertyGroup>
67
<TargetFrameworks>$(FcsTargetNetFxFramework);netstandard2.0</TargetFrameworks>
78
<DisableImplicitFSharpCoreReference>true</DisableImplicitFSharpCoreReference>

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: 15 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
@@ -8884,6 +8885,7 @@ and TcComputationExpression cenv env overallTy mWhole (interpExpr: Expr) builder
88848885
clauses |> List.forall (fun (Clause(_, _, clauseComp, _, _)) -> isSimpleExpr clauseComp)
88858886
| SynExpr.YieldOrReturnFrom _ -> false
88868887
| SynExpr.YieldOrReturn _ -> false
8888+
| SynExpr.DoBang _ -> false
88878889
| _ -> true
88888890

88898891
let basicSynExpr =

tests/FSharp.Test.Utilities/CompilerAssert.fs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,7 @@ let main argv = 0"""
419419
static member ExecutionHasOutput(cmpl: Compilation, expectedOutput: string) =
420420
CompilerAssert.Execute(cmpl, newProcess = true, onOutput = (fun output -> Assert.AreEqual(expectedOutput, output)))
421421

422+
/// Assert that the given source code compiles with the `defaultProjectOptions`, with no errors or warnings
422423
static member Pass (source: string) =
423424
lock gate <| fun () ->
424425
let parseResults, fileAnswer = checker.ParseAndCheckFileInProject("test.fs", 0, SourceText.ofString source, defaultProjectOptions) |> Async.RunSynchronously
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+
])

0 commit comments

Comments
 (0)