11package dotty .tools .dotc .transform
22
33import dotty .tools .dotc .ast .Trees ._
4- import dotty .tools .dotc .ast .tpd
4+ import dotty .tools .dotc .ast .{ TreeTypeMap , tpd }
55import dotty .tools .dotc .core .Contexts .Context
66import dotty .tools .dotc .core .Decorators ._
77import dotty .tools .dotc .core .DenotTransformers .DenotTransformer
@@ -10,13 +10,12 @@ import dotty.tools.dotc.core.Symbols._
1010import dotty .tools .dotc .core .Types ._
1111import dotty .tools .dotc .core ._
1212import 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))
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)
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
@@ -175,8 +191,9 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete
175191 case x => (x, x, accArgs, accT, x.symbol)
176192 }
177193
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)
180197
181198 val targs = typeArguments.map(noTailTransform)
182199 val argumentss = arguments.map(noTailTransforms)
@@ -215,20 +232,21 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete
215232 targs ::: classTypeArgs.map(x => ref(x.typeSymbol))
216233 } else targs
217234
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
220237
221238 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+ }
226243 res
227244 }
228245
229246 if (isRecursiveCall) {
230247 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))
232250 else if (receiverIsSame || receiverIsThis) rewriteTailCall(recv)
233251 else fail(" it changes type of 'this' on a polymorphic recursive call" )
234252 }
0 commit comments