@@ -45,10 +45,10 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase =
45
45
// to more symbols being retained as parameters. Test case in run/capturing.scala.
46
46
47
47
/** 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 ]
49
49
50
50
/** 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 ]
52
52
53
53
// Collect all private parameter accessors and value definitions that need
54
54
// to be retained. There are several reasons why a parameter accessor or
@@ -57,31 +57,36 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase =
57
57
// 2. It is accessed before it is defined
58
58
// 3. It is accessed on an object other than `this`
59
59
// 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 =
62
62
63
63
val sym = tree.symbol
64
64
def retain () = retainedPrivateVals.add(sym)
65
65
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
+ 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()
85
90
86
91
override def transformIdent (tree : tpd.Ident )(using Context ): tpd.Tree = {
87
92
markUsedPrivateSymbols(tree)
@@ -184,6 +189,7 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase =
184
189
transform(tree).changeOwnerAfter(prevOwner, constr.symbol, thisPhase)
185
190
}
186
191
192
+ // mightBeDropped is trivially false for NoSymbol -> NoSymbol isRetained
187
193
def isRetained (acc : Symbol ) =
188
194
! mightBeDropped(acc) || retainedPrivateVals(acc)
189
195
@@ -209,30 +215,28 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase =
209
215
}
210
216
}
211
217
212
- val dropped = mutable.Set [Symbol ]()
218
+ val dropped = mutable.Set .empty [Symbol ]
213
219
214
220
// Split class body into statements that go into constructor and
215
221
// 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
218
225
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 ) =>
221
227
if (isRetained(sym)) {
222
228
if (! stat.rhs.isEmpty && ! isWildcardArg(stat.rhs))
223
229
constrStats += Assign (ref(sym), intoConstr(stat.rhs, sym)).withSpan(stat.span)
224
230
clsStats += cpy.ValDef (stat)(rhs = EmptyTree )
225
231
}
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
233
233
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)
234
239
case stat @ DefDef (name, _, tpt, _) if stat.symbol.isGetter && ! stat.symbol.is(Lazy ) =>
235
- val sym = stat.symbol
236
240
assert(isRetained(sym), sym)
237
241
if sym.isConstExprFinalVal then
238
242
if stat.rhs.isInstanceOf [Literal ] then
@@ -271,9 +275,9 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase =
271
275
case _ =>
272
276
constrStats += intoConstr(stat, tree.symbol)
273
277
}
274
- splitStats(stats1 )
278
+ splitStats(stats )
275
279
case Nil =>
276
- }
280
+ end splitStats
277
281
278
282
/** Check that we do not have both a private field with name `x` and a private field
279
283
* 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 =
303
307
dropped += acc
304
308
Nil
305
309
}
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
307
311
dropped += acc.field
308
312
Nil
309
313
}
310
314
else {
311
315
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)
314
319
val target = if (acc.is(Method )) acc.field else acc
315
320
if (! target.exists) Nil // this case arises when the parameter accessor is an alias
316
321
else {
0 commit comments