@@ -15,7 +15,7 @@ import core.Names.TermName
15
15
import core .NameKinds .SuperArgName
16
16
import SymUtils ._
17
17
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.
19
19
* Example:
20
20
*
21
21
* class B(y: Int) extends A({ def f(x: Int) = x * x; f(y)})
@@ -47,131 +47,160 @@ class HoistSuperArgs extends MiniPhaseTransform with IdentityDenotTransformer {
47
47
// There's an interaction with by name closures in that the <cbn-arg> marker
48
48
// application should not be hoisted, but be left at the point of call.
49
49
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`
53
53
*/
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 ) {
58
55
val superArgDefs = new mutable.ListBuffer [DefDef ]
59
56
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)
65
70
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
+ }
72
83
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))
78
89
// argType with references to paramRefs of the primary constructor instead of
79
90
// local parameter accessors
80
- val meth = ctx.newSymbol(
91
+ val meth = ctx.newSymbol(
81
92
owner = methOwner,
82
93
name = SuperArgName .fresh(cls.name.toTermName),
83
94
flags = Synthetic | Private | Method | staticFlag,
84
95
info = replaceResult(constr.info, argTypeWrtConstr),
85
96
coord = constr.coord)
86
- if (methOwner.isClass) meth.enteredAfter(thisTransform) else meth
87
- }
97
+ if (methOwner.isClass) meth.enteredAfter(thisTransform) else meth
98
+ }
88
99
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
+ }
94
106
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
+ }
102
162
}
103
163
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
152
170
}
153
171
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
157
186
}
158
- (recur(parent), superArgDefs.toList)
159
187
}
160
188
161
189
override def transformTypeDef (tdef : TypeDef )(implicit ctx : Context , info : TransformerInfo ): Tree =
162
190
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
167
196
else {
168
197
val (staticSuperArgDefs, enclSuperArgDefs) =
169
- superArgDefs.partition(_.symbol.is(JavaStatic ))
198
+ hoist. superArgDefs.toList .partition(_.symbol.is(JavaStatic ))
170
199
flatTree(
171
200
cpy.TypeDef (tdef)(
172
201
rhs = cpy.Template (impl)(
173
- parents = hoisted :: others,
174
- body = impl.body ++ staticSuperArgDefs)) ::
202
+ parents = hoistedSuperCall :: others,
203
+ body = hoistedBody ++ staticSuperArgDefs)) ::
175
204
enclSuperArgDefs)
176
205
}
177
206
case _ =>
0 commit comments