summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2010-07-02 04:20:44 +0000
committerPaul Phillips <paulp@improving.org>2010-07-02 04:20:44 +0000
commitfbc453397552983a2c2a229cca8f7c13a641b9c7 (patch)
treeed5101b5693e52f7e8160e3bc7b38ed70ddf3892
parent58adc8d999687d1cfa6394d7f4201dfaf411ccfe (diff)
downloadscala-fbc453397552983a2c2a229cca8f7c13a641b9c7.tar.gz
scala-fbc453397552983a2c2a229cca8f7c13a641b9c7.tar.bz2
scala-fbc453397552983a2c2a229cca8f7c13a641b9c7.zip
Some more improvement on the error messages whe...
Some more improvement on the error messages when @tailrec fails. Now it gives a sensible message if the recursive target is actually a supertype of this, rather than saying the call is not in tail position. No review.
-rw-r--r--src/compiler/scala/tools/nsc/transform/TailCalls.scala29
-rw-r--r--test/files/neg/tailrec-2.check4
-rw-r--r--test/files/neg/tailrec-2.scala26
3 files changed, 49 insertions, 10 deletions
diff --git a/src/compiler/scala/tools/nsc/transform/TailCalls.scala b/src/compiler/scala/tools/nsc/transform/TailCalls.scala
index 9b54dd9428..c7a3e6a778 100644
--- a/src/compiler/scala/tools/nsc/transform/TailCalls.scala
+++ b/src/compiler/scala/tools/nsc/transform/TailCalls.scala
@@ -102,7 +102,7 @@ abstract class TailCalls extends Transform
var tailPos = false
/** The reason this method could not be optimized. */
- var tailrecFailReason = "it contains a recursive call not in tail position"
+ var tailrecFailReason = "reason indeterminate"
/** Is the label accessed? */
var accessed = false
@@ -153,6 +153,13 @@ abstract class TailCalls extends Transform
/** A possibly polymorphic apply to be considered for tail call transformation.
*/
def rewriteApply(target: Tree, fun: Tree, targs: List[Tree], args: List[Tree]) = {
+ def receiver = fun match {
+ case Select(qual, _) => Some(qual)
+ case _ => None
+ }
+
+ def receiverIsSame = receiver exists (enclosingType.widen =:= _.tpe.widen)
+ def receiverIsSuper = receiver exists (enclosingType.widen <:< _.tpe.widen)
def isRecursiveCall = ctx.currentMethod eq fun.symbol
def isMandatory = ctx.currentMethod hasAnnotation TailrecClass
def isEligible = ctx.currentMethod.isEffectivelyFinal
@@ -160,9 +167,6 @@ abstract class TailCalls extends Transform
def matchesTypeArgs = ctx.tparams sameElements (targs map (_.tpe.typeSymbol))
def defaultTree = treeCopy.Apply(tree, target, transformArgs)
- def sameTypeOfThis(receiver: Tree) =
- receiver.tpe.widen =:= enclosingType.widen
-
/** Records failure reason in Context for reporting.
*/
def cannotRewrite(reason: String) = {
@@ -171,6 +175,10 @@ abstract class TailCalls extends Transform
defaultTree
}
+ def notRecursiveReason() =
+ if (receiverIsSuper) "it contains a recursive call targetting a supertype"
+ else "it contains a recursive call not in tail position"
+
def rewriteTailCall(receiver: Tree, otherArgs: List[Tree]): Tree = {
log("Rewriting tail recursive method call at: " + fun.pos)
@@ -178,15 +186,16 @@ abstract class TailCalls extends Transform
typed { atPos(fun.pos)(Apply(Ident(ctx.label), receiver :: otherArgs)) }
}
- if (!isRecursiveCall) defaultTree
+ if (!isRecursiveCall) cannotRewrite(notRecursiveReason())
else if (!isEligible) cannotRewrite("it is neither private nor final so can be overridden")
else if (!ctx.tailPos) cannotRewrite("it contains a recursive call not in tail position")
else if (!matchesTypeArgs) cannotRewrite("it is called recursively with different type arguments")
- else fun match {
- case Select(_, _) if forMSIL => cannotRewrite("it cannot be optimized on MSIL")
- case Select(qual, _) if !sameTypeOfThis(qual) => cannotRewrite("it changes type of 'this' on a polymorphic recursive call")
- case Select(qual, _) => rewriteTailCall(qual, transformArgs)
- case _ => rewriteTailCall(This(currentClass), transformArgs)
+ else receiver match {
+ case Some(qual) =>
+ if (forMSIL) cannotRewrite("it cannot be optimized on MSIL")
+ else if (!receiverIsSame) cannotRewrite("it changes type of 'this' on a polymorphic recursive call")
+ else rewriteTailCall(qual, transformArgs)
+ case _ => rewriteTailCall(This(currentClass), transformArgs)
}
}
diff --git a/test/files/neg/tailrec-2.check b/test/files/neg/tailrec-2.check
new file mode 100644
index 0000000000..ab6733946d
--- /dev/null
+++ b/test/files/neg/tailrec-2.check
@@ -0,0 +1,4 @@
+tailrec-2.scala:6: error: could not optimize @tailrec annotated method: 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
diff --git a/test/files/neg/tailrec-2.scala b/test/files/neg/tailrec-2.scala
new file mode 100644
index 0000000000..4388815a06
--- /dev/null
+++ b/test/files/neg/tailrec-2.scala
@@ -0,0 +1,26 @@
+sealed abstract class Super[+A] {
+ def f[B >: A](mem: List[B]) : List[B]
+}
+// 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)
+}
+// These succeed
+class Bop2[+A](val element: A) extends Super[A] {
+ @annotation.tailrec final def f[B >: A](mem: List[B]): List[B] = (null: Bop2[A]).f(mem)
+}
+object Bop3 extends Super[Nothing] {
+ @annotation.tailrec final def f[B](mem: List[B]): List[B] = (null: Bop3.type).f(mem)
+}
+class Bop4[+A](val element: A) extends Super[A] {
+ @annotation.tailrec final def f[B >: A](mem: List[B]): List[B] = Other.f[A].f(mem)
+}
+
+object Other {
+ def f[T] : Bop4[T] = error("")
+}
+
+object Bop {
+ def m1[A] : Super[A] = error("")
+ def m2[A] : Bop2[A] = error("")
+} \ No newline at end of file