@@ -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,37 @@ 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 && mightBeDropped(sym) 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 inAnonFunInCtor =
72+ method.isAnonymousFunction
73+ && (
74+ method.owner.isLocalDummy
75+ ||
76+ method.owner.owner == sym.owner && ! method.owner.isOneOf(MethodOrLazy )
77+ )
78+ && ! sym.owner.is(Module ) // lambdalift doesn't transform correctly (to do)
79+ val inConstructor =
80+ (method.isPrimaryConstructor || inAnonFunInCtor)
81+ && ctx.owner.enclosingClass == sym.owner
82+ val noField =
83+ inConstructor
84+ && (sym.is(ParamAccessor ) || seenPrivateVals.contains(sym))
85+ // used inside constructor, accessed on this,
86+ // could use constructor argument instead, no need to retain field
87+ if ! noField then
88+ retain()
89+ case _ =>
90+ retain()
8591
8692 override def transformIdent (tree : tpd.Ident )(using Context ): tpd.Tree = {
8793 markUsedPrivateSymbols(tree)
@@ -184,6 +190,7 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase =
184190 transform(tree).changeOwnerAfter(prevOwner, constr.symbol, thisPhase)
185191 }
186192
193+ // mightBeDropped is trivially false for NoSymbol -> NoSymbol isRetained
187194 def isRetained (acc : Symbol ) =
188195 ! mightBeDropped(acc) || retainedPrivateVals(acc)
189196
@@ -209,30 +216,28 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase =
209216 }
210217 }
211218
212- val dropped = mutable.Set [Symbol ]()
219+ val dropped = mutable.Set .empty [Symbol ]
213220
214221 // Split class body into statements that go into constructor and
215222 // definitions that are kept as members of the class.
216- def splitStats (stats : List [Tree ]): Unit = stats match {
217- case stat :: stats1 =>
223+ def splitStats (stats : List [Tree ]): Unit = stats match
224+ case stat :: stats =>
225+ val sym = stat.symbol
218226 stat match {
219- case stat @ ValDef (name, tpt, _) if ! stat.symbol.is(Lazy ) && ! stat.symbol.hasAnnotation(defn.ScalaStaticAnnot ) =>
220- val sym = stat.symbol
227+ case stat @ ValDef (name, tpt, _) if ! sym.is(Lazy ) && ! sym.hasAnnotation(defn.ScalaStaticAnnot ) =>
221228 if (isRetained(sym)) {
222229 if (! stat.rhs.isEmpty && ! isWildcardArg(stat.rhs))
223230 constrStats += Assign (ref(sym), intoConstr(stat.rhs, sym)).withSpan(stat.span)
224231 clsStats += cpy.ValDef (stat)(rhs = EmptyTree )
225232 }
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
233+ else
233234 dropped += sym
235+ if ! stat.rhs.isEmpty then
236+ sym.copySymDenotation(
237+ initFlags = sym.flags &~ Private ,
238+ owner = constr.symbol).installAfter(thisPhase)
239+ constrStats += intoConstr(stat, sym)
234240 case stat @ DefDef (name, _, tpt, _) if stat.symbol.isGetter && ! stat.symbol.is(Lazy ) =>
235- val sym = stat.symbol
236241 assert(isRetained(sym), sym)
237242 if sym.isConstExprFinalVal then
238243 if stat.rhs.isInstanceOf [Literal ] then
@@ -271,9 +276,9 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase =
271276 case _ =>
272277 constrStats += intoConstr(stat, tree.symbol)
273278 }
274- splitStats(stats1 )
279+ splitStats(stats )
275280 case Nil =>
276- }
281+ end splitStats
277282
278283 /** Check that we do not have both a private field with name `x` and a private field
279284 * with name `FieldName(x)`. These will map to the same JVM name and therefore cause
@@ -303,14 +308,15 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase =
303308 dropped += acc
304309 Nil
305310 }
306- else if (! isRetained(acc.field)) { // It may happen for unit fields, tests/run/i6987.scala
311+ else if (acc.field.exists && ! isRetained(acc.field)) { // It may happen for unit fields, tests/run/i6987.scala
307312 dropped += acc.field
308313 Nil
309314 }
310315 else {
311316 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)
317+ if param.hasAnnotation(defn.ConstructorOnlyAnnot ) then
318+ val msg = em " ${acc.name} is marked `@constructorOnly` but it is retained as a field in ${acc.owner}"
319+ report.error(msg, acc.srcPos)
314320 val target = if (acc.is(Method )) acc.field else acc
315321 if (! target.exists) Nil // this case arises when the parameter accessor is an alias
316322 else {
0 commit comments