Skip to content

Commit c48f7fa

Browse files
committed
Fix #5397: Visit local methods and values in Tailrec
If a method is annotated with @tailrec we now report invalid recursive call within local method. This means we now visit by-name arguments, closures and lifted `try`s
1 parent a9029dc commit c48f7fa

File tree

5 files changed

+49
-35
lines changed

5 files changed

+49
-35
lines changed

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

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import dotty.tools.dotc.core.Flags._
1010
import dotty.tools.dotc.core.NameKinds.{TailLabelName, TailLocalName, TailTempName}
1111
import dotty.tools.dotc.core.StdNames.nme
1212
import dotty.tools.dotc.core.Symbols._
13+
import dotty.tools.dotc.transform.SymUtils._
1314
import dotty.tools.dotc.reporting.diagnostic.messages.TailrecNotApplicable
1415
import dotty.tools.dotc.transform.MegaPhase.MiniPhase
1516

@@ -407,8 +408,11 @@ class TailRec extends MiniPhase {
407408
assert(false, "We should never have gotten inside a pattern")
408409
tree
409410

410-
case _: ValDef | _: DefDef | _: Super | _: This |
411-
_: Literal | _: TypeTree | _: TypeDef | EmptyTree =>
411+
case tree: ValOrDefDef =>
412+
if (isMandatory) noTailTransform(tree.rhs)
413+
tree
414+
415+
case _: Super | _: This | _: Literal | _: TypeTree | _: TypeDef | EmptyTree =>
412416
tree
413417

414418
case Labeled(bind, expr) =>

tests/neg-tailcall/t1672b.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,12 @@ object Test1772B {
4343

4444
// the `liftedTree` local method will prevent a tail call here.
4545
@tailrec
46-
def bar(i : Int) : Int = { // error: TailRec optimisation not applicable
46+
def bar(i : Int) : Int = {
4747
if (i == 0) 0
4848
else 1 + (try {
4949
throw new RuntimeException
5050
} catch {
51-
case _: Throwable => bar(i - 1) // old error: cannot rewrite recursive call
51+
case _: Throwable => bar(i - 1) // error: it is not in tail position
5252
})
5353
}
5454
}

tests/neg/i5397.scala

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import annotation.tailrec
2+
3+
object Test {
4+
def foo(x: => Int) = 1
5+
def bar(x: Int => Int) = 1
6+
7+
@tailrec def rec1: Int =
8+
foo(rec1) // error not in tail position
9+
10+
@tailrec def rec2: Int =
11+
bar(_ => rec2) // error not in tail position
12+
13+
@tailrec def rec3: Int =
14+
1 + (try ??? catch {
15+
case _: Throwable =>
16+
rec3 // error not in tail position
17+
})
18+
19+
@tailrec def rec4: Unit = {
20+
def local = rec4 // error not in tail position
21+
}
22+
23+
@tailrec def rec5: Int = {
24+
val x = {
25+
rec5 // error not in tail position
26+
1
27+
}
28+
x
29+
}
30+
}
31+
32+
object Test4649 {
33+
@tailrec
34+
def lazyFilter[E](s: Stream[E], p: E => Boolean): Stream[E] = s match {
35+
case h #:: t =>
36+
if (p(h)) h #:: lazyFilter(t, p) // error not in tail position
37+
else lazyFilter(t, p)
38+
case _ =>
39+
Stream.empty
40+
}
41+
}

tests/pos/ErasureAnd.scala

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
import scala.annotation.tailrec
21
trait A { self: B =>
3-
@tailrec
42
private def foo(arg1: Int, arg2: Int): Int = {
53
def bar = this.foo(arg1, arg2)
64
foo(arg1, arg2)

tests/pos/tailcall/t4649.scala

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

0 commit comments

Comments
 (0)