From 3ed8fb3a46c04e028c8c5f91fe4bedd74cc96112 Mon Sep 17 00:00:00 2001 From: Som Snytt Date: Wed, 28 May 2025 09:54:02 -0700 Subject: [PATCH] Warn trivial recursion with module prefix --- .../dotty/tools/dotc/transform/TailRec.scala | 6 ++++- tests/warn/i23277.scala | 22 +++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 tests/warn/i23277.scala diff --git a/compiler/src/dotty/tools/dotc/transform/TailRec.scala b/compiler/src/dotty/tools/dotc/transform/TailRec.scala index b8052721ff27..472ed78bfb6b 100644 --- a/compiler/src/dotty/tools/dotc/transform/TailRec.scala +++ b/compiler/src/dotty/tools/dotc/transform/TailRec.scala @@ -196,7 +196,8 @@ class TailRec extends MiniPhase { def isInfiniteRecCall(tree: Tree): Boolean = { def tailArgOrPureExpr(stat: Tree): Boolean = stat match { case stat: ValDef if stat.name.is(TailTempName) || !stat.symbol.is(Mutable) => tailArgOrPureExpr(stat.rhs) - case Assign(lhs: Ident, rhs) if lhs.symbol.name.is(TailLocalName) => tailArgOrPureExpr(rhs) + case Assign(lhs: Ident, rhs) if lhs.symbol.name.is(TailLocalName) => + tailArgOrPureExpr(rhs) || varForRewrittenThis.exists(_ == lhs.symbol && rhs.tpe.isStable) case Assign(lhs: Ident, rhs: Ident) => lhs.symbol == rhs.symbol case stat: Ident if stat.symbol.name.is(TailLocalName) => true case _ => tpd.isPureExpr(stat) @@ -345,6 +346,9 @@ class TailRec extends MiniPhase { case prefix: This if prefix.symbol == enclosingClass => // Avoid assigning `this = this` assignParamPairs + case prefix if prefix.symbol.is(Module) && prefix.symbol.moduleClass == enclosingClass => + // Avoid assigning `this = MyObject` + assignParamPairs case _ => (getVarForRewrittenThis(), noTailTransform(prefix)) :: assignParamPairs diff --git a/tests/warn/i23277.scala b/tests/warn/i23277.scala new file mode 100644 index 000000000000..f99f5a373d43 --- /dev/null +++ b/tests/warn/i23277.scala @@ -0,0 +1,22 @@ + +enum Test: + case One + case Two(i: Int) + +object Test: + object Two: + def apply(i: Int): Test.Two = Test.Two(i) // warn + def apply(i: Int, j: Int): Test.Two = new Test.Two(i+j) + def apply(i: Int, s: String): Two = Two(i, s) // warn because unprefixed Two is deemed pure + def apply(): Test.Two = other.apply() // nowarn prefix is method call + def other: this.type = this + +object R extends Runnable: + def r: this.type = this + override def run() = r.run() + +final class C(c: C): + def f(i: Int): Int = c.f(i) + +@main def main = println: + Test.Two(1)