dotnet / dotnet/fsharp

Using computation builder `TryWith` with a non-`Exception` parameter generates invalid IL

Open
#19,061 8 comments 0 reactions 0 assignees View on GitHub
Area-ComputationExpressions Bug
Dominant language
F#
Stars
4.3k
Forks
876
Avg merge
4d 22h
Merged PRs (30d)
144

Description

When a computation builder defines a `TryWith` method whose exception handler does not actually take an `Exception` as its parameter, an invalid CIL is produced from such a `try`/`with` construction.

**Repro steps**

```fs
type ResultBuilder() =
member _.Delay(f : _ -> Result<_, _>) = f
member _.Run(f : _ -> Result<_, _>) = f()

member _.Zero() = Ok()
member _.Return(value) = Ok value
member _.ReturnFrom(result : Result<_, _>) = result
member _.Bind(result : Result<_, _>, f) = Result.bind f result
member _.Combine(result : Result<_, _>, f) = Result.bind f result

member _.TryWith(f : unit -> _, fail : _ -> _) =
let result = f()
match result with
| Ok _ -> result
| Error _ -> fail result

let result = ResultBuilder()

let test() =
result {
try
do! Error "test"
with
| Error msg -> System.Console.WriteLine msg
}

let _ = test()
```

**Expected behavior**

The code should either fail to compile, or compile to a program that runs and behaves as expected, i.e. prints "test".

**Actual behavior**

The code compiles but fails at runtime with:
> System.InvalidProgramException: Common Language Runtime detected an invalid program.

This is caused by the construction of `ExceptionDispatchInfo` in case the "exception" is not matched:
```cil
ldarg.1 // valuetype [FSharp.Core]Microsoft.FSharp.Core.FSharpResult`2
call class [System.Runtime]System.Runtime.ExceptionServices.ExceptionDispatchInfo [System.Runtime]System.Runtime.ExceptionServices.ExceptionDispatchInfo::Capture(class [System.Runtime]System.Exception)
callvirt instance void [System.Runtime]System.Runtime.ExceptionServices.ExceptionDispatchInfo::Throw()
```
This attempts to use a `Result` argument instead of `Exception`, resulting in an invalid program.

**Known workarounds**

Using actual exceptions for this purpose.

**Related information**

Environment: .NET 9.0.303, [sharplab.io](https://sharplab.io/#v2:EYLgxg9gTgpgtADwGwBYA0AbEAzAzmgFxAEsMAfAgTwAcYACAJRlwFcMCAhF0gExigAUASjoBeALAAoOnQC2MWcH50A+gDoAIjAwBDSgOx0QqunAB8jZmwIAeFWlVmRoutikz5i5eoYsAdgZGJuaWrOx2DipOYq7CUu5yCkpQqmoAWvwQwjEA8gDWcdKJXik+MAQsUAEAbjoYLDDOdPl0tfUwCZ7JqUwVVQBiUBCyArBhBEFM4xGOTWPWnUneahzEfjyjVuyTW7b2jg7YTVPWasBrPK508+yLJakAwsPnfjCb4zvT+1GHx7tnFyuNwICTu3XUABUoJQAOrEAgAC0Cxn88NMFn22B0pCCKnRqmcCRkGHK112MWwhRkHh0BDACLJHwA7vCEUS6GRmnlghZgezOQBRKBDUr4rE4vmSKQkibAmIndhcXj8QrS0kEZgEbISaRygDedCJBGhhqKMh4EAAhHQhSK6AAiDW4Aj2oksxFEwXC6ByXAAc3xAGVKM6FGonn5cBASWoYVB4TAADJreiyf3uAC+8UkMpMLidWqEQA)

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.