Skip to content

Move TailRec after Erasure. #5112

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Sep 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ class Compiler {
new ProtectedAccessors, // Add accessors for protected members
new ExtensionMethods, // Expand methods of value classes with extension methods
new ShortcutImplicits, // Allow implicit functions without creating closures
new TailRec, // Rewrite tail recursion to loops
new ByNameClosures, // Expand arguments to by-name parameters to closures
new LiftTry, // Put try expressions that might execute on non-empty stacks into their own methods
new HoistSuperArgs, // Hoist complex arguments of supercalls to enclosing scope
Expand Down Expand Up @@ -96,6 +95,7 @@ class Compiler {
List(new Erasure) :: // Rewrite types to JVM model, erasing all type parameters, abstract types and refinements.
List(new ElimErasedValueType, // Expand erased value types to their underlying implmementation types
new VCElideAllocations, // Peep-hole optimization to eliminate unnecessary value class allocations
new TailRec, // Rewrite tail recursion to loops
new Mixin, // Expand trait fields and trait initializers
new LazyVals, // Expand lazy vals
new Memoize, // Add private fields to getters and setters
Expand Down
5 changes: 5 additions & 0 deletions compiler/src/dotty/tools/dotc/ast/TreeTypeMap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ class TreeTypeMap(
val guard1 = tmap.transform(guard)
val rhs1 = tmap.transform(rhs)
cpy.CaseDef(cdef)(pat1, guard1, rhs1)
case labeled @ Labeled(bind, expr) =>
val tmap = withMappedSyms(bind.symbol :: Nil)
val bind1 = tmap.transformSub(bind)
val expr1 = tmap.transform(expr)
cpy.Labeled(labeled)(bind1, expr1)
case Hole(n, args) =>
Hole(n, args.mapConserve(transform)).withPos(tree.pos).withType(mapType(tree.tpe))
case tree1 =>
Expand Down
30 changes: 15 additions & 15 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -208,17 +208,17 @@ object Types {
def loop(tp: Type): Boolean = tp match {
case tp: TypeRef =>
val sym = tp.symbol
if (sym.isClass) sym.derivesFrom(cls) else loop(tp.superType): @tailrec
if (sym.isClass) sym.derivesFrom(cls) else loop(tp.superType)
case tp: AppliedType =>
tp.superType.derivesFrom(cls)
case tp: MatchType =>
tp.bound.derivesFrom(cls) || tp.reduced.derivesFrom(cls)
case tp: TypeProxy =>
loop(tp.underlying): @tailrec
loop(tp.underlying)
case tp: AndType =>
loop(tp.tp1) || loop(tp.tp2): @tailrec
loop(tp.tp1) || loop(tp.tp2)
case tp: OrType =>
loop(tp.tp1) && loop(tp.tp2): @tailrec
loop(tp.tp1) && loop(tp.tp2)
case tp: JavaArrayType =>
cls == defn.ObjectClass
case _ =>
Expand Down Expand Up @@ -403,16 +403,16 @@ object Types {
*/
final def classSymbol(implicit ctx: Context): Symbol = this match {
case ConstantType(constant) =>
constant.tpe.classSymbol: @tailrec
constant.tpe.classSymbol
case tp: TypeRef =>
val sym = tp.symbol
if (sym.isClass) sym else tp.superType.classSymbol: @tailrec
if (sym.isClass) sym else tp.superType.classSymbol
case tp: ClassInfo =>
tp.cls
case tp: SingletonType =>
NoSymbol
case tp: TypeProxy =>
tp.underlying.classSymbol: @tailrec
tp.underlying.classSymbol
case AndType(l, r) =>
val lsym = l.classSymbol
val rsym = r.classSymbol
Expand All @@ -436,9 +436,9 @@ object Types {
tp.cls :: Nil
case tp: TypeRef =>
val sym = tp.symbol
if (sym.isClass) sym.asClass :: Nil else tp.superType.classSymbols: @tailrec
if (sym.isClass) sym.asClass :: Nil else tp.superType.classSymbols
case tp: TypeProxy =>
tp.underlying.classSymbols: @tailrec
tp.underlying.classSymbols
case AndType(l, r) =>
l.classSymbols union r.classSymbols
case OrType(l, r) =>
Expand Down Expand Up @@ -479,7 +479,7 @@ object Types {
case tp: ClassInfo =>
tp.decls
case tp: TypeProxy =>
tp.underlying.decls: @tailrec
tp.underlying.decls
case _ =>
EmptyScope
}
Expand Down Expand Up @@ -725,7 +725,7 @@ object Types {
val ns = tp.parent.memberNames(keepOnly, pre)
if (keepOnly(pre, tp.refinedName)) ns + tp.refinedName else ns
case tp: TypeProxy =>
tp.underlying.memberNames(keepOnly, pre): @tailrec
tp.underlying.memberNames(keepOnly, pre)
case tp: AndType =>
tp.tp1.memberNames(keepOnly, pre) | tp.tp2.memberNames(keepOnly, pre)
case tp: OrType =>
Expand Down Expand Up @@ -1042,21 +1042,21 @@ object Types {
case tp: TypeRef =>
if (tp.symbol.isClass) tp
else tp.info match {
case TypeAlias(alias) => alias.dealias1(keep): @tailrec
case TypeAlias(alias) => alias.dealias1(keep)
case _ => tp
}
case app @ AppliedType(tycon, args) =>
val tycon1 = tycon.dealias1(keep)
if (tycon1 ne tycon) app.superType.dealias1(keep): @tailrec
if (tycon1 ne tycon) app.superType.dealias1(keep)
else this
case tp: TypeVar =>
val tp1 = tp.instanceOpt
if (tp1.exists) tp1.dealias1(keep): @tailrec else tp
if (tp1.exists) tp1.dealias1(keep) else tp
case tp: AnnotatedType =>
val tp1 = tp.parent.dealias1(keep)
if (keep(tp)(ctx)) tp.derivedAnnotatedType(tp1, tp.annot) else tp1
case tp: LazyRef =>
tp.ref.dealias1(keep): @tailrec
tp.ref.dealias1(keep)
case _ => this
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/transform/MixinOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class MixinOps(cls: ClassSymbol, thisPhase: DenotTransformer)(implicit ctx: Cont
name = member.name.stripScala2LocalSuffix,
flags = member.flags &~ Deferred,
info = cls.thisType.memberInfo(member)).enteredAfter(thisPhase).asTerm
res.addAnnotations(member.annotations)
res.addAnnotations(member.annotations.filter(_.symbol != defn.TailrecAnnot))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I missed this. Is this needed if you move TailRec after Mixin within the same group?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even more so. But right now it's necessary above all for extension methods (for AnyVals, which also use this forwarder() method). In general, when creating a forwarder of any sort, we must remove the @tailrec annotation.

Previously, no forwarder was ever created before TailRec ran. But now that TailRec runs after the creation of extension methods, we must ensure that forwarder drops @tailrec).

res
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ class PatternMatcher extends MiniPhase {

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

override def transformMatch(tree: Match)(implicit ctx: Context): Tree = {
val translated = new Translator(tree.tpe, this).translateMatch(tree)
Expand Down
Loading