Skip to content

Commit d7c1de8

Browse files
committed
refactor field memoization logic -- wip
1 parent 337a9dd commit d7c1de8

File tree

1 file changed

+50
-36
lines changed

1 file changed

+50
-36
lines changed

src/compiler/scala/tools/nsc/transform/Fields.scala

Lines changed: 50 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -111,19 +111,20 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
111111

112112
class FieldMemoization(accessorOrField: Symbol, site: Symbol) {
113113
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]
115116

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))
119118

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
123124

124125
// println(s"fieldMemoizationIn $sym $site = $tp")
125126
def assignSym: Symbol =
126-
if (effectOnly) NoSymbol
127+
if (!stored) NoSymbol
127128
else if (accessorOrField hasFlag ACCESSOR) accessorOrField.setterIn(site)
128129
else accessorOrField
129130
}
@@ -175,7 +176,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
175176
// strict, memoized accessors will receive an implementation in first real class to extend this trait
176177
origDecls.foreach { accessor => if (accessor hasFlag ACCESSOR) {
177178
// 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
179180

180181
// destructively mangle accessor's name (which may cause rehashing of decls), also sets flags
181182
if (accessor hasFlag PRIVATE) accessor makeNotPrivate clazz
@@ -282,7 +283,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
282283
// avoid creating early errors in case of conflicts (wait until refchecks);
283284
// also, skip overridden accessors contributed by supertraits (only act on the last overriding one)
284285
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) {
286287
// add field if needed
287288
val field = clazz.newValue(accessor.localName, accessor.pos) setInfo fieldTypeForGetterIn(accessor, clazz.thisType)
288289

@@ -299,9 +300,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
299300
// println(s"new decls for $clazz: $mixedInAccessorAndFields")
300301

301302
// 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
305304

306305
val newDecls =
307306
if (mixedInAccessorAndFields.isEmpty) oldDecls.filterNot(omittableField)
@@ -396,40 +395,55 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
396395
}
397396
}
398397

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+
*/
399413
stat match {
414+
// TODO: consolidate with ValDef case
400415
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+
419430
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)
421433

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)
425438

426-
case tree => List(
439+
case tree => List (
427440
if (exprOwner != currentOwner && tree.isTerm) atOwner(exprOwner)(super.transform(tree))
428441
else super.transform(tree)
429442
)
430443
}
431444
}
432445

446+
433447
override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] =
434448
if (!currentOwner.isClass || currentOwner.isPackageClass || currentOwner.isInterface) super.transformStats(stats, exprOwner)
435449
else afterOwnPhase {

0 commit comments

Comments
 (0)