@@ -111,19 +111,20 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
111
111
112
112
class FieldMemoization (accessorOrField : Symbol , site : Symbol ) {
113
113
private val tp = fieldTypeOfAccessorIn(accessorOrField, site.thisType)
114
- val memoized = ! tp.isInstanceOf [ConstantType ]
114
+ // not stored, no side-effect
115
+ val pureConstant = tp.isInstanceOf [ConstantType ]
115
116
116
- // do not assign to field
117
- // getter for Unit-typed val, or setter for var that isn't stored (of type Unit)
118
- val effectOnly = isUnitType(tp) || (! site.isTrait && accessorOrField.isSetter && ! memoized)
117
+ val stored = ! (pureConstant || isUnitType(tp))
119
118
120
- val effectful = memoized || effectOnly
121
-
122
- val needsField = ! effectOnly && memoized
119
+ // - setter (for var) or getter (for val) that isn't stored
120
+ // (pureConstant already shortcircuits this, but it may still be a unit-typed val)
121
+ def deriveStoredAccessor (stat : Tree )(deriveUnitDef : Tree => Tree ) =
122
+ if (! stored) deriveUnitDef(stat)
123
+ else stat
123
124
124
125
// println(s"fieldMemoizationIn $sym $site = $tp")
125
126
def assignSym : Symbol =
126
- if (effectOnly ) NoSymbol
127
+ if (! stored ) NoSymbol
127
128
else if (accessorOrField hasFlag ACCESSOR ) accessorOrField.setterIn(site)
128
129
else accessorOrField
129
130
}
@@ -175,7 +176,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
175
176
// strict, memoized accessors will receive an implementation in first real class to extend this trait
176
177
origDecls.foreach { accessor => if (accessor hasFlag ACCESSOR ) {
177
178
// check flags before calling makeNotPrivate
178
- val memoizedGetter = ! (accessor hasFlag (DEFERRED | LAZY )) && fieldMemoizationIn(accessor, clazz).needsField
179
+ val memoizedGetter = ! (accessor hasFlag (DEFERRED | LAZY )) && fieldMemoizationIn(accessor, clazz).stored
179
180
180
181
// destructively mangle accessor's name (which may cause rehashing of decls), also sets flags
181
182
if (accessor hasFlag PRIVATE ) accessor makeNotPrivate clazz
@@ -282,7 +283,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
282
283
// avoid creating early errors in case of conflicts (wait until refchecks);
283
284
// also, skip overridden accessors contributed by supertraits (only act on the last overriding one)
284
285
else if (accessorConflictsExistingVal(accessor) || isOverriddenAccessor(accessor, clazz)) Nil
285
- else if (accessor.isGetter && fieldMemoizationIn(accessor, clazz).needsField ) {
286
+ else if (accessor.isGetter && fieldMemoizationIn(accessor, clazz).stored ) {
286
287
// add field if needed
287
288
val field = clazz.newValue(accessor.localName, accessor.pos) setInfo fieldTypeForGetterIn(accessor, clazz.thisType)
288
289
@@ -299,9 +300,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
299
300
// println(s"new decls for $clazz: $mixedInAccessorAndFields")
300
301
301
302
// omit fields that are not memoized, retain all other members
302
- def omittableField (sym : Symbol ) =
303
- sym.isValue && ! sym.isMethod &&
304
- fieldMemoizationIn(sym, clazz).effectOnly // TODO: not yet `needsField`, to produce same bytecode as 2.12.0-M3
303
+ def omittableField (sym : Symbol ) = sym.isValue && ! sym.isMethod && ! fieldMemoizationIn(sym, clazz).stored
305
304
306
305
val newDecls =
307
306
if (mixedInAccessorAndFields.isEmpty) oldDecls.filterNot(omittableField)
@@ -396,40 +395,55 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
396
395
}
397
396
}
398
397
398
+ def init (rhs : Tree )(implicit fieldMemoization : FieldMemoization ) = initEffect(rhs, fieldMemoization.assignSym) :: Nil
399
+
400
+ /*
401
+ For traits, the getter has the val's RHS, which is already constant-folded. There is no valdef.
402
+ For classes, we still have the classic scheme of private[this] valdef + getter & setter that read/assign to the field.
403
+
404
+ There are two axes: (1) is there a side-effect to the val (2) does the val need storage?
405
+ For a ConstantType, both answers are "no". (For a unit-typed field, there's a side-effect, but no storage needed.)
406
+
407
+ All others (getter for trait field, valdef for class field) have their rhs moved to an initialization statement.
408
+ Trait accessors for stored fields are made abstract (there can be no field in a trait).
409
+ All accessors for non-stored, but effectful fields, receive a constant rhs,
410
+ as the effect is performed by the initialization statement.
411
+ (We could do this for unit-typed fields, but have chosen not to for backwards compatibility.)
412
+ */
399
413
stat match {
414
+ // TODO: consolidate with ValDef case
400
415
case DefDef (_, _, _, _, _, rhs) if (rhs ne EmptyTree ) && (statSym hasFlag ACCESSOR ) && ! excludedAccessorOrFieldByFlags(statSym) =>
401
- val fieldMemoization = fieldMemoizationIn(statSym, clazz)
402
- def getterRhs (x : Tree ) = if (fieldMemoization.effectOnly) mkTypedUnit(statSym.pos) else EmptyTree
403
-
404
- // TODO: consolidate with ValDef case
405
- if (clazz.isTrait) {
406
- // there's a synthetic setter if val is not mutable (symbol is created in info transform)
407
- if (fieldMemoization.effectful) deriveDefDef(stat)(getterRhs) :: initEffect(rhs, fieldMemoization.assignSym) :: Nil
408
- else stat :: Nil
409
- } else (
410
- // regular getter -- field will be preserved (see case ValDef)
411
- if (fieldMemoization.needsField) stat
412
- // getter for Unit-typed val, or setter for var that isn't stored (of type Unit)
413
- else if (fieldMemoization.effectOnly) deriveUnitDef(stat)
414
- // getter for constant, emit literal for its body
415
- else deriveDefDef(stat)(_ => gen.mkAttributedQualifier(rhs.tpe))
416
- ) :: Nil
417
-
418
- // If a val needs a field, an empty valdef and an assignment to its rhs go into the template
416
+ // for accessors defined in a class, this wasn't done yet ???
417
+ def statInlinedConstantRhs = deriveDefDef(stat)(_ => gen.mkAttributedQualifier(rhs.tpe))
418
+
419
+ def dropRhs = deriveDefDef(stat)(_ => EmptyTree )
420
+ implicit val fieldMemoization = fieldMemoizationIn(statSym, clazz)
421
+ def deriveStoredAccessor (tree : Tree ) = fieldMemoization.deriveStoredAccessor(tree)(deriveUnitDef)
422
+
423
+ if (clazz.isTrait)
424
+ if (fieldMemoization.pureConstant) stat :: Nil
425
+ else deriveStoredAccessor(dropRhs) :: init(rhs)
426
+ else
427
+ if (fieldMemoization.pureConstant) statInlinedConstantRhs :: Nil
428
+ else deriveStoredAccessor(stat) :: Nil
429
+
419
430
case ValDef (mods, _, _, rhs) if (rhs ne EmptyTree ) && ! excludedAccessorOrFieldByFlags(statSym) =>
420
- val fieldMemoization = fieldMemoizationIn(statSym, clazz)
431
+ def dropRhs = deriveValDef(stat)(_ => EmptyTree )
432
+ implicit val fieldMemoization = fieldMemoizationIn(statSym, clazz)
421
433
422
- if (fieldMemoization.needsField) deriveValDef(stat)(_ => EmptyTree ) :: initEffect(rhs, fieldMemoization.assignSym) :: Nil
423
- else if (fieldMemoization.effectOnly) initEffect(rhs, NoSymbol ) :: Nil // drop the val entirely -- it could not have been referenced outside accesors
424
- else Nil
434
+ // drop the val for (a) constant (pure & not-stored) and (b) not-stored (but still effectful) fields
435
+ if (fieldMemoization.pureConstant) Nil // (a)
436
+ else if (fieldMemoization.stored) dropRhs :: init(rhs)
437
+ else init(rhs) // (b)
425
438
426
- case tree => List (
439
+ case tree => List (
427
440
if (exprOwner != currentOwner && tree.isTerm) atOwner(exprOwner)(super .transform(tree))
428
441
else super .transform(tree)
429
442
)
430
443
}
431
444
}
432
445
446
+
433
447
override def transformStats (stats : List [Tree ], exprOwner : Symbol ): List [Tree ] =
434
448
if (! currentOwner.isClass || currentOwner.isPackageClass || currentOwner.isInterface) super .transformStats(stats, exprOwner)
435
449
else afterOwnPhase {
0 commit comments