Skip to content

Commit 9b3a294

Browse files
committed
Fix #1089: Special-case local methods
This is a simpler fix than the previous one. Local methods, cannot change `this` and do not need to go through FullParameterization.
1 parent 1cf8d46 commit 9b3a294

File tree

1 file changed

+32
-16
lines changed

1 file changed

+32
-16
lines changed

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

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package dotty.tools.dotc.transform
22

33
import dotty.tools.dotc.ast.Trees._
4-
import dotty.tools.dotc.ast.tpd
4+
import dotty.tools.dotc.ast.{TreeTypeMap, tpd}
55
import dotty.tools.dotc.core.Contexts.Context
66
import dotty.tools.dotc.core.Decorators._
77
import dotty.tools.dotc.core.DenotTransformers.DenotTransformer
@@ -10,13 +10,12 @@ import dotty.tools.dotc.core.Symbols._
1010
import dotty.tools.dotc.core.Types._
1111
import dotty.tools.dotc.core._
1212
import dotty.tools.dotc.transform.TailRec._
13-
import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, MiniPhaseTransform}
13+
import dotty.tools.dotc.transform.TreeTransforms.{MiniPhaseTransform, TransformerInfo}
1414

1515
/**
1616
* A Tail Rec Transformer
17-
*
1817
* @author Erik Stenman, Iulian Dragos,
19-
* ported to dotty by Dmitry Petrashko
18+
* ported and heavily modified for dotty by Dmitry Petrashko
2019
* @version 1.1
2120
*
2221
* What it does:
@@ -77,7 +76,9 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete
7776
private def mkLabel(method: Symbol, abstractOverClass: Boolean)(implicit c: Context): TermSymbol = {
7877
val name = c.freshName(labelPrefix)
7978

80-
c.newSymbol(method, name.toTermName, labelFlags, fullyParameterizedType(method.info, method.enclosingClass.asClass, abstractOverClass, liftThisType = true))
79+
if (method.owner.isClass)
80+
c.newSymbol(method, name.toTermName, labelFlags, fullyParameterizedType(method.info, method.enclosingClass.asClass, abstractOverClass, liftThisType = false))
81+
else c.newSymbol(method, name.toTermName, labelFlags, method.info)
8182
}
8283

8384
override def transformDefDef(tree: tpd.DefDef)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
@@ -103,18 +104,33 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete
103104
// and second one will actually apply,
104105
// now this speculatively transforms tree and throws away result in many cases
105106
val rhsSemiTransformed = {
106-
val transformer = new TailRecElimination(origMeth, owner, thisTpe, mandatory, label, abstractOverClass = defIsTopLevel)
107+
val transformer = new TailRecElimination(origMeth, dd.tparams, owner, thisTpe, mandatory, label, abstractOverClass = defIsTopLevel)
107108
val rhs = atGroupEnd(transformer.transform(dd.rhs)(_))
108109
rewrote = transformer.rewrote
109110
rhs
110111
}
111112

112113
if (rewrote) {
113114
val dummyDefDef = cpy.DefDef(tree)(rhs = rhsSemiTransformed)
114-
val res = fullyParameterizedDef(label, dummyDefDef, abstractOverClass = defIsTopLevel)
115-
val call = forwarder(label, dd, abstractOverClass = defIsTopLevel, liftThisType = true)
116-
Block(List(res), call)
117-
} else {
115+
if (tree.symbol.owner.isClass) {
116+
val labelDef = fullyParameterizedDef(label, dummyDefDef, abstractOverClass = defIsTopLevel)
117+
val call = forwarder(label, dd, abstractOverClass = defIsTopLevel, liftThisType = true)
118+
Block(List(labelDef), call)
119+
} else { // inner method. Tail recursion does not change `this`
120+
val labelDef = polyDefDef(label, trefs => vrefss => {
121+
val origMeth = tree.symbol
122+
val origTParams = tree.tparams.map(_.symbol)
123+
val origVParams = tree.vparamss.flatten map (_.symbol)
124+
new TreeTypeMap(
125+
typeMap = identity(_)
126+
.substDealias(origTParams, trefs)
127+
.subst(origVParams, vrefss.flatten.map(_.tpe)),
128+
oldOwners = origMeth :: Nil,
129+
newOwners = label :: Nil
130+
).transform(rhsSemiTransformed)
131+
})
132+
Block(List(labelDef), ref(label).appliedToArgss(vparamss0.map(_.map(x=> ref(x.symbol)))))
133+
}} else {
118134
if (mandatory)
119135
ctx.error("TailRec optimisation not applicable, method not tail recursive", dd.pos)
120136
dd.rhs
@@ -132,7 +148,7 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete
132148

133149
}
134150

135-
class TailRecElimination(method: Symbol, enclosingClass: Symbol, thisType: Type, isMandatory: Boolean, label: Symbol, abstractOverClass: Boolean) extends tpd.TreeMap {
151+
class TailRecElimination(method: Symbol, methTparams: List[Tree], enclosingClass: Symbol, thisType: Type, isMandatory: Boolean, label: Symbol, abstractOverClass: Boolean) extends tpd.TreeMap {
136152

137153
import dotty.tools.dotc.ast.tpd._
138154

@@ -217,13 +233,13 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete
217233
} else targs
218234

219235
val method = if (callTargs.nonEmpty) TypeApply(Ident(label.termRef), callTargs) else Ident(label.termRef)
220-
val thisPassed = method appliedTo(receiver.ensureConforms(method.tpe.widen.firstParamTypes.head))
236+
val thisPassed = if(this.method.owner.isClass) method appliedTo(receiver.ensureConforms(method.tpe.widen.firstParamTypes.head)) else method
221237

222238
val res =
223-
if (thisPassed.tpe.widen.isParameterless) thisPassed
224-
else argumentss.foldLeft(thisPassed) {
225-
(met, ar) => Apply(met, ar) // Dotty deviation no auto-detupling yet.
226-
}
239+
if (thisPassed.tpe.widen.isParameterless) thisPassed
240+
else argumentss.foldLeft(thisPassed) {
241+
(met, ar) => Apply(met, ar) // Dotty deviation no auto-detupling yet.
242+
}
227243
res
228244
}
229245

0 commit comments

Comments
 (0)