-
Notifications
You must be signed in to change notification settings - Fork 830
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
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:
ldarg.1 // valuetype [FSharp.Core]Microsoft.FSharp.Core.FSharpResult`2<class [FSharp.Core]Microsoft.FSharp.Core.Unit, string>
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
Metadata
Metadata
Assignees
Labels
Type
Projects
Status