Skip to content

Commit 029f9c9

Browse files
committed
Drop trait accessor's RHS during constructors
1 parent 4b6c3dd commit 029f9c9

File tree

2 files changed

+94
-78
lines changed

2 files changed

+94
-78
lines changed

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

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,6 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure =>
5050
}
5151
}
5252

53-
private def mkAssign(clazz: Symbol, assignSym: Symbol, rhs: Tree): Tree = {
54-
val qual = Select(This(clazz), assignSym)
55-
if (assignSym.isSetter) Apply(qual, List(rhs))
56-
else Assign(qual, rhs)
57-
}
58-
5953
/** Add calls to supermixin constructors
6054
* `super[mix].$init$()`
6155
* to tree, which is assumed to be the body of a constructor of class clazz.

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

Lines changed: 94 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -565,9 +565,6 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
565565
}
566566
}
567567

568-
// Constant typed vals are not memoized.
569-
def memoizeValue(sym: Symbol) = !sym.info.resultType.isInstanceOf[ConstantType]
570-
571568
/** Triage definitions and statements in this template into the following categories.
572569
* The primary constructor is treated separately, as it is assembled in part from these pieces.
573570
*
@@ -577,84 +574,109 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
577574
* - `constrStats`: statements that go into the constructor after and including the superclass constructor call
578575
* - `classInitStats`: statements that go into the class initializer
579576
*/
580-
def triageStats = {
581-
val defBuf, auxConstructorBuf, constrPrefixBuf, constrStatBuf, classInitStatBuf = new mutable.ListBuffer[Tree]
582-
583-
// The early initialized field definitions of the class (these are the class members)
584-
val presupers = treeInfo.preSuperFields(stats)
585-
586-
// generate code to copy pre-initialized fields
587-
for (stat <- primaryConstrBody.stats) {
588-
constrStatBuf += stat
589-
stat match {
590-
case ValDef(mods, name, _, _) if mods.hasFlag(PRESUPER) =>
591-
// stat is the constructor-local definition of the field value
592-
val fields = presupers filter (_.getterName == name)
593-
assert(fields.length == 1, s"expected exactly one field by name $name in $presupers of $clazz's early initializers")
594-
val to = fields.head.symbol
595-
596-
if (memoizeValue(to)) constrStatBuf += mkAssign(to, Ident(stat.symbol))
597-
case _ =>
577+
class Triage {
578+
private val defBuf, auxConstructorBuf, constrPrefixBuf, constrStatBuf, classInitStatBuf = new mutable.ListBuffer[Tree]
579+
580+
triage()
581+
582+
val defs = defBuf.toList
583+
val auxConstructors = auxConstructorBuf.toList
584+
val constructorPrefix = constrPrefixBuf.toList
585+
val constructorStats = constrStatBuf.toList
586+
val classInitStats = classInitStatBuf.toList
587+
588+
private def triage() = {
589+
// Constant typed vals are not memoized.
590+
def memoizeValue(sym: Symbol) = !sym.info.resultType.isInstanceOf[ConstantType]
591+
592+
// The early initialized field definitions of the class (these are the class members)
593+
val presupers = treeInfo.preSuperFields(stats)
594+
595+
// generate code to copy pre-initialized fields
596+
for (stat <- primaryConstrBody.stats) {
597+
constrStatBuf += stat
598+
stat match {
599+
case ValDef(mods, name, _, _) if mods.hasFlag(PRESUPER) => // TODO trait presupers
600+
// stat is the constructor-local definition of the field value
601+
val fields = presupers filter (_.getterName == name)
602+
assert(fields.length == 1, s"expected exactly one field by name $name in $presupers of $clazz's early initializers")
603+
val to = fields.head.symbol
604+
605+
if (memoizeValue(to)) constrStatBuf += mkAssign(to, Ident(stat.symbol))
606+
case _ =>
607+
}
598608
}
599-
}
600609

601-
for (stat <- stats) {
602-
val statSym = stat.symbol
603-
604-
// Move the RHS of a ValDef to the appropriate part of the ctor.
605-
// If the val is an early initialized or a parameter accessor,
606-
// it goes before the superclass constructor call, otherwise it goes after.
607-
// A lazy val's effect is not moved to the constructor, as it is delayed.
608-
// Returns `true` when a `ValDef` is needed.
609-
def moveEffectToCtor(mods: Modifiers, rhs: Tree, assignSym: Symbol): Unit = {
610-
val initializingRhs =
611-
if ((assignSym eq NoSymbol) || statSym.isLazy) EmptyTree // not memoized, or effect delayed (for lazy val)
612-
else if (!mods.hasStaticFlag) intoConstructor(statSym, primaryConstr.symbol)(rhs)
613-
else rhs
614-
615-
if (initializingRhs ne EmptyTree) {
616-
val initPhase =
617-
if (mods hasFlag STATIC) classInitStatBuf
618-
else if (mods hasFlag PRESUPER | PARAMACCESSOR) constrPrefixBuf
619-
else constrStatBuf
620-
621-
initPhase += mkAssign(assignSym, initializingRhs)
610+
val primaryConstrSym = primaryConstr.symbol
611+
612+
for (stat <- stats) {
613+
val statSym = stat.symbol
614+
615+
// Move the RHS of a ValDef to the appropriate part of the ctor.
616+
// If the val is an early initialized or a parameter accessor,
617+
// it goes before the superclass constructor call, otherwise it goes after.
618+
// A lazy val's effect is not moved to the constructor, as it is delayed.
619+
// Returns `true` when a `ValDef` is needed.
620+
def moveEffectToCtor(mods: Modifiers, rhs: Tree, assignSym: Symbol): Unit = {
621+
val initializingRhs =
622+
if ((assignSym eq NoSymbol) || statSym.isLazy) EmptyTree // not memoized, or effect delayed (for lazy val)
623+
else if (!mods.hasStaticFlag) intoConstructor(statSym, primaryConstrSym)(rhs)
624+
else rhs
625+
626+
if (initializingRhs ne EmptyTree) {
627+
val initPhase =
628+
if (mods hasFlag STATIC) classInitStatBuf
629+
else if (mods hasFlag PRESUPER | PARAMACCESSOR) constrPrefixBuf
630+
else constrStatBuf
631+
632+
initPhase += mkAssign(assignSym, initializingRhs)
633+
}
622634
}
623-
}
624635

625-
stat match {
626-
// recurse on class definition, store in defBuf
627-
case _: ClassDef if !stat.symbol.isInterface => defBuf += new ConstructorTransformer(unit).transform(stat)
628-
629-
// Triage methods -- they all end up in the template --
630-
// regular ones go to `defBuf`, secondary contructors go to `auxConstructorBuf`.
631-
// The primary constructor is dealt with separately (we're massaging it here).
632-
case _: DefDef if statSym.isPrimaryConstructor || statSym.isMixinConstructor => ()
633-
case _: DefDef if statSym.isConstructor => auxConstructorBuf += stat
634-
case _: DefDef => defBuf += stat
635-
636-
// If a val needs a field, an empty valdef goes into the template.
637-
// Except for lazy and ConstantTyped vals, the field is initialized by an assignment in:
638-
// - the class initializer (static),
639-
// - the constructor, before the super call (early initialized or a parameter accessor),
640-
// - the constructor, after the super call (regular val).
641-
case ValDef(mods, _, _, rhs) =>
642-
if (rhs ne EmptyTree) {
643-
val emitField = memoizeValue(statSym)
644-
moveEffectToCtor(mods, rhs, if (emitField) statSym else NoSymbol)
645-
if (emitField) defBuf += deriveValDef(stat)(_ => EmptyTree)
646-
} else defBuf += stat
647-
648-
// all other statements go into the constructor
649-
case _ => constrStatBuf += intoConstructor(impl.symbol, primaryConstr.symbol)(stat)
636+
stat match {
637+
// recurse on class definition, store in defBuf
638+
case _: ClassDef if !statSym.isInterface =>
639+
defBuf += new ConstructorTransformer(unit).transform(stat)
640+
641+
// primary constructor is already tracked as `primaryConstr`
642+
// non-primary constructors go to auxConstructorBuf
643+
// mixin constructors are suppressed (!?!?)
644+
case _: DefDef if statSym.isConstructor =>
645+
if ((statSym ne primaryConstrSym) && !statSym.isMixinConstructor) auxConstructorBuf += stat
646+
647+
// If a val needs a field, an empty valdef goes into the template.
648+
// Except for lazy and ConstantTyped vals, the field is initialized by an assignment in:
649+
// - the class initializer (static),
650+
// - the constructor, before the super call (early initialized or a parameter accessor),
651+
// - the constructor, after the super call (regular val).
652+
case vd: ValDef =>
653+
if (vd.rhs eq EmptyTree) { defBuf += vd }
654+
else {
655+
val emitField = memoizeValue(statSym)
656+
657+
val assignSym = if (!emitField) NoSymbol else statSym
658+
moveEffectToCtor(vd.mods, vd.rhs, assignSym)
659+
660+
if (emitField) defBuf += deriveValDef(stat)(_ => EmptyTree)
661+
}
662+
663+
case dd: DefDef =>
664+
def traitMemoizedFieldAccessor = clazz.isTrait && statSym.isAccessor && memoizeValue(statSym.accessed)
665+
666+
if ((dd.rhs eq EmptyTree) || !traitMemoizedFieldAccessor) { defBuf += dd }
667+
else defBuf += deriveDefDef(stat)(_ => EmptyTree)
668+
669+
670+
// all other statements go into the constructor
671+
case _ =>
672+
constrStatBuf += intoConstructor(impl.symbol, primaryConstrSym)(stat)
673+
}
650674
}
651675
}
652-
653-
(defBuf.toList, auxConstructorBuf.toList, constrPrefixBuf.toList, constrStatBuf.toList, classInitStatBuf.toList)
654676
}
655677

656678
def transformed = {
657-
val (defs, auxConstructors, constructorPrefix, constructorStats, classInitStats) = triageStats
679+
val triage = new Triage; import triage._
658680

659681
// omit unused outers
660682
val omittableAccessor: Set[Symbol] =

0 commit comments

Comments
 (0)