1
1
package dotty .tools .dotc .transform
2
2
3
3
import dotty .tools .dotc .ast .Trees ._
4
- import dotty .tools .dotc .ast .tpd
4
+ import dotty .tools .dotc .ast .{ TreeTypeMap , tpd }
5
5
import dotty .tools .dotc .core .Contexts .Context
6
6
import dotty .tools .dotc .core .Decorators ._
7
7
import dotty .tools .dotc .core .DenotTransformers .DenotTransformer
@@ -10,13 +10,12 @@ import dotty.tools.dotc.core.Symbols._
10
10
import dotty .tools .dotc .core .Types ._
11
11
import dotty .tools .dotc .core ._
12
12
import dotty .tools .dotc .transform .TailRec ._
13
- import dotty .tools .dotc .transform .TreeTransforms .{TransformerInfo , MiniPhaseTransform }
13
+ import dotty .tools .dotc .transform .TreeTransforms .{MiniPhaseTransform , TransformerInfo }
14
14
15
15
/**
16
16
* A Tail Rec Transformer
17
- *
18
17
* @author Erik Stenman, Iulian Dragos,
19
- * ported to dotty by Dmitry Petrashko
18
+ * ported and heavily modified for dotty by Dmitry Petrashko
20
19
* @version 1.1
21
20
*
22
21
* What it does:
@@ -77,7 +76,9 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete
77
76
private def mkLabel (method : Symbol , abstractOverClass : Boolean )(implicit c : Context ): TermSymbol = {
78
77
val name = c.freshName(labelPrefix)
79
78
80
- c.newSymbol(method, name.toTermName, labelFlags, fullyParameterizedType(method.info, method.enclosingClass.asClass, abstractOverClass))
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)
81
82
}
82
83
83
84
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
103
104
// and second one will actually apply,
104
105
// now this speculatively transforms tree and throws away result in many cases
105
106
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)
107
108
val rhs = atGroupEnd(transformer.transform(dd.rhs)(_))
108
109
rewrote = transformer.rewrote
109
110
rhs
110
111
}
111
112
112
113
if (rewrote) {
113
114
val dummyDefDef = cpy.DefDef (tree)(rhs = rhsSemiTransformed)
114
- val res = fullyParameterizedDef(label, dummyDefDef, abstractOverClass = defIsTopLevel)
115
- val call = forwarder(label, dd, abstractOverClass = defIsTopLevel)
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 {
118
134
if (mandatory)
119
135
ctx.error(" TailRec optimisation not applicable, method not tail recursive" , dd.pos)
120
136
dd.rhs
@@ -132,7 +148,7 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete
132
148
133
149
}
134
150
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 {
136
152
137
153
import dotty .tools .dotc .ast .tpd ._
138
154
@@ -175,8 +191,9 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete
175
191
case x => (x, x, accArgs, accT, x.symbol)
176
192
}
177
193
178
- val (reciever, call, arguments, typeArguments, symbol) = receiverArgumentsAndSymbol(tree)
179
- val recv = noTailTransform(reciever)
194
+ val (prefix, call, arguments, typeArguments, symbol) = receiverArgumentsAndSymbol(tree)
195
+ val hasConformingTargs = (typeArguments zip methTparams).forall{x => x._1.tpe <:< x._2.tpe}
196
+ val recv = noTailTransform(prefix)
180
197
181
198
val targs = typeArguments.map(noTailTransform)
182
199
val argumentss = arguments.map(noTailTransforms)
@@ -215,20 +232,21 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete
215
232
targs ::: classTypeArgs.map(x => ref(x.typeSymbol))
216
233
} else targs
217
234
218
- val method = Apply ( if (callTargs.nonEmpty) TypeApply (Ident (label.termRef), callTargs) else Ident (label.termRef),
219
- List ( receiver))
235
+ val method = if (callTargs.nonEmpty) TypeApply (Ident (label.termRef), callTargs) else Ident (label.termRef)
236
+ val thisPassed = if ( this .method.owner.isClass) method appliedTo( receiver.ensureConforms(method.tpe.widen.firstParamTypes.head)) else method
220
237
221
238
val res =
222
- if (method .tpe.widen.isParameterless) method
223
- else argumentss.foldLeft(method ) {
224
- (met, ar) => Apply (met, ar) // Dotty deviation no auto-detupling yet.
225
- }
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
+ }
226
243
res
227
244
}
228
245
229
246
if (isRecursiveCall) {
230
247
if (ctx.tailPos) {
231
- if (recv eq EmptyTree ) rewriteTailCall(This (enclosingClass.asClass))
248
+ if (! hasConformingTargs) fail(" it changes type arguments on a polymorphic recursive call" )
249
+ else if (recv eq EmptyTree ) rewriteTailCall(This (enclosingClass.asClass))
232
250
else if (receiverIsSame || receiverIsThis) rewriteTailCall(recv)
233
251
else fail(" it changes type of 'this' on a polymorphic recursive call" )
234
252
}
0 commit comments