Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RuntimeWrappedException cannot be caught #18374

Open
IS4Code opened this issue Mar 11, 2025 · 1 comment
Open

RuntimeWrappedException cannot be caught #18374

IS4Code opened this issue Mar 11, 2025 · 1 comment

Comments

@IS4Code
Copy link

IS4Code commented Mar 11, 2025

When a non-Exception object is thrown (from CIL or other languages that support it), it cannot be caught in F# by default.

Repro steps

let throwobj (x:obj) = (# "throw" x #) // or any other code that throws a non-Exception instance

try
  throwobj "test"
with
| e -> printf "%O" e

Expected behavior

The exception should be caught either as System.String or System.Runtime.CompilerServices.RuntimeWrappedException.

Actual behavior

Unhandled exception. System.InvalidCastException: Unable to cast object of type 'System.String' to type 'System.Exception'.

This is caused by the generated exception handler:

.try
{
    ldstr "test"
    throw
    // ...
}
catch [netstandard]System.Object
{
    castclass [System.Runtime]System.Exception // InvalidCastException
    stloc.0
    // ...
}

F# here catches everything, but the cast to Exception throws another exception since the object is not an Exception.

Known workarounds

The code above succeeds if the attribute that controls how wrapped exceptions are exposed is used explicitly:

[<assembly: System.Runtime.CompilerServices.RuntimeCompatibility(WrapNonExceptionThrows = true)>]do()

Related information

Throwing a RuntimeWrappedException manually does not exhibit this behaviour because the runtime does not treat it as a special case then and does not unwrap the value.

Environment: https://sharplab.io/#v2:DYLgZgzgNALiCWwA+wCmMAEMAWAnA9gO74BGAVhgBQAeIpZAlBgLxUDEGARDgYZxtQxsGAWABQ4mLgCe4jFjxF6XGKggxO4wvBzikGVBgC0APgwAHXPAB2MMFwCkAeX6ogA=

@IS4Code
Copy link
Author

IS4Code commented Mar 11, 2025

There are two options how I see this being addressed:

  • Apply RuntimeCompatibilityAttribute implicitly. This makes it behave exactly like C# in this situation.
  • Change castclass to isinst. It is however unclear how the exception should be exposed/caught in that case, since the language applies the catch patterns on the Exception type, not obj.

I think the first option is the safest one. It is a potential breaking change if people rely on try-with throwing an InvalidCastException in this case, but I don't think that is the supposed behaviour.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: New
Development

No branches or pull requests

1 participant