You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Incoherent behavior when using a task with a unit returning statement, between Release and Debug #13099
Please provide a succinct description of the issue.
When executing inside a task, a function that raises an exception is not called if the resulting expression ends in a property that has a unit value.
Provide the steps required to reproduce the problem:
Run the attached code in debug, see that it does raise the exception with failwith using : dotnet run --configuration Debug
Run the attached code in release, see that it does not raise the exception using : dotnet run --configuration Release
printfn "Hello from F#"
type SomeOutputType() =
member x.End = ()
let someFunctionWithReturnType () =
failwith "This should be raised"
SomeOutputType()
let theTaskAtHand () =
task {
(someFunctionWithReturnType ()).End
}
theTaskAtHand().Wait()
Provide a description of the expected behavior.
In both debug and release, the program fails after the exception is raised
Provide a description of the actual behavior observed.
In release, the exception is essentially ignored and nothing happens. Program exits successfully
In debug, the exception is raised and the program fails
Provide a description of any known workarounds.
You can pretty easily change this by switching it to being a function like so : member x.End = ignore and calling said function.
Using ``member x.End() = ()` will not work
Doing so inside an async computation expression will result in the expected behavior with the original code
Provide any related information:
Operating system: Windows 10
NET Runtime kind : .NET 6.0.201
this was discovered in some tests where we end a fluent assertion with an .End property that results in the value (). This isn't amazing code, but it does provide some level of clarity better than having ignore
for me (and from my understanding) the issue here is that we don't get the same behavior, especially since the compiler has removed a statement that could contain important processes. In my use case, it would skip assertions entirely.
This seems more like and edge case, but still offers plenty of surprise when it happens
typeSomeOutputType()=memberx.End=()letsomeFunctionWithReturnType()=
failwith "This should be raised"
SomeOutputType()lettheTaskAtHand()=task{(someFunctionWithReturnType ()).End
}try
theTaskAtHand().Wait()
printfn "BUG: Exception was NOT raised!"with|:? System.AggregateException as e ->
printfn "PASS: Exception raised: %s"(e.InnerException.Message)
Mode
Output
dotnet run -c Debug
PASS: Exception raised: This should be raised ✅
dotnet run -c Release
BUG: Exception was NOT raised! ❌
Root cause: The optimizer eliminates the call to someFunctionWithReturnType() because the only use of the result is accessing .End (a unit-valued property). Since .End is a constant (), the optimizer treats the entire expression as dead code — but someFunctionWithReturnType() has side effects (the failwith). This is a correctness bug in the optimizer's side-effect analysis for method calls whose results flow only into unit-typed property access.
This has been reproducible since .NET 6 (original report, 2022) through the current .NET 10 / F# 11.0.
Activity
jp-fournier-dev commentedon May 4, 2022
To add some context :
.Endproperty that results in the value(). This isn't amazing code, but it does provide some level of clarity better than havingignoreThis seems more like and edge case, but still offers plenty of surprise when it happens
En3Tho commentedon May 4, 2022
It seems that this function is not even called.

state machine's MoveNext method from original code:
when that line is changed to someFunctionWithReturnType() |> ignore

11 remaining items
github-actions commentedon Apr 21, 2026
🤖 This is an automated response from Repo Assist.
Confirmed still present in F# 11.0 (FSI 15.2.101.0, commit
2535da2, 2026-04-20).Verified reproduction — Debug raises the exception, Release silently skips it:
dotnet run -c DebugPASS: Exception raised: This should be raised✅dotnet run -c ReleaseBUG: Exception was NOT raised!❌Root cause: The optimizer eliminates the call to
someFunctionWithReturnType()because the only use of the result is accessing.End(a unit-valued property). Since.Endis a constant(), the optimizer treats the entire expression as dead code — butsomeFunctionWithReturnType()has side effects (thefailwith). This is a correctness bug in the optimizer's side-effect analysis for method calls whose results flow only into unit-typed property access.This has been reproducible since .NET 6 (original report, 2022) through the current .NET 10 / F# 11.0.
Add release notes entry for issue #13099 optimizer fix
Narrow optimizer rule to IsMemberThisVal only; revert broad baseline …
Move #13099 fix to root cause in state machine lowering
Trim comment bloat in #13099 fix and tests
Scope #13099 preservation to effectful member-access receiver temps
Fix #13099: optimizer drops side-effectful receiver of unit member ac…
[automated] Merge branch 'main' => 'feature/net11-scouting' (#20039)
[feature/net11-scouting] Update dependencies from dotnet/arcade (#20034)