Skip to content

Commit bcddc8d

Browse files
committed
Fix #5163: Properly report tailrec failures on recursive call targeting a supertype
1 parent f063845 commit bcddc8d

File tree

13 files changed

+46
-81
lines changed

13 files changed

+46
-81
lines changed

compiler/src/dotty/tools/dotc/transform/TailRec.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,8 +237,9 @@ class TailRec extends MiniPhase {
237237
}
238238
else fail("it is not in tail position")
239239
} else {
240-
// FIXME `(method.name eq call)` is always false (Name vs Symbol). What is this trying to do?
241-
val receiverIsSuper = (method.name eq call) && enclosingClass.appliedRef.widen <:< prefix.tpe.widenDealias
240+
val receiverIsSuper = (method.name eq call.name) &&
241+
method.matches(call) &&
242+
enclosingClass.appliedRef.widen <:< prefix.tpe.widenDealias
242243

243244
if (receiverIsSuper) fail("it contains a recursive call targeting a supertype")
244245
else continue

tests/neg-tailcall/t1672b.check

Lines changed: 0 additions & 16 deletions
This file was deleted.

tests/neg-tailcall/t1672b.scala

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import annotation.tailrec
2+
13
object Test1772B {
2-
@annotation.tailrec
4+
@tailrec
35
def bar : Nothing = { // error: TailRec optimisation not applicable
46
try {
57
throw new RuntimeException
@@ -10,7 +12,7 @@ object Test1772B {
1012
}
1113
}
1214

13-
@annotation.tailrec
15+
@tailrec
1416
def baz : Nothing = { // error: TailRec optimisation not applicable
1517
try {
1618
throw new RuntimeException
@@ -21,7 +23,7 @@ object Test1772B {
2123
}
2224
}
2325

24-
@annotation.tailrec
26+
@tailrec
2527
def boz : Nothing = { // error: TailRec optimisation not applicable
2628
try {
2729
throw new RuntimeException
@@ -30,7 +32,7 @@ object Test1772B {
3032
}
3133
}
3234

33-
@annotation.tailrec
35+
@tailrec
3436
def bez : Nothing = { // error: TailRec optimisation not applicable
3537
try {
3638
bez // error: it is not in tail position
@@ -40,7 +42,7 @@ object Test1772B {
4042
}
4143

4244
// the `liftedTree` local method will prevent a tail call here.
43-
@annotation.tailrec
45+
@tailrec
4446
def bar(i : Int) : Int = { // error: TailRec optimisation not applicable
4547
if (i == 0) 0
4648
else 1 + (try {

tests/neg-tailcall/t3275.check

Lines changed: 0 additions & 4 deletions
This file was deleted.

tests/neg-tailcall/t3275.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
object Test {
2-
@annotation.tailrec def foo() = 5 // error
2+
@annotation.tailrec def foo() = 5 // error: not recursive
33
}

tests/neg-tailcall/t6574.check

Lines changed: 0 additions & 7 deletions
This file was deleted.

tests/neg-tailcall/t6574.scala

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
import annotation.tailrec
2+
13
class Bad[X, Y](val v: Int) extends AnyVal {
2-
@annotation.tailrec final def notTailPos[Z](a: Int)(b: String): Unit = { // error
3-
this.notTailPos[Z](a)(b) // error
4+
@tailrec final def notTailPos[Z](a: Int)(b: String): Unit = { // error: TailRec optimisation not applicable
5+
this.notTailPos[Z](a)(b) // error: it is not in tail position
46
println("tail")
57
}
68

7-
@annotation.tailrec final def differentTypeArgs: Unit = {
9+
@tailrec final def differentTypeArgs: Unit = {
810
{(); new Bad[String, Unit](0)}.differentTypeArgs
911
}
1012
}

tests/neg-tailcall/tailrec-2.check

Lines changed: 0 additions & 7 deletions
This file was deleted.

tests/neg-tailcall/tailrec-2.scala

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,26 @@
1+
import annotation.tailrec
2+
13
sealed abstract class Super[+A] {
24
def f[B >: A](mem: List[B]) : List[B]
35
def g(mem: List[_]) = ???
46
}
57
// This one should fail, target is a supertype
68
class Bop1[+A](val element: A) extends Super[A] {
79

8-
@annotation.tailrec final def f[B >: A](mem: List[B]): List[B] = (null: Super[A]).f(mem)
9-
@annotation.tailrec final def f1[B >: A](mem: List[B]): List[B] = this.g(mem)
10+
@tailrec final def f[B >: A](mem: List[B]): List[B] = // error: TailRec optimisation not applicable
11+
(null: Super[A]).f(mem) // error: recursive call targeting a supertype
12+
13+
@tailrec final def f1[B >: A](mem: List[B]): List[B] = this.g(mem) // error: TailRec optimisation not applicable
1014
}
1115
// These succeed
1216
class Bop2[+A](val element: A) extends Super[A] {
13-
@annotation.tailrec final def f[B >: A](mem: List[B]): List[B] = (null: Bop2[A]).f(mem)
17+
@tailrec final def f[B >: A](mem: List[B]): List[B] = (null: Bop2[A]).f(mem)
1418
}
1519
object Bop3 extends Super[Nothing] {
16-
@annotation.tailrec final def f[B](mem: List[B]): List[B] = (???: Bop3.type).f(mem) // error // error
20+
@tailrec final def f[B](mem: List[B]): List[B] = (??? : Bop3.type).f(mem)
1721
}
1822
class Bop4[+A](val element: A) extends Super[A] {
19-
@annotation.tailrec final def f[B >: A](mem: List[B]): List[B] = Other.f[A].f(mem)
23+
@tailrec final def f[B >: A](mem: List[B]): List[B] = Other.f[A].f(mem)
2024
}
2125

2226
object Other {

tests/neg-tailcall/tailrec-3.check

Lines changed: 0 additions & 10 deletions
This file was deleted.

tests/neg-tailcall/tailrec-3.scala

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
import annotation.tailrec
22

33
object Test {
4-
@tailrec private def quux(xs: List[String]): List[String] = quux(quux(xs)) // error
4+
@tailrec private def quux(xs: List[String]): List[String] =
5+
quux(
6+
quux(xs) // error: not in tail position
7+
)
58
@tailrec private def quux2(xs: List[String]): List[String] = xs match {
6-
case x1 :: x2 :: rest => quux2(x1 :: quux2(rest)) // error
7-
case _ => Nil
9+
case x1 :: x2 :: rest => quux2(
10+
x1 :: quux2(rest)) // error: not in tail position
11+
case _ => Nil
812
}
913
@tailrec private def quux3(xs: List[String]): Boolean = xs match {
10-
case x :: xs if quux3(List("abc")) => quux3(xs) // error
11-
case _ => false
14+
case x :: xs if quux3(List("abc")) => // error: not in tail position
15+
quux3(xs)
16+
case _ => false
1217
}
1318
}
1419

tests/neg-tailcall/tailrec.check

Lines changed: 0 additions & 16 deletions
This file was deleted.

tests/pos/tailcall/i5163.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import annotation.tailrec
2+
3+
class UnrolledBuffer {
4+
def remove(idx: Int): Unit = ()
5+
6+
@tailrec final def remove(idx: Int, count: Int): Unit =
7+
if (count > 0) {
8+
remove(idx)
9+
remove(idx, count - 1)
10+
}
11+
}

0 commit comments

Comments
 (0)