Skip to content

Commit f266c1e

Browse files
committed
omit unneeded fields (for constants)
1 parent 1a9b0f7 commit f266c1e

File tree

4 files changed

+46
-30
lines changed

4 files changed

+46
-30
lines changed

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

+41-26
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,9 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
220220
detectUsages walk auxConstructorBuf
221221
}
222222
}
223-
def mustBeKept(sym: Symbol) = !(omittables(sym) || (sym.owner.isTrait && sym.isValue && !sym.isMethod))
223+
224+
def needsField(sym: Symbol): Boolean = typeNeedsStorage(enteringErasure(sym.info).resultType)
225+
def mustBeKept(sym: Symbol) = !(omittables(sym) || (sym.isValue && !sym.isMethod && (sym.owner.isTrait || !needsField(sym)))) // TODO: assert(!sym.owner.isTrait, sym.ownerChain)
224226

225227
} // OmittablesHelper
226228

@@ -597,24 +599,41 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
597599
def intoConstructor(oldowner: Symbol, tree: Tree) =
598600
intoConstructorTransformer transform tree.changeOwner(oldowner -> constr.symbol)
599601

600-
private def assignmentIntoCtor(stat: ValOrDefDef): Unit = {
601-
val rhs1 = intoConstructor(stat.symbol, stat.rhs)
602+
private def needsCtorEffect(stat: ValOrDefDef): Boolean = !(stat.rhs == EmptyTree || stat.symbol.isLazy)
603+
604+
// Move the RHS of a Val/Def (traits don't have vals, their getter holds the RHS) to the appropriate part of the ctor.
605+
// If the val def 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+
private def moveEffectToCtor(stat: ValOrDefDef): Boolean = {
609+
val fieldNeeded = needsField(stat.symbol)
610+
611+
if (needsCtorEffect(stat)) {
612+
val rhs1 = intoConstructor(stat.symbol, stat.rhs)
602613

603-
// Should tree be moved in front of super constructor call?
604-
// (I.e., is it derived from an early definition?)
605-
val constrBufPart =
606-
if (stat.mods hasFlag PRESUPER | PARAMACCESSOR) constrPrefixBuf
607-
else constrStatBuf
614+
// Should tree be moved in front of super constructor call?
615+
// (I.e., is it derived from an early definition?)
616+
val constrBufPart =
617+
if (stat.mods hasFlag PRESUPER | PARAMACCESSOR) constrPrefixBuf
618+
else constrStatBuf
608619

609-
val assignSym =
610-
if (stat.symbol.isGetter) stat.symbol.asInstanceOf[MethodSymbol].referenced //setterIn(clazz, hasExpandedName = false) // not yet expanded -- see Mixin
611-
else stat.symbol
620+
val effect =
621+
if (fieldNeeded) {
622+
val assignSym =
623+
if (stat.symbol.isGetter) stat.symbol.asInstanceOf[MethodSymbol].referenced //setterIn(clazz, hasExpandedName = false) // not yet expanded -- see Mixin
624+
else stat.symbol
612625

613-
assert(assignSym ne NoSymbol, stat)
626+
assert(assignSym ne NoSymbol, stat)
627+
mkAssign(assignSym, rhs1)
628+
} else rhs1
629+
630+
constrBufPart += effect
631+
}
614632

615-
constrBufPart += mkAssign(assignSym, rhs1)
633+
fieldNeeded
616634
}
617635

636+
618637
// TODO: we probably can't emit an Assign tree after typers, need to Apply the setter
619638
// Create an assignment to class field `to` with rhs `from`
620639
def mkAssign(to: Symbol, from: Tree): Tree =
@@ -692,24 +711,20 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
692711
else if (stat.symbol.isConstructor) auxConstructorBuf += stat
693712
// a concrete getter's RHS is treated like a ValDef (the actual field isn't emitted until Mixin augments the class that inherits the trait)
694713
else if (inTrait && stat.symbol.isGetter && (rhs ne EmptyTree)) {
695-
assignmentIntoCtor(dd)
714+
moveEffectToCtor(dd) // ignore whether we need a field -- it will be added in the inheriting class by Mixin
696715
defBuf += deriveDefDef(dd)(_ => EmptyTree)
697716
}
698717
else defBuf += stat
699718
}
700719
case vd@ ValDef(mods, _, _, rhs) if !mods.hasStaticFlag =>
701-
// val defs with constant right-hand sides are eliminated.
702-
// for all other val defs, an empty valdef goes into the template and
703-
// the initializer goes as an assignment into the constructor
704-
// if the val def is an early initialized or a parameter accessor, it goes
705-
// before the superclass constructor call, otherwise it goes after.
706-
// Lazy vals don't get the assignment in the constructor.
707-
if (!stat.symbol.tpe.isInstanceOf[ConstantType]) { // TODO: MethodSynthesis should no longer emit ValDefs with ConstantTypes
708-
if (rhs != EmptyTree && !stat.symbol.isLazy) {
709-
assignmentIntoCtor(vd)
710-
}
711-
defBuf += deriveValDef(stat)(_ => EmptyTree)
712-
}
720+
// Move field's effect into the constructor (usually an assignment of rhs to field,
721+
// but it could also be the side-effect of the rhs when no storage is needed).
722+
val needsField = moveEffectToCtor(vd)
723+
println(s"$vd needsField: $needsField")
724+
725+
// Only emit fields that actually need to be stored
726+
if (needsField) defBuf += deriveValDef(stat)(_ => EmptyTree)
727+
713728
case ValDef(_, _, _, rhs) =>
714729
// Add static initializer statements to classInitStatBuf and remove the rhs from the val def.
715730
classInitStatBuf += mkAssign(stat.symbol, rhs)

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
266266

