aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/dotty/tools/dotc/Compiler.scala5
-rw-r--r--src/dotty/tools/dotc/core/Definitions.scala4
-rw-r--r--src/dotty/tools/dotc/transform/TailRec.scala341
-rw-r--r--test/dotc/tests.scala9
-rw-r--r--tests/neg/tailcall/t1672b.check (renamed from tests/untried/neg/t1672b.check)0
-rw-r--r--tests/neg/tailcall/t1672b.scala (renamed from tests/untried/neg/t1672b.scala)2
-rw-r--r--tests/neg/tailcall/t3275.check (renamed from tests/untried/neg/t3275.check)0
-rw-r--r--tests/neg/tailcall/t3275.scala (renamed from tests/untried/neg/t3275.scala)0
-rw-r--r--tests/neg/tailcall/t6574.check (renamed from tests/untried/neg/t6574.check)0
-rw-r--r--tests/neg/tailcall/t6574.scala (renamed from tests/untried/neg/t6574.scala)2
-rw-r--r--tests/neg/tailcall/tailrec-2.check (renamed from tests/untried/neg/tailrec-2.check)0
-rw-r--r--tests/neg/tailcall/tailrec-2.scala (renamed from tests/untried/neg/tailrec-2.scala)0
-rw-r--r--tests/neg/tailcall/tailrec-3.check (renamed from tests/untried/neg/tailrec-3.check)0
-rw-r--r--tests/neg/tailcall/tailrec-3.scala (renamed from tests/untried/neg/tailrec-3.scala)0
-rw-r--r--tests/neg/tailcall/tailrec.check (renamed from tests/untried/neg/tailrec.check)0
-rw-r--r--tests/neg/tailcall/tailrec.scala (renamed from tests/untried/neg/tailrec.scala)0
-rw-r--r--tests/pos/tailcall/t1672.scala10
-rw-r--r--tests/pos/tailcall/t4649.flags (renamed from tests/untried/pos/t4649.flags)0
-rw-r--r--tests/pos/tailcall/t4649.scala (renamed from tests/untried/pos/t4649.scala)2
-rw-r--r--tests/pos/tailcall/t6479.scala (renamed from tests/untried/pos/t6479.scala)0
-rw-r--r--tests/pos/tailcall/t6574.scala (renamed from tests/untried/pos/t6574.scala)2
-rw-r--r--tests/pos/tailcall/t6891.flags (renamed from tests/untried/pos/t6891.flags)0
-rw-r--r--tests/pos/tailcall/t6891.scala (renamed from tests/untried/pos/t6891.scala)2
-rw-r--r--tests/pos/tailcall/tailcall.scala5
-rw-r--r--tests/pos/typers.scala2
-rw-r--r--tests/untried/pos/t1672.scala10
26 files changed, 377 insertions, 19 deletions
diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala
index 8bcd919c2..2ba75c56b 100644
--- a/src/dotty/tools/dotc/Compiler.scala
+++ b/src/dotty/tools/dotc/Compiler.scala
@@ -20,8 +20,9 @@ class Compiler {
def phases: List[List[Phase]] =
List(
List(new FrontEnd),
- List(new LazyValsCreateCompanionObjects, new PatternMatcher), //force separataion between lazyVals and LVCreateCO
- List(new LazyValTranformContext().transformer, new Splitter, new TypeTestsCasts, new InterceptedMethods),
+ List(new LazyValsCreateCompanionObjects, new TailRec), //force separataion between lazyVals and LVCreateCO
+ List(new PatternMatcher, new LazyValTranformContext().transformer,
+ new Splitter, new TypeTestsCasts, new InterceptedMethods),
List(new Erasure),
List(new UncurryTreeTransform)
)
diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala
index 40fd33671..0068c0a77 100644
--- a/src/dotty/tools/dotc/core/Definitions.scala
+++ b/src/dotty/tools/dotc/core/Definitions.scala
@@ -172,8 +172,9 @@ class Definitions {
lazy val UnitClass = valueClassSymbol("scala.Unit", BoxedUnitClass, java.lang.Void.TYPE, UnitEnc)
lazy val BooleanClass = valueClassSymbol("scala.Boolean", BoxedBooleanClass, java.lang.Boolean.TYPE, BooleanEnc)
- lazy val Boolean_! = BooleanClass.requiredMethod(nme.UNARY_!)
+ lazy val Boolean_! = BooleanClass.requiredMethod(nme.UNARY_!)
lazy val Boolean_and = BooleanClass.requiredMethod(nme.ZAND)
+ lazy val Boolean_or = BooleanClass.requiredMethod(nme.ZOR)
lazy val ByteClass = valueClassSymbol("scala.Byte", BoxedByteClass, java.lang.Byte.TYPE, ByteEnc)
lazy val ShortClass = valueClassSymbol("scala.Short", BoxedShortClass, java.lang.Short.TYPE, ShortEnc)
@@ -236,6 +237,7 @@ class Definitions {
lazy val AnnotationClass = ctx.requiredClass("scala.annotation.Annotation")
lazy val ClassfileAnnotationClass = ctx.requiredClass("scala.annotation.ClassfileAnnotation")
lazy val StaticAnnotationClass = ctx.requiredClass("scala.annotation.StaticAnnotation")
+ lazy val TailrecAnnotationClass = ctx.requiredClass("scala.annotation.tailrec")
// Annotation classes
lazy val AliasAnnot = ctx.requiredClass("dotty.annotation.internal.Alias")
diff --git a/src/dotty/tools/dotc/transform/TailRec.scala b/src/dotty/tools/dotc/transform/TailRec.scala
new file mode 100644
index 000000000..543510dd7
--- /dev/null
+++ b/src/dotty/tools/dotc/transform/TailRec.scala
@@ -0,0 +1,341 @@
+package dotty.tools.dotc.transform
+
+import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer}
+import dotty.tools.dotc.ast.{Trees, tpd}
+import dotty.tools.dotc.core.Contexts.Context
+import scala.collection.mutable.ListBuffer
+import dotty.tools.dotc.core._
+import dotty.tools.dotc.core.Symbols.NoSymbol
+import scala.annotation.tailrec
+import Types._, Contexts._, Constants._, Names._, NameOps._, Flags._
+import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._
+import Decorators._
+import Symbols._
+import scala.Some
+import dotty.tools.dotc.transform.TreeTransforms.{NXTransformations, TransformerInfo, TreeTransform, TreeTransformer}
+import dotty.tools.dotc.core.Contexts.Context
+import scala.collection.mutable
+import dotty.tools.dotc.core.Names.Name
+import NameOps._
+import dotty.tools.dotc.CompilationUnit
+import dotty.tools.dotc.util.Positions.{Position, Coord}
+import dotty.tools.dotc.util.Positions.NoPosition
+import dotty.tools.dotc.core.DenotTransformers.DenotTransformer
+import dotty.tools.dotc.core.Denotations.SingleDenotation
+import dotty.tools.dotc.transform.TailRec._
+
+/**
+ * A Tail Rec Transformer
+ *
+ * @author Erik Stenman, Iulian Dragos,
+ * ported to dotty by Dmitry Petrashko
+ * @version 1.1
+ *
+ * What it does:
+ * <p>
+ * Finds method calls in tail-position and replaces them with jumps.
+ * A call is in a tail-position if it is the last instruction to be
+ * executed in the body of a method. This is done by recursing over
+ * the trees that may contain calls in tail-position (trees that can't
+ * contain such calls are not transformed). However, they are not that
+ * many.
+ * </p>
+ * <p>
+ * Self-recursive calls in tail-position are replaced by jumps to a
+ * label at the beginning of the method. As the JVM provides no way to
+ * jump from a method to another one, non-recursive calls in
+ * tail-position are not optimized.
+ * </p>
+ * <p>
+ * A method call is self-recursive if it calls the current method and
+ * the method is final (otherwise, it could
+ * be a call to an overridden method in a subclass).
+ *
+ * Recursive calls on a different instance
+ * are optimized. Since 'this' is not a local variable it s added as
+ * a label parameter.
+ * </p>
+ * <p>
+ * This phase has been moved before pattern matching to catch more
+ * of the common cases of tail recursive functions. This means that
+ * more cases should be taken into account (like nested function, and
+ * pattern cases).
+ * </p>
+ * <p>
+ * If a method contains self-recursive calls, a label is added to at
+ * the beginning of its body and the calls are replaced by jumps to
+ * that label.
+ * </p>
+ * <p>
+ *
+ * In scalac, If the method had type parameters, the call must contain same
+ * parameters as type arguments. This is no longer case in dotc.
+ * In scalac, this is named tailCall but it does only provide optimization for
+ * self recursive functions, that's why it's renamed to tailrec
+ * </p>
+ */
+class TailRec extends TreeTransform with DenotTransformer {
+
+ import tpd._
+
+ override def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ref
+
+ override def name: String = "tailrec"
+
+ final val labelPrefix = "tailLabel"
+
+ private def mkLabel(method: Symbol, tp: Type)(implicit c: Context): TermSymbol = {
+ val name = c.freshName(labelPrefix)
+ c.newSymbol(method, name.toTermName, Flags.Synthetic, tp)
+ }
+
+ override def transformDefDef(tree: tpd.DefDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
+ tree match {
+ case dd@DefDef(mods, name, tparams, vparamss0, tpt, rhs0)
+ if (dd.symbol.isEffectivelyFinal) && !((dd.symbol is Flags.Accessor) || (rhs0 eq EmptyTree)) =>
+ val mandatory = dd.symbol.hasAnnotation(defn.TailrecAnnotationClass)
+ cpy.DefDef(tree, mods, name, tparams, vparamss0, tpt, rhs = {
+ val owner = ctx.owner.enclosingClass
+
+ val thisTpe = owner.thisType
+
+ val newType: Type = dd.tpe.widen match {
+ case t: PolyType => PolyType(t.paramNames)(x => t.paramBounds,
+ x => MethodType(List(nme.THIS), List(thisTpe), t.resultType))
+ case t => MethodType(List(nme.THIS), List(thisTpe), t)
+ }
+
+ val label = mkLabel(dd.symbol, newType)
+ var rewrote = false
+
+ // Note: this can be split in two separate transforms(in different groups),
+ // than first one will collect info about which transformations and rewritings should be applied
+ // and second one will actually apply,
+ // now this speculatively transforms tree and throws away result in many cases
+ val res = tpd.Closure(label, args => {
+ val thiz = args.head.head
+ val argMapping: Map[Symbol, Tree] = (vparamss0.flatten.map(_.symbol) zip args.tail.flatten).toMap
+ val transformer = new TailRecElimination(dd.symbol, thiz, argMapping, owner, mandatory, label)
+ val rhs = transformer.transform(rhs0)(ctx.withPhase(ctx.phase.next))
+ rewrote = transformer.rewrote
+ rhs
+ }, tparams)
+
+ if (rewrote) res
+ else {
+ if (mandatory)
+ ctx.error("TailRec optimisation not applicable, method not tail recursive", dd.pos)
+ rhs0
+ }
+ })
+ case d: DefDef if d.symbol.hasAnnotation(defn.TailrecAnnotationClass) =>
+ 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.TailrecAnnotationClass) =>
+ ctx.error("TailRec optimisation not applicable, not a method", d.pos)
+ d
+ case _ => tree
+ }
+
+ }
+
+ class TailRecElimination(method: Symbol, thiz: Tree, argMapping: Map[Symbol, Tree],
+ enclosingClass: Symbol, isMandatory: Boolean, label: Symbol) extends tpd.TreeMap {
+
+ import tpd._
+
+
+ var rewrote = false
+
+ private val defaultReason = "it contains a recursive call not in tail position"
+
+ private var ctx: TailContext = yesTailContext
+
+ /** Rewrite this tree to contain no tail recursive calls */
+ def transform(tree: Tree, nctx: TailContext)(implicit c: Context): Tree = {
+ if (ctx == nctx) transform(tree)
+ else {
+ val saved = ctx
+ ctx = nctx
+ try transform(tree)
+ finally this.ctx = saved
+ }
+ }
+
+ def yesTailTransform(tree: Tree)(implicit c: Context): Tree =
+ transform(tree, yesTailContext)
+
+ def noTailTransform(tree: Tree)(implicit c: Context): Tree =
+ transform(tree, noTailContext)
+
+
+ def noTailTransforms(trees: List[Tree])(implicit c: Context) =
+ trees map (noTailTransform)
+
+
+ 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 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)
+ case Apply(fn, args) if fn.symbol == t.symbol => receiverArgumentsAndSymbol(fn, args :: accArgs, accT)
+ case Select(qual, _) => (qual, t, accArgs, accT, t.symbol)
+ case x: This => (x, x, accArgs, accT, x.symbol)
+ case x: Ident if x.symbol eq method => (EmptyTree, x, accArgs, accT, x.symbol)
+ case x => (x, x, accArgs, accT, x.symbol)
+ }
+
+ val (reciever, call, arguments, typeArguments, symbol) = receiverArgumentsAndSymbol(tree)
+ val recv = noTailTransform(reciever)
+
+ val targs = typeArguments.map(noTailTransform)
+ val argumentss = arguments.map(noTailTransforms)
+
+ val receiverIsSame = enclosingClass.typeRef.widen =:= recv.tpe.widen
+ val receiverIsSuper = (method.name eq sym) && enclosingClass.typeRef.widen <:< recv.tpe.widen
+ val receiverIsThis = recv.tpe.widen =:= thiz.tpe.widen
+
+ val isRecursiveCall = (method eq sym)
+
+ def continue = {
+ val method = noTailTransform(call)
+ val methodWithTargs = if (targs.nonEmpty) TypeApply(method, targs) else method
+ if (methodWithTargs.tpe.widen.isParameterless) methodWithTargs
+ else argumentss.foldLeft(methodWithTargs) {
+ case (method, args) => Apply(method, args)
+ }
+ }
+ def fail(reason: String) = {
+ if (isMandatory) c.error(s"Cannot rewrite recursive call: $reason", tree.pos)
+ else c.debuglog("Cannot rewrite recursive call at: " + tree.pos + " because: " + reason)
+ continue
+ }
+
+ def rewriteTailCall(recv: Tree): Tree = {
+ c.debuglog("Rewriting tail recursive call: " + tree.pos)
+ rewrote = true
+ val method = if (targs.nonEmpty) TypeApply(Ident(label.termRef), targs) else Ident(label.termRef)
+ val recv = noTailTransform(reciever)
+ if (recv.tpe.widen.isParameterless) method
+ else argumentss.foldLeft(Apply(method, List(recv))) {
+ case (method, args) => Apply(method, args)
+ }
+ }
+
+ if (isRecursiveCall) {
+ if (ctx.tailPos) {
+ if (recv eq EmptyTree) rewriteTailCall(thiz)
+ else if (receiverIsSame || receiverIsThis) rewriteTailCall(recv)
+ else fail("it changes type of 'this' on a polymorphic recursive call")
+ }
+ else fail(defaultReason)
+ } else {
+ if (receiverIsSuper) fail("it contains a recursive call targeting a supertype")
+ else continue
+ }
+ }
+
+ def rewriteTry(tree: Try): Tree = {
+ def transformHandlers(t: Tree): Tree = {
+ t match {
+ case Block(List((d: DefDef)), cl@Closure(Nil, _, EmptyTree)) =>
+ val newDef = cpy.DefDef(d, d.mods, d.name, d.tparams, d.vparamss, d.tpt, transform(d.rhs))
+ Block(List(newDef), cl)
+ case _ => assert(false, s"failed to deconstruct try handler ${t.show}"); ???
+ }
+ }
+ if (tree.finalizer eq EmptyTree) {
+ // SI-1672 Catches are in tail position when there is no finalizer
+ tpd.cpy.Try(tree,
+ noTailTransform(tree.expr),
+ transformHandlers(tree.handler),
+ EmptyTree
+ )
+ }
+ else {
+ tpd.cpy.Try(tree,
+ noTailTransform(tree.expr),
+ noTailTransform(tree.handler),
+ noTailTransform(tree.finalizer)
+ )
+ }
+ }
+
+ val res: Tree = tree match {
+ case Block(stats, expr) =>
+ tpd.cpy.Block(tree,
+ noTailTransforms(stats),
+ transform(expr)
+ )
+
+ case t@CaseDef(pat, guard, body) =>
+ cpy.CaseDef(t, pat, guard, transform(body))
+
+ case If(cond, thenp, elsep) =>
+ tpd.cpy.If(tree,
+ transform(cond),
+ transform(thenp),
+ transform(elsep)
+ )
+
+ case Match(selector, cases) =>
+ tpd.cpy.Match(tree,
+ noTailTransform(selector),
+ transformSub(cases)
+ )
+
+ case t: Try =>
+ rewriteTry(t)
+
+ case Apply(fun, args) if fun.symbol == defn.Boolean_or || fun.symbol == defn.Boolean_and =>
+ tpd.cpy.Apply(tree, fun, transform(args))
+
+ case Apply(fun, args) =>
+ rewriteApply(tree, fun.symbol)
+ case Alternative(_) | Bind(_, _) =>
+ assert(false, "We should've never gotten inside a pattern")
+ tree
+ case This(cls) if cls eq enclosingClass =>
+ thiz
+ case Select(qual, name) =>
+ val sym = tree.symbol
+ if (sym == method && ctx.tailPos) rewriteApply(tree, sym)
+ else tpd.cpy.Select(tree, noTailTransform(qual), name)
+ case ValDef(_, _, _, _) | EmptyTree | Super(_, _) | This(_) |
+ Literal(_) | TypeTree(_) | DefDef(_, _, _, _, _, _) | TypeDef(_, _, _) =>
+ tree
+ case Ident(qual) =>
+ val sym = tree.symbol
+ if (sym == method && ctx.tailPos) rewriteApply(tree, sym)
+ else argMapping.get(sym) match {
+ case Some(rewrite) => rewrite
+ case None => tree.tpe match {
+ case TermRef(ThisType(`enclosingClass`), _) =>
+ if (sym.flags is Flags.Local) {
+ // trying to access private[this] member. toggle flag in order to access.
+ val d = sym.denot
+ val newDenot = d.copySymDenotation(initFlags = sym.flags &~ Flags.Local)
+ newDenot.installAfter(TailRec.this)
+ }
+ Select(thiz, sym)
+ case _ => tree
+ }
+ }
+ case _ =>
+ super.transform(tree)
+ }
+
+ res
+ }
+ }
+
+}
+
+object TailRec {
+
+ final class TailContext(val tailPos: Boolean) extends AnyVal
+
+ final val noTailContext = new TailContext(false)
+ final val yesTailContext = new TailContext(true)
+}
diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala
index 3228c3474..29c50c556 100644
--- a/test/dotc/tests.scala
+++ b/test/dotc/tests.scala
@@ -47,6 +47,9 @@ class tests extends CompilerTest {
@Test def pos_i39 = compileFile(posDir, "i39", doErase)
@Test def pos_overloadedAccess = compileFile(posDir, "overloadedAccess", doErase)
@Test def pos_approximateUnion = compileFile(posDir, "approximateUnion", doErase)
+ @Test def pos_tailcall = compileDir(posDir + "tailcall/", doErase)
+
+
@Test def pos_all = compileFiles(posDir, twice)
@Test def new_all = compileFiles(newDir, twice)
@@ -69,6 +72,12 @@ class tests extends CompilerTest {
@Test def neg_t0625_structural = compileFile(negDir, "t0625", xerrors = 1)
@Test def neg_t0654_polyalias = compileFile(negDir, "t0654", xerrors = 2)
@Test def neg_t1192_legalPrefix = compileFile(negDir, "t1192", xerrors = 1)
+ @Test def neg_tailcall_t1672b = compileFile(negDir, "tailcall/t1672b", xerrors = 6)
+ @Test def neg_tailcall_t3275 = compileFile(negDir, "tailcall/t3275", xerrors = 1)
+ @Test def neg_tailcall_t6574 = compileFile(negDir, "tailcall/t6574", xerrors = 4)
+ @Test def neg_tailcall = compileFile(negDir, "tailcall/tailrec", xerrors = 7)
+ @Test def neg_tailcall2 = compileFile(negDir, "tailcall/tailrec-2", xerrors = 2)
+ @Test def neg_tailcall3 = compileFile(negDir, "tailcall/tailrec-3", xerrors = 2)
@Test def dotc = compileDir(dotcDir + "tools/dotc", twice)
@Test def dotc_ast = compileDir(dotcDir + "tools/dotc/ast", twice)
diff --git a/tests/untried/neg/t1672b.check b/tests/neg/tailcall/t1672b.check
index 60ccf7717..60ccf7717 100644
--- a/tests/untried/neg/t1672b.check
+++ b/tests/neg/tailcall/t1672b.check
diff --git a/tests/untried/neg/t1672b.scala b/tests/neg/tailcall/t1672b.scala
index 0ccdd0363..f05d05c34 100644
--- a/tests/untried/neg/t1672b.scala
+++ b/tests/neg/tailcall/t1672b.scala
@@ -1,4 +1,4 @@
-object Test {
+object Test1772B {
@annotation.tailrec
def bar : Nothing = {
try {
diff --git a/tests/untried/neg/t3275.check b/tests/neg/tailcall/t3275.check
index 117c79232..117c79232 100644
--- a/tests/untried/neg/t3275.check
+++ b/tests/neg/tailcall/t3275.check
diff --git a/tests/untried/neg/t3275.scala b/tests/neg/tailcall/t3275.scala
index 18e38a1a9..18e38a1a9 100644
--- a/tests/untried/neg/t3275.scala
+++ b/tests/neg/tailcall/t3275.scala
diff --git a/tests/untried/neg/t6574.check b/tests/neg/tailcall/t6574.check
index c67b4ed80..c67b4ed80 100644
--- a/tests/untried/neg/t6574.check
+++ b/tests/neg/tailcall/t6574.check
diff --git a/tests/untried/neg/t6574.scala b/tests/neg/tailcall/t6574.scala
index 9e1d624e5..59f3108ad 100644
--- a/tests/untried/neg/t6574.scala
+++ b/tests/neg/tailcall/t6574.scala
@@ -4,7 +4,7 @@ class Bad[X, Y](val v: Int) extends AnyVal {
println("tail")
}
- @annotation.tailrec final def differentTypeArgs {: Unit =
+ @annotation.tailrec final def differentTypeArgs : Unit = {
{(); new Bad[String, Unit](0)}.differentTypeArgs
}
}
diff --git a/tests/untried/neg/tailrec-2.check b/tests/neg/tailcall/tailrec-2.check
index 1daad6922..1daad6922 100644
--- a/tests/untried/neg/tailrec-2.check
+++ b/tests/neg/tailcall/tailrec-2.check
diff --git a/tests/untried/neg/tailrec-2.scala b/tests/neg/tailcall/tailrec-2.scala
index d6b8b1355..d6b8b1355 100644
--- a/tests/untried/neg/tailrec-2.scala
+++ b/tests/neg/tailcall/tailrec-2.scala
diff --git a/tests/untried/neg/tailrec-3.check b/tests/neg/tailcall/tailrec-3.check
index a3542fb56..a3542fb56 100644
--- a/tests/untried/neg/tailrec-3.check
+++ b/tests/neg/tailcall/tailrec-3.check
diff --git a/tests/untried/neg/tailrec-3.scala b/tests/neg/tailcall/tailrec-3.scala
index 20361658e..20361658e 100644
--- a/tests/untried/neg/tailrec-3.scala
+++ b/tests/neg/tailcall/tailrec-3.scala
diff --git a/tests/untried/neg/tailrec.check b/tests/neg/tailcall/tailrec.check
index 946d3421e..946d3421e 100644
--- a/tests/untried/neg/tailrec.check
+++ b/tests/neg/tailcall/tailrec.check
diff --git a/tests/untried/neg/tailrec.scala b/tests/neg/tailcall/tailrec.scala
index 83a0c1a9e..83a0c1a9e 100644
--- a/tests/untried/neg/tailrec.scala
+++ b/tests/neg/tailcall/tailrec.scala
diff --git a/tests/pos/tailcall/t1672.scala b/tests/pos/tailcall/t1672.scala
new file mode 100644
index 000000000..9be5c6066
--- /dev/null
+++ b/tests/pos/tailcall/t1672.scala
@@ -0,0 +1,10 @@
+object Test1672 {
+ @annotation.tailrec
+ def bar(x: Int)(y: Int) : Nothing = {
+ try {
+ throw new RuntimeException
+ } catch {
+ case _: Throwable => bar(x)(y)
+ }
+ }
+}
diff --git a/tests/untried/pos/t4649.flags b/tests/pos/tailcall/t4649.flags
index e8fb65d50..e8fb65d50 100644
--- a/tests/untried/pos/t4649.flags
+++ b/tests/pos/tailcall/t4649.flags
diff --git a/tests/untried/pos/t4649.scala b/tests/pos/tailcall/t4649.scala
index 0d6caa8d7..5f009b7a4 100644
--- a/tests/untried/pos/t4649.scala
+++ b/tests/pos/tailcall/t4649.scala
@@ -1,4 +1,4 @@
-object Test {
+object Test4649 {
// @annotation.tailrec
def lazyFilter[E](s: Stream[E], p: E => Boolean): Stream[E] = s match {
case h #:: t => if (p(h)) h #:: lazyFilter(t, p) else lazyFilter(t, p)
diff --git a/tests/untried/pos/t6479.scala b/tests/pos/tailcall/t6479.scala
index e4a4ff601..e4a4ff601 100644
--- a/tests/untried/pos/t6479.scala
+++ b/tests/pos/tailcall/t6479.scala
diff --git a/tests/untried/pos/t6574.scala b/tests/pos/tailcall/t6574.scala
index 6bb0042c6..cd0fdbb8d 100644
--- a/tests/untried/pos/t6574.scala
+++ b/tests/pos/tailcall/t6574.scala
@@ -4,7 +4,7 @@ class Bad[X, Y](val v: Int) extends AnyVal {
this.foo[Z](a)(b)
}
- @annotation.tailrec final def differentReceiver {: Unit =
+ @annotation.tailrec final def differentReceiver : Unit = {
{(); new Bad[X, Y](0)}.differentReceiver
}
diff --git a/tests/untried/pos/t6891.flags b/tests/pos/tailcall/t6891.flags
index fe048006a..fe048006a 100644
--- a/tests/untried/pos/t6891.flags
+++ b/tests/pos/tailcall/t6891.flags
diff --git a/tests/untried/pos/t6891.scala b/tests/pos/tailcall/t6891.scala
index bed2d0d77..edbe6f097 100644
--- a/tests/untried/pos/t6891.scala
+++ b/tests/pos/tailcall/t6891.scala
@@ -1,4 +1,4 @@
-object O {
+object O6891 {
implicit class Foo[A](val value: String) extends AnyVal {
def bippy() = {
@annotation.tailrec def loop(x: A): Unit = loop(x)
diff --git a/tests/pos/tailcall/tailcall.scala b/tests/pos/tailcall/tailcall.scala
new file mode 100644
index 000000000..9cf373cf0
--- /dev/null
+++ b/tests/pos/tailcall/tailcall.scala
@@ -0,0 +1,5 @@
+class tailcall {
+ val shift = 1
+ final def fact(x: Int, acc: Int = 1): Int = if (x == 0) acc else fact(x - shift, acc * x)
+ def id[T <: AnyRef](x: T): T = if (x eq null) x else id(x)
+}
diff --git a/tests/pos/typers.scala b/tests/pos/typers.scala
index 4f012e7bf..a95af558e 100644
--- a/tests/pos/typers.scala
+++ b/tests/pos/typers.scala
@@ -77,7 +77,7 @@ object typers {
class C {
- @tailrec def factorial(acc: Int, n: Int): Int = (n: @switch) match {
+ @tailrec final def factorial(acc: Int, n: Int): Int = (n: @switch) match {
case 0 => acc
case _ => factorial(acc * n, n - 1)
}
diff --git a/tests/untried/pos/t1672.scala b/tests/untried/pos/t1672.scala
deleted file mode 100644
index 5ee6bb175..000000000
--- a/tests/untried/pos/t1672.scala
+++ /dev/null
@@ -1,10 +0,0 @@
-object Test {
- @annotation.tailrec
- def bar : Nothing = {
- try {
- throw new RuntimeException
- } catch {
- case _: Throwable => bar
- }
- }
-}