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

Fix missing TailCall warnings in TryWith/TryFinally #16328

Merged
merged 7 commits into from
Nov 24, 2023

Conversation

dawedawe
Copy link
Contributor

Fixes #16322 by adding the missing checks for TOp.TryWith and TOp.TryFinally.
Add a good amount of test cases.

Thanks again to @jwosty for raising the issue. I really had a blind spot there while I worked on this.

@dawedawe dawedawe requested a review from a team as a code owner November 22, 2023 16:24
Copy link
Member

@psfinaki psfinaki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

Copy link
Contributor

@jwosty jwosty left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice; that was fast!

Original comment

These could some good test cases, too. They already work in the release but it might be good to have them too to protect against regressions. Nevermind, I see that you already did this. Disregard this comment

// should warn
[<TailCall>]
let rec retryAsyncNonTc nRetries (work: unit -> 'a) : Result<'a, Exception list> = 
    try
        Ok (work ())
    with e ->
        match retryAsyncNonTc (nRetries - 1) work with
        | Ok _ as result -> result
        | Error subExns ->
            raise (AggregateException(e::subExns))
// should not warn
[<TailCall>]
let rec retryAsync prevExns nRetries (work: unit -> 'a) : 'a = 
    try
        work ()
    with e ->
        let exns = e::prevExns
        if nRetries < 1 then
            raise (AggregateException(List.rev exns))
        else
            retryAsync exns (nRetries - 1) work

Copy link
Contributor

@jwosty jwosty left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do actually wonder about Task though. Shouldn't a warning be emitted for those as well, since Task tailcalls aren't supported? Correct me if I'm wrong.

// should emit warning, I think?
[<TailCall>] 
let rec f x = task {
    let y = x - 1
    let z = y - 1
    return! f (z - 1)
}

@dawedawe
Copy link
Contributor Author

I do actually wonder about Task though. Shouldn't a warning be emitted for those as well, since Task tailcalls aren't supported? Correct me if I'm wrong.

You're right, the function you showed crashes with a StackOverflow.
We do already warn about it in the v8 release. I added the test to catch regressions.

Copy link
Contributor

@jwosty jwosty left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work!

While we're still here, a test for use bindings might be good too, for more regression protection:

[<TailCall>]
let rec f () =
    use x = makeDisposable()
    f ()

@dawedawe
Copy link
Contributor Author

While we're still here, a test for use bindings might be good too, for more regression protection:

Oh yeah, good idead. Pushed it.

@Martin521
Copy link
Contributor

@dawedawe
Not related to this PR, but I think the namespace should be "ErrorMessages", not "FSharp.Compiler.ComponentTests.ErrorMessages", else the tests appear in test explorers in a weird place.

@dawedawe
Copy link
Contributor Author

@dawedawe Not related to this PR, but I think the namespace should be "ErrorMessages", not "FSharp.Compiler.ComponentTests.ErrorMessages", else the tests appear in test explorers in a weird place.

Oh good catch, thanks! I changed that while here.

@T-Gro T-Gro merged commit 16e7287 into dotnet:main Nov 24, 2023
@dawedawe dawedawe deleted the fix-16322_tailcall_in_try branch November 24, 2023 10:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

TailCallAttribute does warn on non-tail-recursive async function with try-with or try-finally block
5 participants