@@ -21,6 +21,8 @@ import collection.mutable
21
21
/** This transform
22
22
* - moves initializers from body to constructor.
23
23
* - makes all supercalls explicit
24
+ * - also moves private fields that are accessed only from constructor
25
+ * into the constructor if possible.
24
26
*/
25
27
class Constructors extends MiniPhaseTransform with SymTransformer { thisTransform =>
26
28
import tpd ._
@@ -35,7 +37,7 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor
35
37
*/
36
38
override def transformSym (sym : SymDenotation )(implicit ctx : Context ): SymDenotation = {
37
39
def ownerBecomesConstructor (owner : Symbol ): Boolean =
38
- (owner.isLocalDummy || owner.isTerm && ! owner.is(Method )) &&
40
+ (owner.isLocalDummy || owner.isTerm && ! owner.is(Method | Lazy )) &&
39
41
owner.owner.isClass
40
42
if (ownerBecomesConstructor(sym.owner))
41
43
sym.copySymDenotation(owner = sym.owner.enclosingClass.primaryConstructor)
@@ -48,38 +50,14 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor
48
50
private def noDirectRefsFrom (tree : Tree )(implicit ctx : Context ) =
49
51
tree.isDef && tree.symbol.isClass && ! tree.symbol.is(InSuperCall )
50
52
51
- /** Adjustments performed when moving code into the constructor:
52
- * (1) Replace references to param accessors by constructor parameters
53
- * except possibly references to mutable variables, if `excluded = Mutable`.
54
- * (Mutable parameters should be replaced only during the super call)
55
- * (2) If the parameter accessor reference was to an alias getter,
56
- * drop the () when replacing by the parameter.
53
+ /** Class members that can be eliminated if referenced only from their own
54
+ * constructor.
57
55
*/
58
- class IntoConstrMap (accessors : List [Symbol ], params : List [Symbol ]) extends TreeMap {
59
- private var excluded : FlagSet = _
60
- override def transform (tree : Tree )(implicit ctx : Context ): Tree = tree match {
61
- case Ident (_) | Select (This (_), _) =>
62
- val sym = tree.symbol
63
- if (sym is (ParamAccessor , butNot = excluded)) {
64
- val param = sym.subst(accessors, params)
65
- if (param ne sym) ref(param).withPos(tree.pos)
66
- else tree
67
- }
68
- else tree
69
- case Apply (fn, Nil ) =>
70
- val fn1 = transform(fn)
71
- if ((fn1 ne fn) && fn1.symbol.is(Param ) && fn1.symbol.owner.isPrimaryConstructor)
72
- fn1 // in this case, fn1.symbol was an alias for a parameter in a superclass
73
- else cpy.Apply (tree)(fn1, Nil )
74
- case _ =>
75
- if (noDirectRefsFrom(tree)) tree else super .transform(tree)
76
- }
56
+ private def mightBeDropped (sym : Symbol )(implicit ctx : Context ) =
57
+ sym.is(Private , butNot = KeeperFlags ) && ! sym.is(MutableParamAccessor )
77
58
78
- def apply (tree : Tree , excluded : FlagSet )(implicit ctx : Context ): Tree = {
79
- this .excluded = excluded
80
- transform(tree)
81
- }
82
- }
59
+ private final val KeeperFlags = Method | Lazy | NotJavaPrivate
60
+ private final val MutableParamAccessor = allOf(Mutable , ParamAccessor )
83
61
84
62
override def transformTemplate (tree : Template )(implicit ctx : Context , info : TransformerInfo ): Tree = {
85
63
val cls = ctx.owner.asClass
@@ -102,7 +80,34 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor
102
80
}
103
81
val paramSyms = vparamsWithOuter map (_.symbol)
104
82
105
- val intoConstr = new IntoConstrMap (accessors, paramSyms)
83
+ // Adjustments performed when moving code into the constructor:
84
+ // (1) Replace references to param accessors by constructor parameters
85
+ // except possibly references to mutable variables, if `excluded = Mutable`.
86
+ // (Mutable parameters should be replaced only during the super call)
87
+ // (2) If the parameter accessor reference was to an alias getter,
88
+ // drop the () when replacing by the parameter.
89
+ object intoConstr extends TreeMap {
90
+ private var excluded : FlagSet = _
91
+ override def transform (tree : Tree )(implicit ctx : Context ): Tree = tree match {
92
+ case Ident (_) | Select (This (_), _) =>
93
+ var sym = tree.symbol
94
+ if (sym is (ParamAccessor , butNot = excluded)) sym = sym.subst(accessors, paramSyms)
95
+ if (sym.owner.isConstructor) ref(sym).withPos(tree.pos) else tree
96
+ case Apply (fn, Nil ) =>
97
+ val fn1 = transform(fn)
98
+ if ((fn1 ne fn) && fn1.symbol.is(Param ) && fn1.symbol.owner.isPrimaryConstructor)
99
+ fn1 // in this case, fn1.symbol was an alias for a parameter in a superclass
100
+ else cpy.Apply (tree)(fn1, Nil )
101
+ case _ =>
102
+ if (noDirectRefsFrom(tree)) tree else super .transform(tree)
103
+ }
104
+
105
+ def apply (tree : Tree , inSuperCall : Boolean = false )(implicit ctx : Context ): Tree = {
106
+ this .excluded = if (inSuperCall) EmptyFlags else Mutable
107
+ transform(tree)
108
+ }
109
+ }
110
+
106
111
val superCalls = new mutable.ListBuffer [Tree ]
107
112
108
113
// If parent is a constructor call, pull out the call into a separate
@@ -115,12 +120,12 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor
115
120
nme.CONSTRUCTOR ),
116
121
superArgs) =>
117
122
val toClass = ! superType.symbol.is(Trait )
118
- val mappedArgs = superArgs.map(
119
- intoConstr(_, excluded = if (toClass) Mutable else EmptyFlags ))
123
+ val mappedArgs = superArgs.map(intoConstr(_, inSuperCall = toClass))
120
124
val receiver =
121
125
if (toClass) Super (This (cls), tpnme.EMPTY , inConstrCall = true )
122
126
else This (cls)
123
- superCalls += cpy.Apply (superApp)(
127
+ superCalls +=
128
+ cpy.Apply (superApp)(
124
129
receiver.withPos(superNew.pos)
125
130
.select(superSel.symbol).withPos(superSel.pos),
126
131
mappedArgs)
@@ -129,67 +134,97 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor
129
134
}
130
135
val parentTypeTrees = tree.parents.map(normalizeParent)
131
136
137
+ // Collect all private parameter accessors and value definitions that need
138
+ // to be retained. There are several reasons why a parameter accessor or
139
+ // definition might need to be retained:
140
+ // 1. It is accessed after the constructor has finished
141
+ // 2. It is accessed before it is defined
142
+ // 3. It is accessed on an object other than `this`
143
+ // 4. It is a mutable parameter accessor
144
+ // 5. It is has a wildcard initializer `_`
145
+ object usage extends TreeTraverser {
146
+ private var inConstr : Boolean = true
147
+ private val seen = mutable.Set [Symbol ](accessors : _* )
148
+ val retained = mutable.Set [Symbol ]()
149
+ def dropped : collection.Set [Symbol ] = seen -- retained
150
+ override def traverse (tree : Tree ) = {
151
+ val sym = tree.symbol
152
+ tree match {
153
+ case Ident (_) | Select (This (_), _) if inConstr && seen(tree.symbol) =>
154
+ // could refer to definition in constructors, so no retention necessary
155
+ case tree : RefTree =>
156
+ if (mightBeDropped(sym)) retained += sym
157
+ case _ =>
158
+ }
159
+ if (! noDirectRefsFrom(tree)) traverseChildren(tree)
160
+ }
161
+ def collect (stats : List [Tree ]): Unit = stats foreach {
162
+ case stat : ValDef if ! stat.symbol.is(Lazy ) =>
163
+ traverse(stat)
164
+ if (mightBeDropped(stat.symbol))
165
+ (if (isWildcardStarArg(stat.rhs)) retained else seen) += stat.symbol
166
+ case stat : DefTree =>
167
+ inConstr = false
168
+ traverse(stat)
169
+ inConstr = true
170
+ case stat =>
171
+ traverse(stat)
172
+ }
173
+ }
174
+ usage.collect(superCalls.toList ++ tree.body)
175
+
176
+ def isRetained (acc : Symbol ) = ! mightBeDropped(acc) || usage.retained(acc)
177
+
178
+ val constrStats, clsStats = new mutable.ListBuffer [Tree ]
179
+
132
180
// Split class body into statements that go into constructor and
133
181
// definitions that are kept as members of the class.
134
- def splitStats (stats : List [Tree ]): ( List [ Tree ], List [ Tree ]) = stats match {
182
+ def splitStats (stats : List [Tree ]): Unit = stats match {
135
183
case stat :: stats1 =>
136
- val (constrStats, clsStats) = splitStats(stats1)
137
184
stat match {
138
- case stat @ ValDef (mods, name, tpt, rhs) =>
139
- val inits =
140
- if (rhs.isEmpty || isWildcardArg(rhs)) Nil
141
- else Assign (ref(stat.symbol), intoConstr(rhs, excluded = Mutable ))
142
- .withPos(stat.pos) :: Nil
143
- (inits ::: constrStats, cpy.ValDef (stat)(rhs = EmptyTree ) :: clsStats)
185
+ case stat @ ValDef (mods, name, tpt, rhs) if ! stat.symbol.is(Lazy ) =>
186
+ val sym = stat.symbol
187
+ if (isRetained(sym)) {
188
+ if (! rhs.isEmpty && ! isWildcardArg(rhs))
189
+ constrStats += Assign (ref(sym), intoConstr(rhs)).withPos(stat.pos)
190
+ clsStats += cpy.ValDef (stat)(rhs = EmptyTree )
191
+ }
192
+ else if (! rhs.isEmpty) {
193
+ sym.copySymDenotation(
194
+ initFlags = sym.flags &~ Private ,
195
+ owner = constr.symbol).installAfter(thisTransform)
196
+ constrStats += intoConstr(stat)
197
+ }
144
198
case _ : DefTree =>
145
- (constrStats, stat :: clsStats)
199
+ clsStats += stat
146
200
case _ =>
147
- (intoConstr(stat, excluded = Mutable ) :: constrStats, clsStats )
201
+ constrStats += intoConstr(stat )
148
202
}
203
+ splitStats(stats1)
149
204
case Nil =>
150
205
(Nil , Nil )
151
206
}
152
- val (constrStats, clsStats) = splitStats(tree.body)
153
-
154
- // Collect all private parameter accessors that need to be retained
155
- // because they are accessed after the constructor has finished.
156
- val collectRetained = new TreeAccumulator [Set [Symbol ]] {
157
- override def apply (retained : Set [Symbol ], tree : Tree ) = tree match {
158
- case tree : RefTree =>
159
- val sym = tree.symbol
160
- foldOver(
161
- if (sym.is(PrivateParamAccessor ) && sym.owner == cls) retained + sym else retained,
162
- tree)
163
- case _ =>
164
- if (noDirectRefsFrom(tree)) retained else foldOver(retained, tree)
165
- }
166
- }
167
- val retainedPrivate = collectRetained(collectRetained(Set [Symbol ](), constrStats), clsStats)
168
- def isRetained (acc : Symbol ) =
169
- (! acc.is(Private ) || acc.is(NotJavaPrivate ) || retainedPrivate(acc))
207
+ splitStats(tree.body)
170
208
171
209
val accessorFields = accessors.filterNot(_ is Method )
172
- val (retainedAccessors, droppedAccessors) = accessorFields.partition(isRetained)
173
210
174
211
// The initializers for the retained accessors */
175
- val copyParams = retainedAccessors .map(acc =>
212
+ val copyParams = accessorFields.filter(isRetained) .map(acc =>
176
213
Assign (ref(acc), ref(acc.subst(accessors, paramSyms))).withPos(tree.pos))
177
214
178
215
// Drop accessors that are not retained from class scope
179
- if (droppedAccessors.nonEmpty) {
216
+ val dropped = usage.dropped
217
+ if (dropped.nonEmpty) {
180
218
val clsInfo = cls.classInfo // TODO investigate: expand clsInfo to cls.info => dotty type error
181
219
cls.copy(
182
220
info = clsInfo.derivedClassInfo(
183
- decls = clsInfo.decls.filteredScope(! droppedAccessors .contains(_))))
221
+ decls = clsInfo.decls.filteredScope(! dropped .contains(_))))
184
222
}
185
223
186
224
cpy.Template (tree)(
187
225
constr = cpy.DefDef (constr)(
188
- rhs = Block (superCalls.toList ::: copyParams ::: constrStats, unitLiteral)),
226
+ rhs = Block (superCalls.toList ::: copyParams ::: constrStats.toList , unitLiteral)),
189
227
parents = parentTypeTrees,
190
- body = clsStats filter {
191
- case vdef : ValDef => ! droppedAccessors.contains(vdef.symbol)
192
- case _ => true
193
- })
228
+ body = clsStats.toList)
194
229
}
195
230
}
0 commit comments