fsharp icon indicating copy to clipboard operation
fsharp copied to clipboard

Incoherent behavior when using a task with a unit returning statement, between Release and Debug

Open jp-fournier-dev opened this issue 3 years ago • 2 comments

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:

  1. Run the attached code in debug, see that it does raise the exception with failwith using : dotnet run --configuration Debug
  2. 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

jp-fournier-dev avatar May 04 '22 19:05 jp-fournier-dev

To add some context :

  • 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

jp-fournier-dev avatar May 04 '22 19:05 jp-fournier-dev

It seems that this function is not even called. state machine's MoveNext method from original code: image

when that line is changed to someFunctionWithReturnType() |> ignore image

En3Tho avatar May 04 '22 20:05 En3Tho