@@ -21,6 +21,8 @@ import collection.mutable
2121/** This transform
2222 * - moves initializers from body to constructor.
2323 * - makes all supercalls explicit
24+ * - also moves private fields that are accessed only from constructor
25+ * into the constructor if possible.
2426 */
2527class Constructors extends MiniPhaseTransform with SymTransformer { thisTransform =>
2628 import tpd ._
@@ -35,7 +37,7 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor
3537 */
3638 override def transformSym (sym : SymDenotation )(implicit ctx : Context ): SymDenotation = {
3739 def ownerBecomesConstructor (owner : Symbol ): Boolean =
38- (owner.isLocalDummy || owner.isTerm && ! owner.is(Method )) &&
40+ (owner.isLocalDummy || owner.isTerm && ! owner.is(Method | Lazy )) &&
3941 owner.owner.isClass
4042 if (ownerBecomesConstructor(sym.owner))
4143 sym.copySymDenotation(owner = sym.owner.enclosingClass.primaryConstructor)
@@ -48,38 +50,14 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor
4850 private def noDirectRefsFrom (tree : Tree )(implicit ctx : Context ) =
4951 tree.isDef && tree.symbol.isClass && ! tree.symbol.is(InSuperCall )
5052
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.
5755 */
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 )
7758
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 )
8361
8462 override def transformTemplate (tree : Template )(implicit ctx : Context , info : TransformerInfo ): Tree = {
8563 val cls = ctx.owner.asClass
@@ -102,7 +80,34 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor
10280 }
10381 val paramSyms = vparamsWithOuter map (_.symbol)
10482
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+
106111 val superCalls = new mutable.ListBuffer [Tree ]
107112
108113 // If parent is a constructor call, pull out the call into a separate
@@ -115,12 +120,12 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor
115120 nme.CONSTRUCTOR ),
116121 superArgs) =>
117122 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))
120124 val receiver =
121125 if (toClass) Super (This (cls), tpnme.EMPTY , inConstrCall = true )
122126 else This (cls)
123- superCalls += cpy.Apply (superApp)(
127+ superCalls +=
128+ cpy.Apply (superApp)(
124129 receiver.withPos(superNew.pos)
125130 .select(superSel.symbol).withPos(superSel.pos),
126131 mappedArgs)
@@ -129,67 +134,97 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor
129134 }
130135 val parentTypeTrees = tree.parents.map(normalizeParent)
131136
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+
132180 // Split class body into statements that go into constructor and
133181 // 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 {
135183 case stat :: stats1 =>
136- val (constrStats, clsStats) = splitStats(stats1)
137184 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+ }
144198 case _ : DefTree =>
145- (constrStats, stat :: clsStats)
199+ clsStats += stat
146200 case _ =>
147- (intoConstr(stat, excluded = Mutable ) :: constrStats, clsStats )
201+ constrStats += intoConstr(stat )
148202 }
203+ splitStats(stats1)
149204 case Nil =>
150205 (Nil , Nil )
151206 }
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)
170208
171209 val accessorFields = accessors.filterNot(_ is Method )
172- val (retainedAccessors, droppedAccessors) = accessorFields.partition(isRetained)
173210
174211 // The initializers for the retained accessors */
175- val copyParams = retainedAccessors .map(acc =>
212+ val copyParams = accessorFields.filter(isRetained) .map(acc =>
176213 Assign (ref(acc), ref(acc.subst(accessors, paramSyms))).withPos(tree.pos))
177214
178215 // Drop accessors that are not retained from class scope
179- if (droppedAccessors.nonEmpty) {
216+ val dropped = usage.dropped
217+ if (dropped.nonEmpty) {
180218 val clsInfo = cls.classInfo // TODO investigate: expand clsInfo to cls.info => dotty type error
181219 cls.copy(
182220 info = clsInfo.derivedClassInfo(
183- decls = clsInfo.decls.filteredScope(! droppedAccessors .contains(_))))
221+ decls = clsInfo.decls.filteredScope(! dropped .contains(_))))
184222 }
185223
186224 cpy.Template (tree)(
187225 constr = cpy.DefDef (constr)(
188- rhs = Block (superCalls.toList ::: copyParams ::: constrStats, unitLiteral)),
226+ rhs = Block (superCalls.toList ::: copyParams ::: constrStats.toList , unitLiteral)),
189227 parents = parentTypeTrees,
190- body = clsStats filter {
191- case vdef : ValDef => ! droppedAccessors.contains(vdef.symbol)
192- case _ => true
193- })
228+ body = clsStats.toList)
194229 }
195230}
0 commit comments