Skip to content

Commit d5201d3

Browse files
authored
Merge pull request #1909 from dotty-staging/fix-1687
Fix #1687: postpone computations in tailrec until they are needed.
2 parents 494aee2 + 945001d commit d5201d3

File tree

3 files changed

+68
-34
lines changed

3 files changed

+68
-34
lines changed

compiler/src/dotty/tools/dotc/transform/TailRec.scala

Lines changed: 39 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -216,18 +216,13 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete
216216

217217
val (prefix, call, arguments, typeArguments, symbol) = receiverArgumentsAndSymbol(tree)
218218
val hasConformingTargs = (typeArguments zip methTparams).forall{x => x._1.tpe <:< x._2.tpe}
219-
val recv = noTailTransform(prefix)
220219

221220
val targs = typeArguments.map(noTailTransform)
222221
val argumentss = arguments.map(noTailTransforms)
223222

224-
val recvWiden = recv.tpe.widenDealias
225-
226-
val receiverIsSame = enclosingClass.typeRef.widenDealias =:= recvWiden
227-
val receiverIsSuper = (method.name eq sym) && enclosingClass.typeRef.widen <:< recvWiden
228-
val receiverIsThis = recv.tpe =:= thisType || recv.tpe.widen =:= thisType
229-
230223
val isRecursiveCall = (method eq sym)
224+
val recvWiden = prefix.tpe.widenDealias
225+
231226

232227
def continue = {
233228
val method = noTailTransform(call)
@@ -244,40 +239,50 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete
244239
continue
245240
}
246241

247-
def rewriteTailCall(recv: Tree): Tree = {
248-
c.debuglog("Rewriting tail recursive call: " + tree.pos)
249-
rewrote = true
250-
val receiver = noTailTransform(recv)
251-
252-
val callTargs: List[tpd.Tree] =
253-
if (abstractOverClass) {
254-
val classTypeArgs = recv.tpe.baseTypeWithArgs(enclosingClass).argInfos
255-
targs ::: classTypeArgs.map(x => ref(x.typeSymbol))
256-
} else targs
257-
258-
val method = if (callTargs.nonEmpty) TypeApply(Ident(label.termRef), callTargs) else Ident(label.termRef)
259-
val thisPassed =
260-
if (this.method.owner.isClass)
261-
method.appliedTo(receiver.ensureConforms(method.tpe.widen.firstParamTypes.head))
262-
else method
263-
264-
val res =
265-
if (thisPassed.tpe.widen.isParameterless) thisPassed
266-
else argumentss.foldLeft(thisPassed) {
267-
(met, ar) => Apply(met, ar) // Dotty deviation no auto-detupling yet.
268-
}
269-
res
270-
}
242+
271243

272244
if (isRecursiveCall) {
273245
if (ctx.tailPos) {
246+
val receiverIsSame = enclosingClass.typeRef.widenDealias =:= recvWiden
247+
val receiverIsThis = prefix.tpe =:= thisType || prefix.tpe.widen =:= thisType
248+
249+
def rewriteTailCall(recv: Tree): Tree = {
250+
c.debuglog("Rewriting tail recursive call: " + tree.pos)
251+
rewrote = true
252+
val receiver = noTailTransform(recv)
253+
254+
val callTargs: List[tpd.Tree] =
255+
if (abstractOverClass) {
256+
val classTypeArgs = recv.tpe.baseTypeWithArgs(enclosingClass).argInfos
257+
targs ::: classTypeArgs.map(x => ref(x.typeSymbol))
258+
} else targs
259+
260+
val method = if (callTargs.nonEmpty) TypeApply(Ident(label.termRef), callTargs) else Ident(label.termRef)
261+
val thisPassed =
262+
if (this.method.owner.isClass)
263+
method.appliedTo(receiver.ensureConforms(method.tpe.widen.firstParamTypes.head))
264+
else method
265+
266+
val res =
267+
if (thisPassed.tpe.widen.isParameterless) thisPassed
268+
else argumentss.foldLeft(thisPassed) {
269+
(met, ar) => Apply(met, ar) // Dotty deviation no auto-detupling yet.
270+
}
271+
res
272+
}
273+
274274
if (!hasConformingTargs) fail("it changes type arguments on a polymorphic recursive call")
275-
else if (recv eq EmptyTree) rewriteTailCall(This(enclosingClass.asClass))
276-
else if (receiverIsSame || receiverIsThis) rewriteTailCall(recv)
277-
else fail("it changes type of 'this' on a polymorphic recursive call")
275+
else {
276+
val recv = noTailTransform(prefix)
277+
if (recv eq EmptyTree) rewriteTailCall(This(enclosingClass.asClass))
278+
else if (receiverIsSame || receiverIsThis) rewriteTailCall(recv)
279+
else fail("it changes type of 'this' on a polymorphic recursive call")
280+
}
278281
}
279282
else fail(defaultReason)
280283
} else {
284+
val receiverIsSuper = (method.name eq sym) && enclosingClass.typeRef.widen <:< recvWiden
285+
281286
if (receiverIsSuper) fail("it contains a recursive call targeting a supertype")
282287
else continue
283288
}

tests/pos/i1687.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
object O {
2+
def f: String = {
3+
"1" + 1 + "1" + 1 +
4+
"1" + 1 + "1" + 1 +
5+
"1" + 1 + "1" + 1 +
6+
"1" + 1 + "1" + 1 +
7+
"1" + 1 + "1" + 1 +
8+
"1" + 1 + "1" + 1
9+
}
10+
}

tests/pos/i1687b.scala

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
object O {
2+
def f: String = {
3+
"1" + 1 + "1" + 1 +
4+
"1" + 1 + "1" + 1 +
5+
"1" + 1 + "1" + 1 +
6+
"1" + 1 + "1" + 1 +
7+
"1" + 1 + "1" + 1 +
8+
"1" + 1 + "1" + 1 +
9+
"1" + 1 + "1" + 1 +
10+
"1" + 1 + "1" + 1 +
11+
"1" + 1 + "1" + 1 +
12+
"1" + 1 + "1" + 1 +
13+
"1" + 1 + "1" + 1 +
14+
"1" + 1 + "1" + 1 +
15+
"1" + 1 + "1" + 1 +
16+
"1" + 1 + "1" + 1 +
17+
"1" + 1 + "1" + 1
18+
}
19+
}

0 commit comments

Comments
 (0)