Skip to content

Commit 3eca547

Browse files
committed
Optimise the Tailrec phase
We can stop traversing a tree in Tailrec as soon as we are not in tail position anymore and we are not within a labeled block in tail position. We keep traversing the tree however if the method is @tailrec annotated to report errors on eventual recursive calls.
1 parent 2da566b commit 3eca547

File tree

1 file changed

+31
-26
lines changed

1 file changed

+31
-26
lines changed

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

Lines changed: 31 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -245,11 +245,23 @@ class TailRec extends MiniPhase {
245245
def yesTailTransform(tree: Tree)(implicit ctx: Context): Tree =
246246
transform(tree, tailPosition = true)
247247

248+
/** If not in tail position a tree traversal may not be needed.
249+
*
250+
* A recursive call may still be in tail position if within the return
251+
* expression of a labelled block.
252+
* A tree traversal may also be needed to report a failure to transform
253+
* a recursive call of a @tailrec annotated method (i.e. `isMandatory`).
254+
*/
255+
private def isTraversalNeeded =
256+
isMandatory || tailPositionLabeledSyms.nonEmpty
257+
248258
def noTailTransform(tree: Tree)(implicit ctx: Context): Tree =
249-
transform(tree, tailPosition = false)
259+
if (isTraversalNeeded) transform(tree, tailPosition = false)
260+
else tree
250261

251262
def noTailTransforms[Tr <: Tree](trees: List[Tr])(implicit ctx: Context): List[Tr] =
252-
trees.mapConserve(noTailTransform).asInstanceOf[List[Tr]]
263+
if (isTraversalNeeded) trees.mapConserve(noTailTransform).asInstanceOf[List[Tr]]
264+
else trees
253265

254266
override def transform(tree: Tree)(implicit ctx: Context): Tree = {
255267
/* Rewrite an Apply to be considered for tail call transformation. */
@@ -328,24 +340,6 @@ class TailRec extends MiniPhase {
328340
continue
329341
}
330342

331-
def rewriteTry(tree: Try): Try = {
332-
if (tree.finalizer eq EmptyTree) {
333-
// SI-1672 Catches are in tail position when there is no finalizer
334-
cpy.Try(tree)(
335-
noTailTransform(tree.expr),
336-
transformSub(tree.cases),
337-
EmptyTree
338-
)
339-
}
340-
else {
341-
cpy.Try(tree)(
342-
noTailTransform(tree.expr),
343-
noTailTransforms(tree.cases),
344-
noTailTransform(tree.finalizer)
345-
)
346-
}
347-
}
348-
349343
tree match {
350344
case tree @ Apply(fun, args) =>
351345
val meth = fun.symbol
@@ -380,13 +374,22 @@ class TailRec extends MiniPhase {
380374
)
381375

382376
case tree: Try =>
383-
rewriteTry(tree)
377+
val expr = noTailTransform(tree.expr)
378+
if (tree.finalizer eq EmptyTree) {
379+
// SI-1672 Catches are in tail position when there is no finalizer
380+
cpy.Try(tree)(expr, transformSub(tree.cases), EmptyTree)
381+
}
382+
else cpy.Try(tree)(
383+
expr,
384+
noTailTransforms(tree.cases),
385+
noTailTransform(tree.finalizer)
386+
)
384387

385388
case tree @ WhileDo(cond, body) =>
386-
// we traverse the body and the condition for the purpose of reporting errors
387-
noTailTransform(cond)
388-
noTailTransform(body)
389-
tree
389+
cpy.WhileDo(tree)(
390+
noTailTransform(cond),
391+
noTailTransform(body)
392+
)
390393

391394
case _: Alternative | _: Bind =>
392395
assert(false, "We should never have gotten inside a pattern")
@@ -399,7 +402,9 @@ class TailRec extends MiniPhase {
399402
case Labeled(bind, expr) =>
400403
if (inTailPosition)
401404
tailPositionLabeledSyms += bind.symbol
402-
cpy.Labeled(tree)(bind, transform(expr))
405+
try cpy.Labeled(tree)(bind, transform(expr))
406+
finally if (inTailPosition)
407+
tailPositionLabeledSyms -= bind.symbol
403408

404409
case Return(expr, from) =>
405410
val fromSym = from.symbol

0 commit comments

Comments
 (0)