diff options
author | Dmitry Petrashko <dmitry.petrashko@gmail.com> | 2017-01-22 14:02:57 +0100 |
---|---|---|
committer | Dmitry Petrashko <dmitry.petrashko@gmail.com> | 2017-01-22 14:02:57 +0100 |
commit | 6c3814aff996a464880e36a0ab3d2a10a2051bfe (patch) | |
tree | 45127a2b53bee3a7339a2f24ca4fcb58b24ad8c8 /compiler/src/dotty/tools/dotc/transform | |
parent | 6a95fef182f23a610a4e80dfd9bfcbf23364f0ee (diff) | |
download | dotty-6c3814aff996a464880e36a0ab3d2a10a2051bfe.tar.gz dotty-6c3814aff996a464880e36a0ab3d2a10a2051bfe.tar.bz2 dotty-6c3814aff996a464880e36a0ab3d2a10a2051bfe.zip |
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.
Diffstat (limited to 'compiler/src/dotty/tools/dotc/transform')
-rw-r--r-- | compiler/src/dotty/tools/dotc/transform/TailRec.scala | 73 |
1 files changed, 39 insertions, 34 deletions
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 } |