diff options
-rw-r--r-- | src/dotty/tools/dotc/Compiler.scala | 5 | ||||
-rw-r--r-- | src/dotty/tools/dotc/core/Definitions.scala | 4 | ||||
-rw-r--r-- | src/dotty/tools/dotc/transform/TailRec.scala | 341 | ||||
-rw-r--r-- | test/dotc/tests.scala | 9 | ||||
-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.scala | 10 | ||||
-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.scala | 5 | ||||
-rw-r--r-- | tests/pos/typers.scala | 2 | ||||
-rw-r--r-- | tests/untried/pos/t1672.scala | 10 |
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 - } - } -} |