summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/compiler/scala/tools/nsc/transform/TailCalls.scala21
-rw-r--r--test/files/neg/t3275.check4
-rw-r--r--test/files/neg/t3275.scala3
-rw-r--r--test/files/neg/t5455.check4
-rw-r--r--test/files/neg/t5455.scala16
-rw-r--r--test/files/neg/tailrec-2.check7
-rw-r--r--test/files/neg/tailrec-2.scala3
7 files changed, 54 insertions, 4 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/TailCalls.scala b/src/compiler/scala/tools/nsc/transform/TailCalls.scala
index 848d6be47b..fdb5c7e52e 100644
--- a/src/compiler/scala/tools/nsc/transform/TailCalls.scala
+++ b/src/compiler/scala/tools/nsc/transform/TailCalls.scala
@@ -223,8 +223,25 @@ abstract class TailCalls extends Transform {
}
tree match {
- case dd @ DefDef(_, _, _, vparamss0, _, rhs0) =>
+ case ValDef(_, _, _, _) =>
+ if (tree.symbol.isLazy && tree.symbol.hasAnnotation(TailrecClass))
+ unit.error(tree.pos, "lazy vals are not tailcall transformed")
+
+ super.transform(tree)
+
+ case dd @ DefDef(_, _, _, vparamss0, _, rhs0) if !dd.symbol.hasAccessorFlag =>
val newCtx = new Context(dd)
+ def isRecursiveCall(t: Tree) = {
+ val sym = t.symbol
+ (sym != null) && {
+ sym.isMethod && (dd.symbol.name == sym.name) && (dd.symbol.enclClass isSubClass sym.enclClass)
+ }
+ }
+ if (newCtx.isMandatory) {
+ if (!rhs0.exists(isRecursiveCall)) {
+ unit.error(tree.pos, "@tailrec annotated method contains no recursive calls")
+ }
+ }
debuglog("Considering " + dd.name + " for tailcalls")
val newRHS = transform(rhs0, newCtx)
@@ -248,7 +265,7 @@ abstract class TailCalls extends Transform {
))
}
else {
- if (newCtx.isMandatory)
+ if (newCtx.isMandatory && newRHS.exists(isRecursiveCall))
newCtx.tailrecFailure()
newRHS
diff --git a/test/files/neg/t3275.check b/test/files/neg/t3275.check
new file mode 100644
index 0000000000..117c792321
--- /dev/null
+++ b/test/files/neg/t3275.check
@@ -0,0 +1,4 @@
+t3275.scala:2: error: @tailrec annotated method contains no recursive calls
+ @annotation.tailrec def foo() = 5
+ ^
+one error found
diff --git a/test/files/neg/t3275.scala b/test/files/neg/t3275.scala
new file mode 100644
index 0000000000..18e38a1a97
--- /dev/null
+++ b/test/files/neg/t3275.scala
@@ -0,0 +1,3 @@
+object Test {
+ @annotation.tailrec def foo() = 5
+}
diff --git a/test/files/neg/t5455.check b/test/files/neg/t5455.check
new file mode 100644
index 0000000000..788daf99fa
--- /dev/null
+++ b/test/files/neg/t5455.check
@@ -0,0 +1,4 @@
+t5455.scala:4: error: lazy vals are not tailcall transformed
+ @annotation.tailrec final lazy val bar: Thing[Int] = {
+ ^
+one error found
diff --git a/test/files/neg/t5455.scala b/test/files/neg/t5455.scala
new file mode 100644
index 0000000000..22d6c442c9
--- /dev/null
+++ b/test/files/neg/t5455.scala
@@ -0,0 +1,16 @@
+trait Test {
+ def root: Test
+
+ @annotation.tailrec final lazy val bar: Thing[Int] = {
+ if (this eq root)
+ Thing(() => System.identityHashCode(bar))
+ else
+ root.bar
+ }
+
+ def f = bar.f()
+}
+
+case class Thing[A](f: () => A) {
+ override def toString = "" + f()
+}
diff --git a/test/files/neg/tailrec-2.check b/test/files/neg/tailrec-2.check
index 4f763a1c8d..a918858773 100644
--- a/test/files/neg/tailrec-2.check
+++ b/test/files/neg/tailrec-2.check
@@ -1,4 +1,7 @@
-tailrec-2.scala:6: error: could not optimize @tailrec annotated method f: it contains a recursive call targetting a supertype
+tailrec-2.scala:8: error: could not optimize @tailrec annotated method f: it contains a recursive call targetting a supertype
@annotation.tailrec final def f[B >: A](mem: List[B]): List[B] = (null: Super[A]).f(mem)
^
-one error found
+tailrec-2.scala:9: error: @tailrec annotated method contains no recursive calls
+ @annotation.tailrec final def f1[B >: A](mem: List[B]): List[B] = this.g(mem)
+ ^
+two errors found
diff --git a/test/files/neg/tailrec-2.scala b/test/files/neg/tailrec-2.scala
index 342cd85323..9eb3af2f07 100644
--- a/test/files/neg/tailrec-2.scala
+++ b/test/files/neg/tailrec-2.scala
@@ -1,9 +1,12 @@
sealed abstract class Super[+A] {
def f[B >: A](mem: List[B]) : List[B]
+ def g(mem: List[_]) = ???
}
// This one should fail, target is a supertype
class Bop1[+A](val element: A) extends Super[A] {
+
@annotation.tailrec final def f[B >: A](mem: List[B]): List[B] = (null: Super[A]).f(mem)
+ @annotation.tailrec final def f1[B >: A](mem: List[B]): List[B] = this.g(mem)
}
// These succeed
class Bop2[+A](val element: A) extends Super[A] {