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 [<tailcall>] false positive with yield! #16933

Merged
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/8.0.300.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
### Fixed

* Fix a false positive of the `[<TailCall>]` analysis in combination with `yield!`. ([PR #16933](https://github.com/dotnet/fsharp/pull/16933))
* Don't blow the stack when traversing deeply nested sequential expressions. ([PR #16882](https://github.com/dotnet/fsharp/pull/16882))
* Fix wrong range start of INTERP_STRING_END. ([PR #16774](https://github.com/dotnet/fsharp/pull/16774), [PR #16785](https://github.com/dotnet/fsharp/pull/16785))
* Fix missing warning for recursive calls in list comprehensions. ([PR #16652](https://github.com/dotnet/fsharp/pull/16652))
Expand Down
7 changes: 7 additions & 0 deletions src/Compiler/Checking/TailCallChecks.fs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,13 @@ and CheckCall cenv args ctxts (tailCall: TailCall) =
| Expr.App _ -> Some(TailCall.YesFromExpr cenv.g e)
| IsAppInLambdaBody t -> Some t
| _ -> None
| Expr.App(args = args) ->
args
|> List.tryPick (fun a ->
match a with
| IsAppInLambdaBody t -> Some t
| _ -> None)

| _ -> None

// if we haven't already decided this is no tail call, try to detect CPS-like expressions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1522,3 +1522,42 @@ namespace N
Message =
"The member or function 'reverse' has the 'TailCallAttribute' attribute, but is not being used in a tail recursive way." }
]

[<FSharp.Test.FactForNETCOREAPP>]
let ``Don't warn for yield! call of tail rec func`` () =
"""
namespace N

module M =

type SynExpr =
| Sequential of expr1 : SynExpr * expr2 : SynExpr
| NotSequential
member _.Range = 99

type SyntaxNode = SynExpr of SynExpr

type SyntaxVisitor () = member _.VisitExpr _ = None

let visitor = SyntaxVisitor ()
let dive expr range f = range, fun () -> Some expr
let traverseSynExpr _ expr = Some expr

[<TailCall>]
let rec traverseSequentials path expr =
seq {
match expr with
| SynExpr.Sequential(expr1 = expr1; expr2 = SynExpr.Sequential _ as expr2) ->
yield dive expr expr.Range (fun expr -> visitor.VisitExpr(path, traverseSynExpr path, (fun _ -> None), expr))
let path = SyntaxNode.SynExpr expr :: path
yield dive expr1 expr1.Range (traverseSynExpr path)
yield! traverseSequentials path expr2 // should not warn

| _ ->
yield dive expr expr.Range (traverseSynExpr path)
}
"""
|> FSharp
|> withLangVersion80
|> compile
|> shouldSucceed
Loading