diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index fe1ce0f02742..16fcaf694b98 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -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 @@ -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) diff --git a/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala b/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala index 2d2cbf4de3aa..868c48b0c0ae 100644 --- a/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala +++ b/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala @@ -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) @@ -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) diff --git a/compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala b/compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala index 77e735bdfe0a..3cc8b68233d0 100644 --- a/compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala +++ b/compiler/src/dotty/tools/dotc/transform/ShortcutImplicits.scala @@ -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 @@ -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) => @@ -167,3 +175,7 @@ class ShortcutImplicits extends MiniPhase with IdentityDenotTransformer { thisPh else mdef } } + +object ShortcutImplicits { + val name = "shortcutImplicits" +} diff --git a/compiler/src/dotty/tools/dotc/transform/TailRec.scala b/compiler/src/dotty/tools/dotc/transform/TailRec.scala index 61d4b232ba3b..6ef41144204b 100644 --- a/compiler/src/dotty/tools/dotc/transform/TailRec.scala +++ b/compiler/src/dotty/tools/dotc/transform/TailRec.scala @@ -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 */ diff --git a/tests/neg/i4196.scala b/tests/neg/i4196.scala new file mode 100644 index 000000000000..ba197eaa2c5d --- /dev/null +++ b/tests/neg/i4196.scala @@ -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 +} diff --git a/tests/pos/i4196.scala b/tests/pos/i4196.scala new file mode 100644 index 000000000000..24662df97962 --- /dev/null +++ b/tests/pos/i4196.scala @@ -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) +}