diff options
author | Dmitry Petrashko <dark@d-d.me> | 2016-04-26 13:35:04 +0200 |
---|---|---|
committer | Dmitry Petrashko <dark@d-d.me> | 2016-04-26 13:35:04 +0200 |
commit | 02f1ec90f02d347da8d9cafff8782906699cac66 (patch) | |
tree | 5d5598f1921574051368f0e7f648cee2fd252b0c | |
parent | 0514d0efcdf159ea0ffdb51ac1e9a59d3ee429c0 (diff) | |
parent | d20314fd9a741e1a3203dbacf0080e9e923559ca (diff) | |
download | dotty-02f1ec90f02d347da8d9cafff8782906699cac66.tar.gz dotty-02f1ec90f02d347da8d9cafff8782906699cac66.tar.bz2 dotty-02f1ec90f02d347da8d9cafff8782906699cac66.zip |
Merge pull request #1227 from dotty-staging/implement-1221
Allow to specify per-callsite @tailrec annotation.
-rw-r--r-- | src/dotty/tools/dotc/transform/TailRec.scala | 25 | ||||
-rw-r--r-- | tests/neg/tailcall/i1221.scala | 10 | ||||
-rw-r--r-- | tests/neg/tailcall/i1221b.scala | 10 | ||||
-rw-r--r-- | tests/pos/tailcall/i1221.scala | 10 |
4 files changed, 50 insertions, 5 deletions
diff --git a/src/dotty/tools/dotc/transform/TailRec.scala b/src/dotty/tools/dotc/transform/TailRec.scala index 23686b522..efa0633d8 100644 --- a/src/dotty/tools/dotc/transform/TailRec.scala +++ b/src/dotty/tools/dotc/transform/TailRec.scala @@ -73,6 +73,20 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete final val labelPrefix = "tailLabel" final val labelFlags = Flags.Synthetic | Flags.Label + /** Symbols of methods that have @tailrec annotatios inside */ + private val methodsWithInnerAnnots = new collection.mutable.HashSet[Symbol]() + + override def transformUnit(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = { + methodsWithInnerAnnots.clear() + tree + } + + override def transformTyped(tree: Typed)(implicit ctx: Context, info: TransformerInfo): Tree = { + if (tree.tpt.tpe.hasAnnotation(defn.TailrecAnnot)) + methodsWithInnerAnnots += ctx.owner.enclosingMethod + tree + } + private def mkLabel(method: Symbol, abstractOverClass: Boolean)(implicit c: Context): TermSymbol = { val name = c.freshName(labelPrefix) @@ -137,10 +151,10 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete } }) } - case d: DefDef if d.symbol.hasAnnotation(defn.TailrecAnnot) => + case d: DefDef if d.symbol.hasAnnotation(defn.TailrecAnnot) || methodsWithInnerAnnots.contains(d.symbol) => ctx.error("TailRec optimisation not applicable, method is neither private nor final so can be overridden", d.pos) d - case d if d.symbol.hasAnnotation(defn.TailrecAnnot) => + case d if d.symbol.hasAnnotation(defn.TailrecAnnot) || methodsWithInnerAnnots.contains(d.symbol) => ctx.error("TailRec optimisation not applicable, not a method", d.pos) d case _ => tree @@ -180,7 +194,7 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete override def transform(tree: Tree)(implicit c: Context): Tree = { /* A possibly polymorphic apply to be considered for tail call transformation. */ - def rewriteApply(tree: Tree, sym: Symbol): Tree = { + def rewriteApply(tree: Tree, sym: Symbol, required: Boolean = false): Tree = { def receiverArgumentsAndSymbol(t: Tree, accArgs: List[List[Tree]] = Nil, accT: List[Tree] = Nil): (Tree, Tree, List[List[Tree]], List[Tree], Symbol) = t match { case TypeApply(fun, targs) if fun.symbol eq t.symbol => receiverArgumentsAndSymbol(fun, accArgs, targs) @@ -216,7 +230,7 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete } } def fail(reason: String) = { - if (isMandatory) c.error(s"Cannot rewrite recursive call: $reason", tree.pos) + if (isMandatory || required) c.error(s"Cannot rewrite recursive call: $reason", tree.pos) else c.debuglog("Cannot rewrite recursive call at: " + tree.pos + " because: " + reason) continue } @@ -299,7 +313,8 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete noTailTransforms(stats), transform(expr) ) - + case tree @ Typed(t: Apply, tpt) if tpt.tpe.hasAnnotation(defn.TailrecAnnot) => + tpd.Typed(rewriteApply(t, t.fun.symbol, required = true), tpt) case tree@If(cond, thenp, elsep) => tpd.cpy.If(tree)( noTailTransform(cond), diff --git a/tests/neg/tailcall/i1221.scala b/tests/neg/tailcall/i1221.scala new file mode 100644 index 000000000..7cf9312f5 --- /dev/null +++ b/tests/neg/tailcall/i1221.scala @@ -0,0 +1,10 @@ +import annotation.tailrec + +object I1221{ + final def foo(a: Int): Int = { + if ((foo(a - 1): @tailrec) > 0) // error: not in tail position + foo(a - 1): @tailrec + else + foo(a - 2): @tailrec + } +} diff --git a/tests/neg/tailcall/i1221b.scala b/tests/neg/tailcall/i1221b.scala new file mode 100644 index 000000000..f8e2add9a --- /dev/null +++ b/tests/neg/tailcall/i1221b.scala @@ -0,0 +1,10 @@ +import annotation.tailrec + +class Test { + def foo(a: Int): Int = { // error: method is not final + if ((foo(a - 1): @tailrec) > 0) + foo(a - 1): @tailrec + else + foo(a - 2): @tailrec + } +} diff --git a/tests/pos/tailcall/i1221.scala b/tests/pos/tailcall/i1221.scala new file mode 100644 index 000000000..ba1dbc4a9 --- /dev/null +++ b/tests/pos/tailcall/i1221.scala @@ -0,0 +1,10 @@ +import annotation.tailrec + +object i1221{ + final def foo(a: Int): Int = { + if (foo(a - 1) > 0) + foo(a - 1): @tailrec + else + foo(a - 2): @tailrec + } +} |