Skip to content

Commit 2ee4db6

Browse files
committed
Also hoist complex arguments out of this-calls
Also hoist complex arguments out of this-calls of secondary constructors.
1 parent e9128e4 commit 2ee4db6

File tree

1 file changed

+127
-98
lines changed

1 file changed

+127
-98
lines changed

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

Lines changed: 127 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import core.Names.TermName
1515
import core.NameKinds.SuperArgName
1616
import SymUtils._
1717

18-
/** This phase hoists complex arguments to supercalls out of the enclosing class.
18+
/** This phase hoists complex arguments of supercalls and this-calls out of the enclosing class.
1919
* Example:
2020
*
2121
* class B(y: Int) extends A({ def f(x: Int) = x * x; f(y)})
@@ -47,131 +47,160 @@ class HoistSuperArgs extends MiniPhaseTransform with IdentityDenotTransformer {
4747
// There's an interaction with by name closures in that the <cbn-arg> marker
4848
// application should not be hoisted, but be left at the point of call.
4949

50-
/** Hoist complex arguments in super call `parent` out of the class.
51-
* @return A pair consisting of the transformed super call and a list of super argument
52-
* defininitions.
50+
/** Defines methods for hoisting complex supercall arguments out of
51+
* parent super calls and constructor definitions.
52+
* Hoisted superarg methods are collected in `superArgDefs`
5353
*/
54-
def hoistSuperArgs(parent: Tree, cls: Symbol)(implicit ctx: Context): (Tree, List[DefDef]) = {
55-
lazy val constr = cls.primaryConstructor
56-
lazy val allParams = // The parameters that can be accessed in the supercall
57-
cls.info.decls.filter(d => d.is(TypeParam) || d.is(TermParamAccessor))
54+
class Hoister(cls: Symbol)(implicit ctx: Context) {
5855
val superArgDefs = new mutable.ListBuffer[DefDef]
5956

60-
/** The parameter references defined by the primary constructor info */
61-
def allParamRefs(tp: Type): List[ParamRef] = tp match {
62-
case tp: LambdaType => tp.paramRefs ++ allParamRefs(tp.resultType)
63-
case _ => Nil
64-
}
57+
/** If argument is complex, hoist it out into its own method and refer to the
58+
* method instead.
59+
* @param arg The argument that might be hoisted
60+
* @param cdef The definition of the constructor from which the call is made
61+
* @return The argument after possible hoisting
62+
*/
63+
private def hoistSuperArg(arg: Tree, cdef: DefDef): Tree = {
64+
val constr = cdef.symbol
65+
lazy val origParams = // The parameters that can be accessed in the supercall
66+
if (constr == cls.primaryConstructor)
67+
cls.info.decls.filter(d => d.is(TypeParam) || d.is(TermParamAccessor))
68+
else
69+
(cdef.tparams ::: cdef.vparamss.flatten).map(_.symbol)
6570

66-
/** Splice `restpe` in final result type position of `tp` */
67-
def replaceResult(tp: Type, restpe: Type): Type = tp match {
68-
case tp: LambdaType =>
69-
tp.derivedLambdaType(resType = replaceResult(tp.resultType, restpe))
70-
case _ => restpe
71-
}
71+
/** The parameter references defined by the constructor info */
72+
def allParamRefs(tp: Type): List[ParamRef] = tp match {
73+
case tp: LambdaType => tp.paramRefs ++ allParamRefs(tp.resultType)
74+
case _ => Nil
75+
}
76+
77+
/** Splice `restpe` in final result type position of `tp` */
78+
def replaceResult(tp: Type, restpe: Type): Type = tp match {
79+
case tp: LambdaType =>
80+
tp.derivedLambdaType(resType = replaceResult(tp.resultType, restpe))
81+
case _ => restpe
82+
}
7283

73-
/** A method representing a hoisted supercall argument */
74-
def newSuperArgMethod(argType: Type) = {
75-
val (staticFlag, methOwner) =
76-
if (cls.owner.is(Package)) (JavaStatic, cls) else (EmptyFlags, cls.owner)
77-
val argTypeWrtConstr = argType.subst(allParams, allParamRefs(constr.info))
84+
/** A method representing a hoisted supercall argument */
85+
def newSuperArgMethod(argType: Type) = {
86+
val (staticFlag, methOwner) =
87+
if (cls.owner.is(Package)) (JavaStatic, cls) else (EmptyFlags, cls.owner)
88+
val argTypeWrtConstr = argType.subst(origParams, allParamRefs(constr.info))
7889
// argType with references to paramRefs of the primary constructor instead of
7990
// local parameter accessors
80-
val meth = ctx.newSymbol(
91+
val meth = ctx.newSymbol(
8192
owner = methOwner,
8293
name = SuperArgName.fresh(cls.name.toTermName),
8394
flags = Synthetic | Private | Method | staticFlag,
8495
info = replaceResult(constr.info, argTypeWrtConstr),
8596
coord = constr.coord)
86-
if (methOwner.isClass) meth.enteredAfter(thisTransform) else meth
87-
}
97+
if (methOwner.isClass) meth.enteredAfter(thisTransform) else meth
98+
}
8899

89-
def refNeedsHoist(tp: Type): Boolean = tp match {
90-
case tp: ThisType => !tp.cls.isStaticOwner && tp.cls != cls
91-
case tp: TermRef => refNeedsHoist(tp.prefix)
92-
case _ => false
93-
}
100+
/** Type of a reference implies that it needs to be hoisted */
101+
def refNeedsHoist(tp: Type): Boolean = tp match {
102+
case tp: ThisType => !tp.cls.isStaticOwner && tp.cls != cls
103+
case tp: TermRef => refNeedsHoist(tp.prefix)
104+
case _ => false
105+
}
94106

95-
/** Super call argument is complex, needs to be hoisted */
96-
def needsHoist(tree: Tree) = tree match {
97-
case _: DefDef => true
98-
case _: Template => true
99-
case _: New => !tree.tpe.typeSymbol.isStatic
100-
case _: RefTree | _: This => refNeedsHoist(tree.tpe)
101-
case _ => false
107+
/** Super call argument is complex, needs to be hoisted */
108+
def needsHoist(tree: Tree) = tree match {
109+
case _: DefDef => true
110+
case _: Template => true
111+
case _: New => !tree.tpe.typeSymbol.isStatic
112+
case _: RefTree | _: This => refNeedsHoist(tree.tpe)
113+
case _ => false
114+
}
115+
116+
// begin hoistSuperArg
117+
arg match {
118+
case Apply(fn, arg1 :: Nil) if fn.symbol == defn.cbnArg =>
119+
cpy.Apply(arg)(fn, hoistSuperArg(arg1, cdef) :: Nil)
120+
case _ if (arg.existsSubTree(needsHoist)) =>
121+
val superMeth = newSuperArgMethod(arg.tpe)
122+
val superArgDef = polyDefDef(superMeth, trefs => vrefss => {
123+
val paramSyms = trefs.map(_.typeSymbol) ::: vrefss.flatten.map(_.symbol)
124+
val tmap = new TreeTypeMap(
125+
typeMap = new TypeMap {
126+
def apply(tp: Type) = tp match {
127+
case tp: NamedType
128+
if (tp.symbol.owner == cls || tp.symbol.owner == constr) &&
129+
tp.symbol.is(ParamOrAccessor) =>
130+
val mappedSym = origParams.zip(paramSyms).toMap.apply(tp.symbol)
131+
if (tp.symbol.isType) mappedSym.typeRef else mappedSym.termRef
132+
case _ =>
133+
mapOver(tp)
134+
}
135+
},
136+
treeMap = {
137+
case tree: RefTree if paramSyms.contains(tree.symbol) =>
138+
cpy.Ident(tree)(tree.name).withType(tree.tpe)
139+
case tree =>
140+
tree
141+
})
142+
tmap(arg).changeOwnerAfter(constr, superMeth, thisTransform)
143+
})
144+
superArgDefs += superArgDef
145+
def termParamRefs(tp: Type, params: List[Symbol]): List[List[Tree]] = tp match {
146+
case tp: PolyType =>
147+
termParamRefs(tp.resultType, params)
148+
case tp: MethodType =>
149+
val (thisParams, otherParams) = params.splitAt(tp.paramNames.length)
150+
thisParams.map(ref) :: termParamRefs(tp.resultType, otherParams)
151+
case _ =>
152+
Nil
153+
}
154+
val (typeParams, termParams) = origParams.span(_.isType)
155+
val res = ref(superMeth)
156+
.appliedToTypes(typeParams.map(_.typeRef))
157+
.appliedToArgss(termParamRefs(constr.info, termParams))
158+
ctx.log(i"hoist $arg, cls = $cls = $res")
159+
res
160+
case _ => arg
161+
}
102162
}
103163

104-
/** If argument is complex, hoist it out into its own method and refer to the
105-
* method instead.
106-
* @return The argument after possible hoisting
107-
* Might append a method definition to `superArgs` as a side effect.
108-
*/
109-
def hoistSuperArg(arg: Tree): Tree = arg match {
110-
case Apply(fn, arg1 :: Nil) if fn.symbol == defn.cbnArg =>
111-
cpy.Apply(arg)(fn, hoistSuperArg(arg1) :: Nil)
112-
case _ if (arg.existsSubTree(needsHoist)) =>
113-
val superMeth = newSuperArgMethod(arg.tpe)
114-
val superArgDef = polyDefDef(superMeth, trefs => vrefss => {
115-
val paramSyms = trefs.map(_.typeSymbol) ::: vrefss.flatten.map(_.symbol)
116-
val tmap = new TreeTypeMap(
117-
typeMap = new TypeMap {
118-
def apply(tp: Type) = tp match {
119-
case tp: NamedType if tp.symbol.owner == cls && tp.symbol.is(ParamOrAccessor) =>
120-
val mappedSym = allParams.zip(paramSyms).toMap.apply(tp.symbol)
121-
if (tp.symbol.isType) mappedSym.typeRef else mappedSym.termRef
122-
case _ =>
123-
mapOver(tp)
124-
}
125-
},
126-
treeMap = {
127-
case tree: RefTree if paramSyms.contains(tree.symbol) =>
128-
cpy.Ident(tree)(tree.name).withType(tree.tpe)
129-
case tree =>
130-
tree
131-
}
132-
)
133-
tmap(arg).changeOwnerAfter(constr, superMeth, thisTransform)
134-
})
135-
superArgDefs += superArgDef
136-
def termParamRefs(tp: Type): List[List[Tree]] = tp match {
137-
case tp: PolyType =>
138-
termParamRefs(tp.resultType)
139-
case tp: MethodType =>
140-
def paramRef(name: TermName) =
141-
ref(cls.info.decl(name).suchThat(_.is(ParamAccessor)).symbol)
142-
tp.paramNames.map(paramRef) :: termParamRefs(tp.resultType)
143-
case _ =>
144-
Nil
145-
}
146-
val res = ref(superMeth)
147-
.appliedToTypes(cls.typeParams.map(_.typeRef))
148-
.appliedToArgss(termParamRefs(constr.info))
149-
ctx.log(i"hoist $arg, cls = $cls = $res")
150-
res
151-
case _ => arg
164+
/** Hoist complex arguments in super call out of the class. */
165+
def hoistSuperArgsFromCall(superCall: Tree, cdef: DefDef): Tree = superCall match {
166+
case Apply(fn, args) =>
167+
cpy.Apply(superCall)(hoistSuperArgsFromCall(fn, cdef), args.mapconserve(hoistSuperArg(_, cdef)))
168+
case _ =>
169+
superCall
152170
}
153171

154-
def recur(tree: Tree): Tree = tree match {
155-
case Apply(fn, args) => cpy.Apply(tree)(recur(fn), args.mapconserve(hoistSuperArg))
156-
case _ => tree
172+
/** Hoist complex arguments in this-constructor call of secondary constructor out of the class. */
173+
def hoistSuperArgsFromConstr(stat: Tree): Tree = stat match {
174+
case stat: DefDef if stat.symbol.isClassConstructor =>
175+
cpy.DefDef(stat)(rhs =
176+
stat.rhs match {
177+
case Block(superCall :: stats, expr) =>
178+
val superCall1 = hoistSuperArgsFromCall(superCall, stat)
179+
if (superCall1 eq superCall) stat.rhs
180+
else cpy.Block(stat.rhs)(superCall1 :: stats, expr)
181+
case _ =>
182+
hoistSuperArgsFromCall(stat.rhs, stat)
183+
})
184+
case _ =>
185+
stat
157186
}
158-
(recur(parent), superArgDefs.toList)
159187
}
160188

161189
override def transformTypeDef(tdef: TypeDef)(implicit ctx: Context, info: TransformerInfo): Tree =
162190
tdef.rhs match {
163-
case impl @ Template(_, superCall :: others, _, _) =>
164-
val cls = tdef.symbol
165-
val (hoisted, superArgDefs) = hoistSuperArgs(superCall, cls)
166-
if (superArgDefs.isEmpty) tdef
191+
case impl @ Template(cdef, superCall :: others, _, _) =>
192+
val hoist = new Hoister(tdef.symbol)
193+
val hoistedSuperCall = hoist.hoistSuperArgsFromCall(superCall, cdef)
194+
val hoistedBody = impl.body.mapconserve(hoist.hoistSuperArgsFromConstr)
195+
if (hoist.superArgDefs.isEmpty) tdef
167196
else {
168197
val (staticSuperArgDefs, enclSuperArgDefs) =
169-
superArgDefs.partition(_.symbol.is(JavaStatic))
198+
hoist.superArgDefs.toList.partition(_.symbol.is(JavaStatic))
170199
flatTree(
171200
cpy.TypeDef(tdef)(
172201
rhs = cpy.Template(impl)(
173-
parents = hoisted :: others,
174-
body = impl.body ++ staticSuperArgDefs)) ::
202+
parents = hoistedSuperCall :: others,
203+
body = hoistedBody ++ staticSuperArgDefs)) ::
175204
enclSuperArgDefs)
176205
}
177206
case _ =>

0 commit comments

Comments
 (0)