Skip to content

Commit cb0eb7c

Browse files
committed
Fix #4196: TailRec-optimize methods returning implicit function types
Before `ShortcutImplicits`, calls to such methods are never in tail position since we call `.apply` on them to pass the implicit arguments. By moving `ShortcutImplicits` before `TailRec` we avoid this problem.
1 parent 8a4902e commit cb0eb7c

File tree

4 files changed

+25
-1
lines changed

4 files changed

+25
-1
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/ShortcutImplicits.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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) =>

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 =
4+
if (i == 0)
5+
0
6+
else
7+
foo(i - 1)*2 // error: method not tail recursive
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)