Skip to content

Commit b8be1e7

Browse files
committed
Move TailRec after Erasure.
This used not to be possible because it was too complicated for TailRec to understand trees produced by the pattern matcher. Now that patmat uses `Labeled` blocks instead of label-defs, it is trivial to do so: the argument of a `return` from a labeled block `lab` is in tail position if and only if the labeled block `lab` is itself in tail position. Running TailRec after erasure has two major benefits: * it is much simpler, as it does not have to deal with type parameters, `TypeApply`s, and a bunch of other stuff. * it supports polymorphic tail-recursive calls by construction (recursive calls whose receiver or method has different type parameters). It also has one difficulty: it cannot see the *call-site* `@tailrec` annotations anymore. This is why we add a mini-phase to record such annotations as tree attachments. Having TailRec after erasure will also be necessary to later make it use `Labeled` blocks and loops instead of label-defs itself. Indeed, in that case it will need to declare local `var`s for its term parameters, which would break path-dependent types within the method if it were done before erasure.
1 parent a0b429b commit b8be1e7

File tree

10 files changed

+155
-131
lines changed

10 files changed

+155
-131
lines changed

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ class Compiler {
6767
new ProtectedAccessors, // Add accessors for protected members
6868
new ExtensionMethods, // Expand methods of value classes with extension methods
6969
new ShortcutImplicits, // Allow implicit functions without creating closures
70-
new TailRec, // Rewrite tail recursion to loops
7170
new ByNameClosures, // Expand arguments to by-name parameters to closures
7271
new LiftTry, // Put try expressions that might execute on non-empty stacks into their own methods
7372
new HoistSuperArgs, // Hoist complex arguments of supercalls to enclosing scope
@@ -92,10 +91,12 @@ class Compiler {
9291
new ResolveSuper, // Implement super accessors and add forwarders to trait methods
9392
new PrimitiveForwarders, // Add forwarders to trait methods that have a mismatch between generic and primitives
9493
new FunctionXXLForwarders, // Add forwarders for FunctionXXL apply method
94+
new RecordTailRecCallSites, // Records call-site @tailrec annotations as attachments
9595
new ArrayConstructors) :: // Intercept creation of (non-generic) arrays and intrinsify.
9696
List(new Erasure) :: // Rewrite types to JVM model, erasing all type parameters, abstract types and refinements.
9797
List(new ElimErasedValueType, // Expand erased value types to their underlying implmementation types
9898
new VCElideAllocations, // Peep-hole optimization to eliminate unnecessary value class allocations
99+
new TailRec, // Rewrite tail recursion to loops
99100
new Mixin, // Expand trait fields and trait initializers
100101
new LazyVals, // Expand lazy vals
101102
new Memoize, // Add private fields to getters and setters

compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,11 @@ class TreeTypeMap(
116116
val guard1 = tmap.transform(guard)
117117
val rhs1 = tmap.transform(rhs)
118118
cpy.CaseDef(cdef)(pat1, guard1, rhs1)
119+
case labeled @ Labeled(bind, expr) =>
120+
val tmap = withMappedSyms(bind.symbol :: Nil)
121+
val bind1 = tmap.transformSub(bind)
122+
val expr1 = tmap.transform(expr)
123+
cpy.Labeled(labeled)(bind1, expr1)
119124
case Hole(n, args) =>
120125
Hole(n, args.mapConserve(transform)).withPos(tree.pos).withType(mapType(tree.tpe))
121126
case tree1 =>

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class MixinOps(cls: ClassSymbol, thisPhase: DenotTransformer)(implicit ctx: Cont
2424
name = member.name.stripScala2LocalSuffix,
2525
flags = member.flags &~ Deferred,
2626
info = cls.thisType.memberInfo(member)).enteredAfter(thisPhase).asTerm
27-
res.addAnnotations(member.annotations)
27+
res.addAnnotations(member.annotations.filter(_.symbol != defn.TailrecAnnot))
2828
res
2929
}
3030

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

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ class PatternMatcher extends MiniPhase {
2727

2828
override def phaseName = PatternMatcher.name
2929
override def runsAfter = Set(ElimRepeated.name)
30-
override def runsAfterGroupsOf = Set(TailRec.name) // tailrec is not capable of reversing the patmat tranformation made for tree
3130

3231
override def transformMatch(tree: Match)(implicit ctx: Context): Tree = {
3332
val translated = new Translator(tree.tpe, this).translateMatch(tree)
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package dotty.tools.dotc
2+
package transform
3+
4+
import core._
5+
import MegaPhase._
6+
import Contexts.Context
7+
import Flags._
8+
import SymUtils._
9+
import Symbols._
10+
import SymDenotations._
11+
import Types._
12+
import Decorators._
13+
import DenotTransformers._
14+
import StdNames._
15+
import NameOps._
16+
import ast.Trees._
17+
import dotty.tools.dotc.ast.tpd
18+
import util.Positions._
19+
import Names._
20+
21+
import collection.mutable
22+
import ResolveSuper._
23+
24+
import scala.collection.immutable.::
25+
26+
27+
/** This phase saves call-site `@tailrec` annotations as attachments.
28+
*
29+
* Since erasure will come before the `tailrec` phase, it will erase the `@tailrec` annotations
30+
* in the `Typed` nodes.
31+
*/
32+
class RecordTailRecCallSites extends MiniPhase {
33+
import ast.tpd._
34+
35+
override def phaseName: String = "recordTailrecCallSites"
36+
37+
override def transformTyped(tree: Typed)(implicit ctx: Context): Tree = {
38+
if (tree.tpt.tpe.hasAnnotation(defn.TailrecAnnot) && tree.expr.isInstanceOf[Apply])
39+
tree.expr.pushAttachment(TailRec.TailRecCallSiteKey, ())
40+
super.transformTyped(tree)
41+
}
42+
}

0 commit comments

Comments
 (0)