267267
// mixin one field only (this method is called for both accessors)
268268
// don't mix in a field at all if it doesn't need storage
269-
if (!(traitMember.isSetter || typeNeedsNoStorage(traitMember.tpe.resultType)))
269+
if (traitMember.isGetter && typeNeedsStorage(traitMember.tpe.resultType))
270270
mixinFieldSym(traitMember)
271271
}
272272
else devWarning(s"Overridden concrete accessor: ${traitMember.fullLocationString}")

src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala

+3-2
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,8 @@ trait MethodSynthesis {
134134
ImplicitClassWrapper(tree).createAndEnterSymbol()
135135
}
136136

137-
// TODO: see standardAccessors
137+
// TODO: why is all this symbol creation disconnected from addDerivedTrees,
138+
// which creates another list of Field/Getter/Setter factories???
138139
def enterGetterSetter(tree: ValDef) {
139140
val ValDef(mods, name, _, _) = tree
140141
if (nme.isSetterName(name))
@@ -321,7 +322,7 @@ trait MethodSynthesis {
321322
// Constructors will move the assignment to the constructor, abstracting over the field using the field setter,
322323
// and Mixin will add a field to the class that mixes in the trait, implementing the accessors in terms of it
323324
def noFieldHere = nothingToMemoize || owner.isTrait
324-
protected def nothingToMemoize = isDeferred || typeNeedsNoStorage(basisSym.tpe.resultType)
325+
protected def nothingToMemoize = isDeferred // TODO: we don't know the val's type at this point... || typeNeedsNoStorage(???)
325326

326327
def isSetter = false
327328
def isDeferred = mods.isDeferred

src/reflect/scala/reflect/internal/Definitions.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ trait Definitions extends api.StandardDefinitions {
236236
// Take into account annotations so that we keep annotated unit lazy val
237237
// to get better error message already from the cps plugin itself
238238
def isUnitType(tp: Type) = tp.typeSymbol == UnitClass && tp.annotations.isEmpty // you never know with annotations...
239-
def typeNeedsNoStorage(tp: Type) = tp.isInstanceOf[ConstantType] || isUnitType(tp)
239+
def typeNeedsStorage(tp: Type) = !(tp.isInstanceOf[ConstantType] || isUnitType(tp))
240240

241241
def hasMultipleNonImplicitParamLists(member: Symbol): Boolean = hasMultipleNonImplicitParamLists(member.info)
242242
def hasMultipleNonImplicitParamLists(info: Type): Boolean = info match {

0 commit comments

Comments
 (0)