Skip to content

Fix #4196: TailRec-optimize methods returning implicit function types #4197

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
Mar 29, 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,6 +67,7 @@ class Compiler {
new NormalizeFlags, // Rewrite some definition flags
new ExtensionMethods, // Expand methods of value classes with extension methods
new ExpandSAMs, // Expand single abstract method closures to anonymous classes
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
Expand All @@ -77,7 +78,6 @@ class Compiler {
new PatternMatcher, // Compile pattern matches
new ExplicitOuter, // Add accessors to outer classes from nested ones.
new ExplicitSelf, // Make references to non-trivial self types explicit as casts
new ShortcutImplicits, // Allow implicit functions without creating closures
new CrossCastAnd, // Normalize selections involving intersection types.
new Splitter) :: // Expand selections involving union types into conditionals
List(new ErasedDecls, // Removes all erased defs and vals decls (except for parameters)
Expand Down
4 changes: 3 additions & 1 deletion compiler/src/dotty/tools/dotc/transform/MegaPhase.scala
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase {
private val cpy: TypedTreeCopier = cpyBetweenPhases

/** Transform node using all phases in this group that have idxInGroup >= start */
def transformNode(tree: Tree, start: Int)(implicit ctx: Context) = {
def transformNode(tree: Tree, start: Int)(implicit ctx: Context): Tree = {
def goNamed(tree: Tree, start: Int) = tree match {
case tree: Ident => goIdent(tree, start)
case tree: Select => goSelect(tree, start)
Expand All @@ -169,6 +169,8 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase {
def goUnnamed(tree: Tree, start: Int) = tree match {
case tree: Apply => goApply(tree, start)
case tree: TypeTree => goTypeTree(tree, start)
case tree: Thicket =>
cpy.Thicket(tree)(tree.trees.mapConserve(transformNode(_, start)))
case tree: This => goThis(tree, start)
case tree: Literal => goLiteral(tree, start)
case tree: Block => goBlock(tree, start)
Expand Down
14 changes: 13 additions & 1 deletion compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ import collection.mutable
class ShortcutImplicits extends MiniPhase with IdentityDenotTransformer { thisPhase =>
import tpd._

override def phaseName: String = "shortcutImplicits"
override def phaseName: String = ShortcutImplicits.name

override def changesMembers = true // the phase adds "direct" methods

Expand Down Expand Up @@ -138,6 +138,14 @@ class ShortcutImplicits extends MiniPhase with IdentityDenotTransformer { thisPh
if (shouldBeSpecialized(original)) {
val direct = directMethod(original)

// Move @tailrec to the direct method
original.getAnnotation(defn.TailrecAnnot) match {
case Some(annot) =>
direct.addAnnotation(annot)
original.removeAnnotation(defn.TailrecAnnot)
case _ =>
}

def splitClosure(tree: Tree): (List[Type] => List[List[Tree]] => Tree, Tree) = tree match {
case Block(Nil, expr) => splitClosure(expr)
case Block((meth @ DefDef(nme.ANON_FUN, Nil, clparams :: Nil, _, _)) :: Nil, cl: Closure) =>
Expand Down Expand Up @@ -167,3 +175,7 @@ class ShortcutImplicits extends MiniPhase with IdentityDenotTransformer { thisPh
else mdef
}
}

object ShortcutImplicits {
val name = "shortcutImplicits"
}
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/transform/TailRec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ class TailRec extends MiniPhase with FullParameterization {

override def phaseName: String = TailRec.name

override def runsAfter = Set(ShortcutImplicits.name) // Replaces non-tail calls by tail calls

final val labelFlags = Flags.Synthetic | Flags.Label

/** Symbols of methods that have @tailrec annotatios inside */
Expand Down
8 changes: 8 additions & 0 deletions tests/neg/i4196.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
object Test {
@annotation.tailrec
def foo(i: implicit Unit => Int): implicit Unit => Int = // error: method not tail recursive
if (i == 0)
0
else
foo(i - 1)*2 // error: Cannot rewrite recursive call not in tail position
}
8 changes: 8 additions & 0 deletions tests/pos/i4196.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
object Test {
@annotation.tailrec
def foo(i: implicit Unit => Int): implicit Unit => Int =
if (i == 0)
0
else
foo(i - 1)
}