@@ -33,6 +33,58 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor
33
33
override def runsAfter : Set [Class [_ <: Phase ]] = Set (classOf [Memoize ])
34
34
35
35
36
+ // Collect all private parameter accessors and value definitions that need
37
+ // to be retained. There are several reasons why a parameter accessor or
38
+ // definition might need to be retained:
39
+ // 1. It is accessed after the constructor has finished
40
+ // 2. It is accessed before it is defined
41
+ // 3. It is accessed on an object other than `this`
42
+ // 4. It is a mutable parameter accessor
43
+ // 5. It is has a wildcard initializer `_`
44
+ private val retainedPrivateVals = mutable.Set [Symbol ]()
45
+ private val seenPrivateVals = mutable.Set [Symbol ]()
46
+
47
+ private def markUsedPrivateSymbols (tree : RefTree )(implicit ctx : Context ): Unit = {
48
+
49
+ val sym = tree.symbol
50
+ def retain () =
51
+ retainedPrivateVals.add(sym)
52
+
53
+ if (sym.exists && sym.owner.isClass && mightBeDropped(sym)) {
54
+ val owner = sym.owner.asClass
55
+
56
+ tree match {
57
+ case Ident (_) | Select (This (_), _) =>
58
+ def inConstructor = {
59
+ val method = ctx.owner.enclosingMethod
60
+ method.isPrimaryConstructor && ctx.owner.enclosingClass == owner
61
+ }
62
+ if (inConstructor && (sym.is(ParamAccessor ) || seenPrivateVals.contains(sym))) {
63
+ // used inside constructor, accessed on this,
64
+ // could use constructor argument instead, no need to retain field
65
+ }
66
+ else retain()
67
+ case _ => retain()
68
+ }
69
+ }
70
+ }
71
+
72
+ override def transformIdent (tree : tpd.Ident )(implicit ctx : Context , info : TransformerInfo ): tpd.Tree = {
73
+ markUsedPrivateSymbols(tree)
74
+ tree
75
+ }
76
+
77
+ override def transformSelect (tree : tpd.Select )(implicit ctx : Context , info : TransformerInfo ): tpd.Tree = {
78
+ markUsedPrivateSymbols(tree)
79
+ tree
80
+ }
81
+
82
+ override def transformValDef (tree : tpd.ValDef )(implicit ctx : Context , info : TransformerInfo ): tpd.Tree = {
83
+ if (mightBeDropped(tree.symbol))
84
+ (if (isWildcardStarArg(tree.rhs)) retainedPrivateVals else seenPrivateVals) += tree.symbol
85
+ tree
86
+ }
87
+
36
88
/** All initializers for non-lazy fields should be moved into constructor.
37
89
* All non-abstract methods should be implemented (this is assured for constructors
38
90
* in this phase and for other methods in memoize).
@@ -75,6 +127,7 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor
75
127
76
128
override def transformTemplate (tree : Template )(implicit ctx : Context , info : TransformerInfo ): Tree = {
77
129
val cls = ctx.owner.asClass
130
+
78
131
val constr @ DefDef (nme.CONSTRUCTOR , Nil , vparams :: Nil , _, EmptyTree ) = tree.constr
79
132
80
133
// Produce aligned accessors and constructor parameters. We have to adjust
@@ -113,46 +166,9 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor
113
166
}
114
167
}
115
168
116
- // Collect all private parameter accessors and value definitions that need
117
- // to be retained. There are several reasons why a parameter accessor or
118
- // definition might need to be retained:
119
- // 1. It is accessed after the constructor has finished
120
- // 2. It is accessed before it is defined
121
- // 3. It is accessed on an object other than `this`
122
- // 4. It is a mutable parameter accessor
123
- // 5. It is has a wildcard initializer `_`
124
- object usage extends TreeTraverser {
125
- private var inConstr : Boolean = true
126
- private val seen = mutable.Set [Symbol ](accessors : _* )
127
- val retained = mutable.Set [Symbol ]()
128
- def dropped : collection.Set [Symbol ] = seen -- retained
129
- override def traverse (tree : Tree )(implicit ctx : Context ) = {
130
- val sym = tree.symbol
131
- tree match {
132
- case Ident (_) | Select (This (_), _) if inConstr && seen(tree.symbol) =>
133
- // could refer to definition in constructors, so no retention necessary
134
- case tree : RefTree =>
135
- if (mightBeDropped(sym)) retained += sym
136
- case _ =>
137
- }
138
- if (! noDirectRefsFrom(tree)) traverseChildren(tree)
139
- }
140
- def collect (stats : List [Tree ]): Unit = stats foreach {
141
- case stat : ValDef if ! stat.symbol.is(Lazy ) =>
142
- traverse(stat)
143
- if (mightBeDropped(stat.symbol))
144
- (if (isWildcardStarArg(stat.rhs)) retained else seen) += stat.symbol
145
- case stat : DefTree =>
146
- inConstr = false
147
- traverse(stat)
148
- inConstr = true
149
- case stat =>
150
- traverse(stat)
151
- }
169
+ def isRetained (acc : Symbol ) = {
170
+ ! mightBeDropped(acc) || retainedPrivateVals(acc)
152
171
}
153
- usage.collect(tree.body)
154
-
155
- def isRetained (acc : Symbol ) = ! mightBeDropped(acc) || usage.retained(acc)
156
172
157
173
val constrStats, clsStats = new mutable.ListBuffer [Tree ]
158
174
@@ -170,6 +186,8 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor
170
186
}
171
187
}
172
188
189
+ val dropped = mutable.Set [Symbol ]()
190
+
173
191
// Split class body into statements that go into constructor and
174
192
// definitions that are kept as members of the class.
175
193
def splitStats (stats : List [Tree ]): Unit = stats match {
@@ -183,6 +201,7 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor
183
201
clsStats += cpy.ValDef (stat)(rhs = EmptyTree )
184
202
}
185
203
else if (! stat.rhs.isEmpty) {
204
+ dropped += sym
186
205
sym.copySymDenotation(
187
206
initFlags = sym.flags &~ Private ,
188
207
owner = constr.symbol).installAfter(thisTransform)
@@ -203,8 +222,10 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor
203
222
204
223
// The initializers for the retained accessors */
205
224
val copyParams = accessors flatMap { acc =>
206
- if (! isRetained(acc)) Nil
207
- else {
225
+ if (! isRetained(acc)) {
226
+ dropped += acc
227
+ Nil
228
+ } else {
208
229
val target = if (acc.is(Method )) acc.field else acc
209
230
if (! target.exists) Nil // this case arises when the parameter accessor is an alias
210
231
else {
@@ -224,12 +245,13 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor
224
245
}
225
246
226
247
// Drop accessors that are not retained from class scope
227
- val dropped = usage.dropped
228
248
if (dropped.nonEmpty) {
229
249
val clsInfo = cls.classInfo // TODO investigate: expand clsInfo to cls.info => dotty type error
230
250
cls.copy(
231
251
info = clsInfo.derivedClassInfo(
232
252
decls = clsInfo.decls.filteredScope(! dropped.contains(_))))
253
+
254
+ // TODO: this happens to work only because Constructors is the last phase in group
233
255
}
234
256
235
257
val (superCalls, followConstrStats) = constrStats.toList match {
0 commit comments