Skip to content

Commit c1ad60e

Browse files
authored
Merge pull request #4197 from dotty-staging/fix/tailrec-implicitfuns
Fix #4196: TailRec-optimize methods returning implicit function types
2 parents 1183d0d + 928a559 commit c1ad60e

File tree

6 files changed

+35
-3
lines changed

6 files changed

+35
-3
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ class Compiler {
6767
new NormalizeFlags, // Rewrite some definition flags
6868
new ExtensionMethods, // Expand methods of value classes with extension methods
6969
new ExpandSAMs, // Expand single abstract method closures to anonymous classes
70+
new ShortcutImplicits, // Allow implicit functions without creating closures
7071
new TailRec, // Rewrite tail recursion to loops
7172
new ByNameClosures, // Expand arguments to by-name parameters to closures
7273
new LiftTry, // Put try expressions that might execute on non-empty stacks into their own methods
@@ -77,7 +78,6 @@ class Compiler {
7778
new PatternMatcher, // Compile pattern matches
7879
new ExplicitOuter, // Add accessors to outer classes from nested ones.
7980
new ExplicitSelf, // Make references to non-trivial self types explicit as casts
80-
new ShortcutImplicits, // Allow implicit functions without creating closures
8181
new CrossCastAnd, // Normalize selections involving intersection types.
8282
new Splitter) :: // Expand selections involving union types into conditionals
8383
List(new ErasedDecls, // Removes all erased defs and vals decls (except for parameters)

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase {
156156
private val cpy: TypedTreeCopier = cpyBetweenPhases
157157

158158
/** Transform node using all phases in this group that have idxInGroup >= start */
159-
def transformNode(tree: Tree, start: Int)(implicit ctx: Context) = {
159+
def transformNode(tree: Tree, start: Int)(implicit ctx: Context): Tree = {
160160
def goNamed(tree: Tree, start: Int) = tree match {
161161
case tree: Ident => goIdent(tree, start)
162162
case tree: Select => goSelect(tree, start)
@@ -169,6 +169,8 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase {
169169
def goUnnamed(tree: Tree, start: Int) = tree match {
170170
case tree: Apply => goApply(tree, start)
171171
case tree: TypeTree => goTypeTree(tree, start)
172+
case tree: Thicket =>
173+
cpy.Thicket(tree)(tree.trees.mapConserve(transformNode(_, start)))
172174
case tree: This => goThis(tree, start)
173175
case tree: Literal => goLiteral(tree, start)
174176
case tree: Block => goBlock(tree, start)

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ import collection.mutable
4848
class ShortcutImplicits extends MiniPhase with IdentityDenotTransformer { thisPhase =>
4949
import tpd._
5050

51-
override def phaseName: String = "shortcutImplicits"
51+
override def phaseName: String = ShortcutImplicits.name
5252

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

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

141+
// Move @tailrec to the direct method
142+
original.getAnnotation(defn.TailrecAnnot) match {
143+
case Some(annot) =>
144+
direct.addAnnotation(annot)
145+
original.removeAnnotation(defn.TailrecAnnot)
146+
case _ =>
147+
}
148+
141149
def splitClosure(tree: Tree): (List[Type] => List[List[Tree]] => Tree, Tree) = tree match {
142150
case Block(Nil, expr) => splitClosure(expr)
143151
case Block((meth @ DefDef(nme.ANON_FUN, Nil, clparams :: Nil, _, _)) :: Nil, cl: Closure) =>
@@ -167,3 +175,7 @@ class ShortcutImplicits extends MiniPhase with IdentityDenotTransformer { thisPh
167175
else mdef
168176
}
169177
}
178+
179+
object ShortcutImplicits {
180+
val name = "shortcutImplicits"
181+
}

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ class TailRec extends MiniPhase with FullParameterization {
7070

7171
override def phaseName: String = TailRec.name
7272

73+
override def runsAfter = Set(ShortcutImplicits.name) // Replaces non-tail calls by tail calls
74+
7375
final val labelFlags = Flags.Synthetic | Flags.Label
7476

7577
/** Symbols of methods that have @tailrec annotatios inside */

tests/neg/i4196.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
object Test {
2+
@annotation.tailrec
3+
def foo(i: implicit Unit => Int): implicit Unit => Int = // error: method not tail recursive
4+
if (i == 0)
5+
0
6+
else
7+
foo(i - 1)*2 // error: Cannot rewrite recursive call not in tail position
8+
}

tests/pos/i4196.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
object Test {
2+
@annotation.tailrec
3+
def foo(i: implicit Unit => Int): implicit Unit => Int =
4+
if (i == 0)
5+
0
6+
else
7+
foo(i - 1)
8+
}

0 commit comments

Comments
 (0)