Skip to content

Commit f375e08

Browse files
NinoFlorisbaronfel
authored andcommitted
Replace throw with EDI throw in CE catch handlers, fixes #8529 (#8882)
* Replace throw with EDI throw in CE catch handlers, fixes #8529 * Address feedback, move to EDI.Capture(exn).Throw() for netfx compatibility
1 parent 6856d2c commit f375e08

File tree

4 files changed

+58
-11
lines changed

4 files changed

+58
-11
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: 1 addition & 1 deletion
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

0 commit comments

Comments
 (0)