Skip to content

Commit 819c2ff

Browse files
authored
Merge pull request #14067 from dotty-staging/return-tailrec
Allow return in tailrec position
2 parents 300a8b6 + 4368847 commit 819c2ff

File tree

3 files changed

+76
-15
lines changed

3 files changed

+76
-15
lines changed

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

+3-15
Original file line numberDiff line numberDiff line change
@@ -277,23 +277,11 @@ class TailRec extends MiniPhase {
277277
def yesTailTransform(tree: Tree)(using Context): Tree =
278278
transform(tree, tailPosition = true)
279279

280-
/** If not in tail position a tree traversal may not be needed.
281-
*
282-
* A recursive call may still be in tail position if within the return
283-
* expression of a labeled block.
284-
* A tree traversal may also be needed to report a failure to transform
285-
* a recursive call of a @tailrec annotated method (i.e. `isMandatory`).
286-
*/
287-
private def isTraversalNeeded =
288-
isMandatory || tailPositionLabeledSyms.size > 0
289-
290280
def noTailTransform(tree: Tree)(using Context): Tree =
291-
if (isTraversalNeeded) transform(tree, tailPosition = false)
292-
else tree
281+
transform(tree, tailPosition = false)
293282

294283
def noTailTransforms[Tr <: Tree](trees: List[Tr])(using Context): List[Tr] =
295-
if (isTraversalNeeded) trees.mapConserve(noTailTransform).asInstanceOf[List[Tr]]
296-
else trees
284+
trees.mapConserve(noTailTransform).asInstanceOf[List[Tr]]
297285

298286
override def transform(tree: Tree)(using Context): Tree = {
299287
/* Rewrite an Apply to be considered for tail call transformation. */
@@ -444,7 +432,7 @@ class TailRec extends MiniPhase {
444432

445433
case Return(expr, from) =>
446434
val fromSym = from.symbol
447-
val inTailPosition = fromSym.is(Label) && tailPositionLabeledSyms.contains(fromSym)
435+
val inTailPosition = !fromSym.is(Label) || tailPositionLabeledSyms.contains(fromSym)
448436
cpy.Return(tree)(transform(expr, inTailPosition), from)
449437

450438
case _ =>

tests/run/tailrec-return.check

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
6
2+
false
3+
true
4+
false
5+
true
6+
Ada Lovelace, Alan Turing
7+
List(9, 10)

tests/run/tailrec-return.scala

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
object Test:
2+
3+
@annotation.tailrec
4+
def sum(n: Int, acc: Int = 0): Int =
5+
if n != 0 then return sum(n - 1, acc + n)
6+
acc
7+
8+
@annotation.tailrec
9+
def isEven(n: Int): Boolean =
10+
if n != 0 && n != 1 then return isEven(n - 2)
11+
if n == 1 then return false
12+
true
13+
14+
@annotation.tailrec
15+
def isEvenApply(n: Int): Boolean =
16+
// Return inside an `Apply.fun`
17+
(
18+
if n != 0 && n != 1 then return isEvenApply(n - 2)
19+
else if n == 1 then return false
20+
else (x: Boolean) => x
21+
)(true)
22+
23+
@annotation.tailrec
24+
def isEvenWhile(n: Int): Boolean =
25+
// Return inside a `WhileDo.cond`
26+
while(
27+
if n != 0 && n != 1 then return isEvenWhile(n - 2)
28+
else if n == 1 then return false
29+
else true
30+
) {}
31+
true
32+
33+
@annotation.tailrec
34+
def isEvenReturn(n: Int): Boolean =
35+
// Return inside a `Return`
36+
return
37+
if n != 0 && n != 1 then return isEvenReturn(n - 2)
38+
else if n == 1 then return false
39+
else true
40+
41+
@annotation.tailrec
42+
def names(l: List[(String, String) | Null], acc: List[String] = Nil): List[String] =
43+
l match
44+
case Nil => acc.reverse
45+
case x :: xs =>
46+
if x == null then return names(xs, acc)
47+
48+
val displayName = x._1 + " " + x._2
49+
names(xs, displayName :: acc)
50+
51+
def nonTail(l: List[Int]): List[Int] =
52+
l match
53+
case Nil => Nil
54+
case x :: xs =>
55+
// The call to nonTail should *not* be eliminated
56+
(x + 1) :: nonTail(xs)
57+
58+
59+
def main(args: Array[String]): Unit =
60+
println(sum(3))
61+
println(isEven(5))
62+
println(isEvenApply(6))
63+
println(isEvenWhile(7))
64+
println(isEvenReturn(8))
65+
println(names(List(("Ada", "Lovelace"), null, ("Alan", "Turing"))).mkString(", "))
66+
println(nonTail(List(8, 9)))

0 commit comments

Comments
 (0)