dotnet / fsharp

The F# compiler, F# core library, F# language service, and F# tooling integration for Visual Studio
https://dotnet.microsoft.com/languages/fsharp
MIT License
3.82k stars 773 forks source link

Incorrect TailCall warning on async recursive functions #17237

Closed kdurkin77 closed 1 week ago

kdurkin77 commented 1 month ago

When we use the TailCall attribute on an async recursive function, that actually has an async call, we get a warning that it's not being used in a tail recursive way, but it should be

Examples below:

[<TailCall>] //Has Warning
let rec f'1 (g: bool Async) = async {
    match! g with
    | false -> ()
    | true -> return! f'1 g
    }

[<TailCall>] //Has Warning
let rec f'2 (g: bool Async) = async {
    let! x = g
    match x with
    | false -> ()
    | true -> return! f'2 g
    }

[<TailCall>] //No Warning
let rec f'3 (g: bool) = async {
    match g with
    | false -> ()
    | true -> return! f'3 g
    }

Repro steps

  1. Create an f# project
  2. Create a recursive function that has an async call and use the TailCall attribute.
  3. Build, and notice the warning

Sample sln: TailCallTest.zip

Expected behavior No warnings

Actual behavior Warnings display in output

warning FS3569: The member or function 'f'1' has the 'TailCallAttribute' attribute, but is not being used in a tail recursive way. warning FS3569: The member or function 'f'2' has the 'TailCallAttribute' attribute, but is not being used in a tail recursive way.

Known workarounds Remove the TailCall attribute, but I am unsure if this is actually running as a TailCall

Related information

Provide any related information (optional): Occurs in .net8.0, .net6.0, .net5.0 Visual Studio 17.9.7 and 17.10.0

dawedawe commented 1 month ago

I'll take a look in the next couple of days.

vzarytovskii commented 1 month ago

This likely happens because of how codegen works for binding. Which is pretty much just continuation style for async.