@@ -45,10 +45,10 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase =
4545 // to more symbols being retained as parameters. Test case in run/capturing.scala.
4646
4747 /** The private vals that are known to be retained as class fields */
48- private val retainedPrivateVals = mutable.Set [Symbol ]()
48+ private val retainedPrivateVals = mutable.Set .empty [Symbol ]
4949
5050 /** The private vals whose definition comes before the current focus */
51- private val seenPrivateVals = mutable.Set [Symbol ]()
51+ private val seenPrivateVals = mutable.Set .empty [Symbol ]
5252
5353 // Collect all private parameter accessors and value definitions that need
5454 // to be retained. There are several reasons why a parameter accessor or
@@ -57,31 +57,36 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase =
5757 // 2. It is accessed before it is defined
5858 // 3. It is accessed on an object other than `this`
5959 // 4. It is a mutable parameter accessor
60- // 5. It is has a wildcard initializer `_`
61- private def markUsedPrivateSymbols (tree : RefTree )(using Context ): Unit = {
60+ // 5. It has a wildcard initializer `_`
61+ private def markUsedPrivateSymbols (tree : RefTree )(using Context ): Unit =
6262
6363 val sym = tree.symbol
6464 def retain () = retainedPrivateVals.add(sym)
6565
66- if (sym.exists && sym.owner.isClass && mightBeDropped(sym)) {
67- val owner = sym.owner.asClass
68-
69- tree match {
70- case Ident (_) | Select (This (_), _) =>
71- def inConstructor = {
72- val method = ctx.owner.enclosingMethod
73- method.isPrimaryConstructor && ctx.owner.enclosingClass == owner
74- }
75- if (inConstructor &&
76- (sym.is(ParamAccessor ) || seenPrivateVals.contains(sym))) {
77- // used inside constructor, accessed on this,
78- // could use constructor argument instead, no need to retain field
79- }
80- else retain()
81- case _ => retain()
82- }
83- }
84- }
66+ if sym.exists && sym.owner.isClass && sym.mightBeDropped then
67+ tree match
68+ case Ident (_) | Select (This (_), _) =>
69+ val method = ctx.owner.enclosingMethod
70+ // template exprs are moved (below) to constructor, where lifted anonfun will take its captured env as an arg
71+ inline def methodOwnerIsLocalDummyOrSibling =
72+ val owner = method.owner
73+ owner.isLocalDummy || owner.owner == sym.owner && ! owner.isOneOf(MethodOrLazy )
74+ inline def inAnonFunInCtor =
75+ method.isAnonymousFunction
76+ && methodOwnerIsLocalDummyOrSibling
77+ && ! sym.owner.is(Module ) // lambdalift doesn't transform correctly (to do)
78+ val inConstructor =
79+ (method.isPrimaryConstructor || inAnonFunInCtor)
80+ && ctx.owner.enclosingClass == sym.owner
81+ val noField =
82+ inConstructor
83+ && (sym.is(ParamAccessor ) || seenPrivateVals.contains(sym))
84+ // used inside constructor, accessed on this,
85+ // could use constructor argument instead, no need to retain field
86+ if ! noField then
87+ retain()
88+ case _ =>
89+ retain()
8590
8691 override def transformIdent (tree : tpd.Ident )(using Context ): tpd.Tree = {
8792 markUsedPrivateSymbols(tree)
@@ -94,7 +99,7 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase =
9499 }
95100
96101 override def transformValDef (tree : tpd.ValDef )(using Context ): tpd.Tree = {
97- if (mightBeDropped( tree.symbol)) seenPrivateVals += tree.symbol
102+ if tree.symbol.mightBeDropped then seenPrivateVals += tree.symbol
98103 tree
99104 }
100105
@@ -123,7 +128,7 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase =
123128 /** Class members that can be eliminated if referenced only from their own
124129 * constructor.
125130 */
126- private def mightBeDropped (sym : Symbol )(using Context ) =
131+ extension (sym : Symbol ) private def mightBeDropped (using Context ) =
127132 sym.is(Private , butNot = MethodOrLazy ) && ! sym.isAllOf(MutableParamAccessor )
128133
129134 private final val MutableParamAccessor = Mutable | ParamAccessor
@@ -184,8 +189,9 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase =
184189 transform(tree).changeOwnerAfter(prevOwner, constr.symbol, thisPhase)
185190 }
186191
192+ // mightBeDropped is trivially false for NoSymbol -> NoSymbol isRetained
187193 def isRetained (acc : Symbol ) =
188- ! mightBeDropped( acc) || retainedPrivateVals(acc)
194+ ! acc.mightBeDropped || retainedPrivateVals(acc)
189195
190196 val constrStats, clsStats = new mutable.ListBuffer [Tree ]
191197
@@ -209,30 +215,28 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase =
209215 }
210216 }
211217
212- val dropped = mutable.Set [Symbol ]()
218+ val dropped = mutable.Set .empty [Symbol ]
213219
214220 // Split class body into statements that go into constructor and
215221 // definitions that are kept as members of the class.
216- def splitStats (stats : List [Tree ]): Unit = stats match {
217- case stat :: stats1 =>
222+ def splitStats (stats : List [Tree ]): Unit = stats match
223+ case stat :: stats =>
224+ val sym = stat.symbol
218225 stat match {
219- case stat @ ValDef (name, tpt, _) if ! stat.symbol.is(Lazy ) && ! stat.symbol.hasAnnotation(defn.ScalaStaticAnnot ) =>
220- val sym = stat.symbol
226+ case stat @ ValDef (name, tpt, _) if ! sym.is(Lazy ) && ! sym.hasAnnotation(defn.ScalaStaticAnnot ) =>
221227 if (isRetained(sym)) {
222228 if (! stat.rhs.isEmpty && ! isWildcardArg(stat.rhs))
223229 constrStats += Assign (ref(sym), intoConstr(stat.rhs, sym)).withSpan(stat.span)
224230 clsStats += cpy.ValDef (stat)(rhs = EmptyTree )
225231 }
226- else if (! stat.rhs.isEmpty) {
227- dropped += sym
228- sym.copySymDenotation(
229- initFlags = sym.flags &~ Private ,
230- owner = constr.symbol).installAfter(thisPhase)
231- constrStats += intoConstr(stat, sym)
232- } else
232+ else
233233 dropped += sym
234+ if ! stat.rhs.isEmpty then
235+ sym.copySymDenotation(
236+ initFlags = sym.flags &~ Private ,
237+ owner = constr.symbol).installAfter(thisPhase)
238+ constrStats += intoConstr(stat, sym)
234239 case stat @ DefDef (name, _, tpt, _) if stat.symbol.isGetter && ! stat.symbol.is(Lazy ) =>
235- val sym = stat.symbol
236240 assert(isRetained(sym), sym)
237241 if sym.isConstExprFinalVal then
238242 if stat.rhs.isInstanceOf [Literal ] then
@@ -271,9 +275,9 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase =
271275 case _ =>
272276 constrStats += intoConstr(stat, tree.symbol)
273277 }
274- splitStats(stats1 )
278+ splitStats(stats )
275279 case Nil =>
276- }
280+ end splitStats
277281
278282 /** Check that we do not have both a private field with name `x` and a private field
279283 * with name `FieldName(x)`. These will map to the same JVM name and therefore cause
@@ -303,14 +307,15 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase =
303307 dropped += acc
304308 Nil
305309 }
306- else if (! isRetained(acc.field)) { // It may happen for unit fields, tests/run/i6987.scala
310+ else if (acc.field.exists && ! isRetained(acc.field)) { // It may happen for unit fields, tests/run/i6987.scala
307311 dropped += acc.field
308312 Nil
309313 }
310314 else {
311315 val param = acc.subst(accessors, paramSyms)
312- if (param.hasAnnotation(defn.ConstructorOnlyAnnot ))
313- report.error(em " ${acc.name} is marked `@constructorOnly` but it is retained as a field in ${acc.owner}" , acc.srcPos)
316+ if param.hasAnnotation(defn.ConstructorOnlyAnnot ) then
317+ val msg = em " ${acc.name} is marked `@constructorOnly` but it is retained as a field in ${acc.owner}"
318+ report.error(msg, acc.srcPos)
314319 val target = if (acc.is(Method )) acc.field else acc
315320 if (! target.exists) Nil // this case arises when the parameter accessor is an alias
316321 else {
0 commit comments