Skip to content

Commit 12e33ce

Browse files
authored
Fix a false negative of the [<TailCall>] analysis in combination with async (issue #17237) (#17241)
1 parent 3216e8d commit 12e33ce

File tree

3 files changed

+52
-1
lines changed

3 files changed

+52
-1
lines changed

docs/release-notes/.FSharp.Compiler.Service/8.0.400.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
### Fixed
22

3+
* Fix a false positive of the `[<TailCall>]` analysis in combination with async. ([Issue #17237](https://github.com/dotnet/fsharp/issues/17237), [PR #17241](https://github.com/dotnet/fsharp/pull/17241))
34
* Extended #help directive in fsi to show documentation in the REPL. ([PR #17140](https://github.com/dotnet/fsharp/pull/17140))
45
* Fix internal error when dotting into delegates with multiple type parameters. ([PR #17227](https://github.com/dotnet/fsharp/pull/17227))
56
* Error for partial implementation of interface with static and non-static abstract members. ([Issue #17138](https://github.com/dotnet/fsharp/issues/17138), [PR #17160](https://github.com/dotnet/fsharp/pull/17160))

src/Compiler/Checking/TailCallChecks.fs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -756,7 +756,18 @@ let CheckModuleBinding cenv (isRec: bool) (TBind _ as bind) =
756756
| Expr.Lambda(bodyExpr = bodyExpr) -> checkTailCall insideSubBindingOrTry bodyExpr
757757
| Expr.DebugPoint(_debugPointAtLeafExpr, expr) -> checkTailCall insideSubBindingOrTry expr
758758
| Expr.Let(binding = binding; bodyExpr = bodyExpr) ->
759-
checkTailCall true binding.Expr
759+
// detect continuation shapes like MakeAsync
760+
let isContinuation =
761+
match bodyExpr with
762+
| Expr.App(funcExpr = Expr.Val(valRef = valRef)) ->
763+
match valRef.GeneralizedType with
764+
| [ _ ],
765+
TType_fun(domainType = TType_fun(domainType = TType_app _; rangeType = TType_app _); rangeType = TType_app _) ->
766+
true
767+
| _ -> false
768+
| _ -> false
769+
770+
checkTailCall (not isContinuation) binding.Expr
760771

761772
let warnForBodyExpr =
762773
insideSubBindingOrTry

tests/FSharp.Compiler.ComponentTests/ErrorMessages/TailCallAttribute.fs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1683,3 +1683,42 @@ module M =
16831683
Message =
16841684
"The member or function 'traverseSequentials' has the 'TailCallAttribute' attribute, but is not being used in a tail recursive way." }
16851685
]
1686+
1687+
[<FSharp.Test.FactForNETCOREAPP>]
1688+
let ``Don't warn for rec call of async func that evaluates an async parameter in a match!`` () =
1689+
"""
1690+
namespace N
1691+
1692+
module M =
1693+
1694+
[<TailCall>]
1695+
let rec f (g: bool Async) = async {
1696+
match! g with
1697+
| false -> ()
1698+
| true -> return! f g
1699+
}
1700+
"""
1701+
|> FSharp
1702+
|> withLangVersion80
1703+
|> compile
1704+
|> shouldSucceed
1705+
1706+
[<FSharp.Test.FactForNETCOREAPP>]
1707+
let ``Don't warn for rec call of async func that evaluates an async parameter in a let!`` () =
1708+
"""
1709+
namespace N
1710+
1711+
module M =
1712+
1713+
[<TailCall>]
1714+
let rec f (g: bool Async) = async {
1715+
let! x = g
1716+
match x with
1717+
| false -> ()
1718+
| true -> return! f g
1719+
}
1720+
"""
1721+
|> FSharp
1722+
|> withLangVersion80
1723+
|> compile
1724+
|> shouldSucceed

0 commit comments

Comments
 (0)