Runtime async leaves an outer-owned reraise() unchanged when it is inside a nested try/with protected body. Moving the suspending outer handler out of its catch region then leaves an orphan IL rethrow.
Repro steps
Save as Reraise.fs and compile as a library using the compiler and matching net10.0 FSharp.Core built from main e61631709fadcb83a90d336852c650e8c86b0189.
module Reraise
open System
open System.IO
open System.Threading.Tasks
open System.Runtime.CompilerServices
open Microsoft.FSharp.Core.CompilerServices.StateMachineHelpers
let recover (original: exn) (audit: Task) : Task<int> =
__runtimeAsyncReturn (
try
raise original
with _ ->
AsyncHelpers.Await audit
try
reraise ()
with :? IOException ->
7)
dotnet artifacts/bin/fsc/Release/net11.0/fsc.dll \
--targetprofile:netcore --langversion:preview --optimize+ --target:library \
-r:artifacts/bin/FSharp.Core/Release/net10.0/FSharp.Core.dll \
-o:Reraise.dll Reraise.fs
Compile and inspect only; do not execute the invalid output.
Expected behavior
After audit completes, the nested handler returns 7 for an IOException. Other exceptions propagate as the original exception object. The protected-body rethrow must refer to the outer catch; the inner handler keeps its own exception scope.
Actual behavior
Compilation succeeds, but recover contains rethrow at IL_0040, outside every catch/filter handler. The handler ranges are [0015,0024) and [0063,0086). The separate rethrow at IL_007B is inside the inner handler and is legal.
A region-aware component assertion fails with:
recover: orphan rethrow at IL_0040
Known workarounds
Match the caught exception directly. For an unhandled exception, ExceptionDispatchInfo.Capture(error).Throw() avoids the orphan rethrow. The pending-audit, exception-identity and IOException fallback controls pass.
Related information
Reproduced after #20235 on main e6163170, with a freshly built compiler/Core and .NET 11.0.0-rc.1.26420.103 on macOS ARM64. This is accepted low-level intrinsic code. It is separate from #20405, which adds support for currently rejected CE-handler reraise forms; no FS0413 language change is requested here.
Runtime async leaves an outer-owned
reraise()unchanged when it is inside a nestedtry/withprotected body. Moving the suspending outer handler out of its catch region then leaves an orphan ILrethrow.Repro steps
Save as
Reraise.fsand compile as a library using the compiler and matching net10.0 FSharp.Core built from maine61631709fadcb83a90d336852c650e8c86b0189.Compile and inspect only; do not execute the invalid output.
Expected behavior
After
auditcompletes, the nested handler returns7for an IOException. Other exceptions propagate as the original exception object. The protected-body rethrow must refer to the outer catch; the inner handler keeps its own exception scope.Actual behavior
Compilation succeeds, but
recovercontainsrethrowat IL_0040, outside every catch/filter handler. The handler ranges are[0015,0024)and[0063,0086). The separaterethrowat IL_007B is inside the inner handler and is legal.A region-aware component assertion fails with:
Known workarounds
Match the caught exception directly. For an unhandled exception,
ExceptionDispatchInfo.Capture(error).Throw()avoids the orphan rethrow. The pending-audit, exception-identity and IOException fallback controls pass.Related information
Reproduced after #20235 on main
e6163170, with a freshly built compiler/Core and .NET11.0.0-rc.1.26420.103on macOS ARM64. This is accepted low-level intrinsic code. It is separate from #20405, which adds support for currently rejected CE-handlerreraiseforms; no FS0413 language change is requested here.