From 6c3814aff996a464880e36a0ab3d2a10a2051bfe Mon Sep 17 00:00:00 2001 From: Dmitry Petrashko Date: Sun, 22 Jan 2017 14:02:57 +0100 Subject: Fix #1687: postpone computations in tailrec until they are needed. Previous version precomputed everything needed to make the final decision. This was fast-path for method that will be tail-rec transformed. Unfortunatelly, in case decision was not to tail-rec optimize it could have led to exponential number of transformations. Now, the fast-path is for methods that will not. --- .../src/dotty/tools/dotc/transform/TailRec.scala | 73 ++++++++++++---------- 1 file changed, 39 insertions(+), 34 deletions(-) (limited to 'compiler') diff --git a/compiler/src/dotty/tools/dotc/transform/TailRec.scala b/compiler/src/dotty/tools/dotc/transform/TailRec.scala index dc4454439..3e7a7ed89 100644 --- a/compiler/src/dotty/tools/dotc/transform/TailRec.scala +++ b/compiler/src/dotty/tools/dotc/transform/TailRec.scala @@ -216,18 +216,13 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete val (prefix, call, arguments, typeArguments, symbol) = receiverArgumentsAndSymbol(tree) val hasConformingTargs = (typeArguments zip methTparams).forall{x => x._1.tpe <:< x._2.tpe} - val recv = noTailTransform(prefix) val targs = typeArguments.map(noTailTransform) val argumentss = arguments.map(noTailTransforms) - val recvWiden = recv.tpe.widenDealias - - val receiverIsSame = enclosingClass.typeRef.widenDealias =:= recvWiden - val receiverIsSuper = (method.name eq sym) && enclosingClass.typeRef.widen <:< recvWiden - val receiverIsThis = recv.tpe =:= thisType || recv.tpe.widen =:= thisType - val isRecursiveCall = (method eq sym) + val recvWiden = prefix.tpe.widenDealias + def continue = { val method = noTailTransform(call) @@ -244,40 +239,50 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete continue } - def rewriteTailCall(recv: Tree): Tree = { - c.debuglog("Rewriting tail recursive call: " + tree.pos) - rewrote = true - val receiver = noTailTransform(recv) - - val callTargs: List[tpd.Tree] = - if (abstractOverClass) { - val classTypeArgs = recv.tpe.baseTypeWithArgs(enclosingClass).argInfos - targs ::: classTypeArgs.map(x => ref(x.typeSymbol)) - } else targs - - val method = if (callTargs.nonEmpty) TypeApply(Ident(label.termRef), callTargs) else Ident(label.termRef) - val thisPassed = - if (this.method.owner.isClass) - method.appliedTo(receiver.ensureConforms(method.tpe.widen.firstParamTypes.head)) - else method - - val res = - if (thisPassed.tpe.widen.isParameterless) thisPassed - else argumentss.foldLeft(thisPassed) { - (met, ar) => Apply(met, ar) // Dotty deviation no auto-detupling yet. - } - res - } + if (isRecursiveCall) { if (ctx.tailPos) { + val receiverIsSame = enclosingClass.typeRef.widenDealias =:= recvWiden + val receiverIsThis = prefix.tpe =:= thisType || prefix.tpe.widen =:= thisType + + def rewriteTailCall(recv: Tree): Tree = { + c.debuglog("Rewriting tail recursive call: " + tree.pos) + rewrote = true + val receiver = noTailTransform(recv) + + val callTargs: List[tpd.Tree] = + if (abstractOverClass) { + val classTypeArgs = recv.tpe.baseTypeWithArgs(enclosingClass).argInfos + targs ::: classTypeArgs.map(x => ref(x.typeSymbol)) + } else targs + + val method = if (callTargs.nonEmpty) TypeApply(Ident(label.termRef), callTargs) else Ident(label.termRef) + val thisPassed = + if (this.method.owner.isClass) + method.appliedTo(receiver.ensureConforms(method.tpe.widen.firstParamTypes.head)) + else method + + val res = + if (thisPassed.tpe.widen.isParameterless) thisPassed + else argumentss.foldLeft(thisPassed) { + (met, ar) => Apply(met, ar) // Dotty deviation no auto-detupling yet. + } + res + } + if (!hasConformingTargs) fail("it changes type arguments on a polymorphic recursive call") - else if (recv eq EmptyTree) rewriteTailCall(This(enclosingClass.asClass)) - else if (receiverIsSame || receiverIsThis) rewriteTailCall(recv) - else fail("it changes type of 'this' on a polymorphic recursive call") + else { + val recv = noTailTransform(prefix) + if (recv eq EmptyTree) rewriteTailCall(This(enclosingClass.asClass)) + else if (receiverIsSame || receiverIsThis) rewriteTailCall(recv) + else fail("it changes type of 'this' on a polymorphic recursive call") + } } else fail(defaultReason) } else { + val receiverIsSuper = (method.name eq sym) && enclosingClass.typeRef.widen <:< recvWiden + if (receiverIsSuper) fail("it contains a recursive call targeting a supertype") else continue } -- cgit v1.2.3