@@ -5,6 +5,7 @@ import dotty.tools.dotc.ast.Trees._
55import dotty .tools .dotc .ast .{TreeTypeMap , tpd }
66import dotty .tools .dotc .config .Printers .tailrec
77import dotty .tools .dotc .core .Contexts .Context
8+ import dotty .tools .dotc .core .Constants .Constant
89import dotty .tools .dotc .core .Decorators ._
910import dotty .tools .dotc .core .Flags ._
1011import dotty .tools .dotc .core .NameKinds .{TailLabelName , TailLocalName , TailTempName }
@@ -174,6 +175,30 @@ class TailRec extends MiniPhase {
174175 ).transform(rhsSemiTransformed)
175176 }
176177
178+ /** Is the RHS a direct recursive tailcall, possibly with swapped arguments or modified pure arguments.
179+ * ```
180+ * def f(<params>): T = f(<args>)
181+ * ```
182+ * where `<args>` are pure arguments or references to parameters in `<params>`.
183+ */
184+ def isInfiniteRecCall (tree : Tree ): Boolean = {
185+ def tailArgOrPureExpr (stat : Tree ): Boolean = stat match {
186+ case stat : ValDef if stat.name.is(TailTempName ) || ! stat.symbol.is(Mutable ) => tailArgOrPureExpr(stat.rhs)
187+ case Assign (lhs : Ident , rhs) if lhs.symbol.name.is(TailLocalName ) => tailArgOrPureExpr(rhs)
188+ case stat : Ident if stat.symbol.name.is(TailLocalName ) => true
189+ case _ => tpd.isPureExpr(stat)
190+ }
191+ tree match {
192+ case Typed (expr, _) => isInfiniteRecCall(expr)
193+ case Return (Literal (Constant (())), label) => label.symbol == transformer.continueLabel
194+ case Block (stats, expr) => stats.forall(tailArgOrPureExpr) && isInfiniteRecCall(expr)
195+ case _ => false
196+ }
197+ }
198+
199+ if isInfiniteRecCall(rhsFullyTransformed) then
200+ ctx.warning(" Infinite recursive call" , tree.sourcePos)
201+
177202 cpy.DefDef (tree)(rhs =
178203 Block (
179204 initialVarDefs,
0 commit comments