aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitry Petrashko <dark@d-d.me>2016-04-26 13:35:04 +0200
committerDmitry Petrashko <dark@d-d.me>2016-04-26 13:35:04 +0200
commit02f1ec90f02d347da8d9cafff8782906699cac66 (patch)
tree5d5598f1921574051368f0e7f648cee2fd252b0c
parent0514d0efcdf159ea0ffdb51ac1e9a59d3ee429c0 (diff)
parentd20314fd9a741e1a3203dbacf0080e9e923559ca (diff)
downloaddotty-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.scala25
-rw-r--r--tests/neg/tailcall/i1221.scala10
-rw-r--r--tests/neg/tailcall/i1221b.scala10
-rw-r--r--tests/pos/tailcall/i1221.scala10
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
+ }
+}