Skip to content

Commit 4b4932e

Browse files
committed
do assignment to trait fields in AddInterfaces
regular class vals get assignments during constructors, as before
1 parent 1a358ce commit 4b4932e

File tree

11 files changed

+99
-114
lines changed

11 files changed

+99
-114
lines changed

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

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,9 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure =>
5858
def needsImplMethod(sym: Symbol) = (
5959
sym.isMethod
6060
&& isInterfaceMember(sym)
61-
&& (!(sym hasFlag ACCESSOR) || sym.isLazy || !(sym hasFlag SYNTHESIZE_IMPL_IN_SUBCLASS)) // SYNTHESIZE_IMPL_IN_SUBCLASS accessors are mixed in by the fields phase, but others should be treated as regulars methods (constant-typed getters)
62-
// so that constructors populates the init method in the impl class
61+
// SYNTHESIZE_IMPL_IN_SUBCLASS accessors are mixed in by the fields phase, but others should be treated as regulars methods
62+
// (this will eventually include constant-typed getters, as they can be fully implemented in the interface)
63+
&& (!(sym hasFlag ACCESSOR) || sym.isLazy || !(sym hasFlag SYNTHESIZE_IMPL_IN_SUBCLASS))
6364
&& (!sym.hasFlag(DEFERRED | SUPERACCESSOR) || (sym hasFlag lateDEFERRED))
6465
)
6566

@@ -245,13 +246,25 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure =>
245246
}
246247

247248
private def isInterfaceTree(tree: Tree) = tree.isDef && isInterfaceMember(tree.symbol)
249+
private def isMemoizedTraitGetter(tree: Tree) = (tree.symbol hasFlag SYNTHESIZE_IMPL_IN_SUBCLASS) && tree.symbol.isGetter && !tree.symbol.info.resultType.isInstanceOf[ConstantType]
248250

249-
private def deriveMemberForImplClass(tree: Tree): Tree =
250-
if (isInterfaceTree(tree)) if (needsImplMethod(tree.symbol)) implMethodDef(tree) else EmptyTree
251+
private def mkAssign(clazz: Symbol, assignSym: Symbol, rhs: Tree): Tree = {
252+
val qual = Select(This(clazz), assignSym)
253+
if (assignSym.isSetter) Apply(qual, List(rhs))
254+
else Assign(qual, rhs)
255+
}
256+
257+
private def deriveMemberForImplClass(clazz: Symbol, iface: Symbol, exprOwner: Symbol)(tree: Tree): Tree =
258+
if (isInterfaceTree(tree))
259+
if (needsImplMethod(tree.symbol)) implMethodDef(tree)
260+
else if (isMemoizedTraitGetter(tree)) mkAssign(clazz, tree.symbol.setterIn(iface), tree.asInstanceOf[DefDef].rhs.changeOwner(tree.symbol -> exprOwner))
261+
else EmptyTree
251262
else tree
252263

253264
private def deriveMemberForInterface(tree: Tree): Tree =
254-
if (isInterfaceTree(tree)) if (needsImplMethod(tree.symbol)) DefDef(tree.symbol, EmptyTree) else tree
265+
if (isInterfaceTree(tree))
266+
if (needsImplMethod(tree.symbol) || isMemoizedTraitGetter(tree)) DefDef(tree.symbol, EmptyTree) // TODO: use deriveDefDef(tree)(_ => EmptyTree) ?
267+
else tree
255268
else EmptyTree
256269

257270
private def ifaceTemplate(templ: Template): Template =
@@ -284,20 +297,22 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure =>
284297
if (treeInfo.firstConstructor(stats) != EmptyTree) stats
285298
else DefDef(clazz.primaryConstructor, Block(List(), Literal(Constant(())))) :: stats
286299

287-
private def implTemplate(clazz: Symbol, templ: Template): Template = atPos(templ.pos) {
288-
val templ1 = (
289-
Template(templ.parents, noSelfType, addMixinConstructorDef(clazz, templ.body map deriveMemberForImplClass))
290-
setSymbol clazz.newLocalDummy(templ.pos)
291-
)
292-
templ1.changeOwner(templ.symbol.owner -> clazz, templ.symbol -> templ1.symbol)
293-
templ1
300+
private def implTemplate(clazz: Symbol, iface: ClassDef): Template = atPos(iface.impl.pos) {
301+
val templ = iface.impl
302+
val exprOwner = clazz.newLocalDummy(templ.pos)
303+
val derivedImplClassMembers = templ.body map deriveMemberForImplClass(clazz, iface.symbol, exprOwner)
304+
val templWithMixedinMembers = Template(templ.parents, noSelfType, addMixinConstructorDef(clazz, derivedImplClassMembers))
305+
306+
templWithMixedinMembers setSymbol exprOwner
307+
templWithMixedinMembers changeOwner (templ.symbol.owner -> clazz, templ.symbol -> exprOwner)
308+
templWithMixedinMembers
294309
}
295310

296311
def implClassDefs(trees: List[Tree]): List[Tree] = {
297312
trees collect {
298313
case cd: ClassDef if cd.symbol.needsImplClass =>
299314
val clazz = implClass(cd.symbol).initialize
300-
ClassDef(clazz, implTemplate(clazz, cd.impl))
315+
ClassDef(clazz, implTemplate(clazz, cd))
301316
}
302317
}
303318

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

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -359,7 +359,7 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
359359
}
360360

361361
log("merging: " + originalStats.mkString("\n") + "\nwith\n" + specializedStats.mkString("\n"))
362-
val res = for (s <- originalStats; stat = s.duplicate) yield {
362+
for (s <- originalStats; stat = s.duplicate) yield {
363363
log("merge: looking at " + stat)
364364
val stat1 = stat match {
365365
case Assign(sel @ Select(This(_), field), _) =>
@@ -389,9 +389,8 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
389389
} else
390390
stat1
391391
}
392-
if (specBuf.nonEmpty)
393-
println("residual specialized constructor statements: " + specBuf)
394-
res
392+
// if (specBuf.nonEmpty)
393+
// println("residual specialized constructor statements: " + specBuf)
395394
}
396395

397396
/* Add an 'if' around the statements coming after the super constructor. This
@@ -548,7 +547,9 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
548547
// Create an assignment to class field `to` with rhs `from`
549548
def mkAssign(to: Symbol, from: Tree): Tree =
550549
localTyper.typedPos(to.pos) {
551-
Assign(Select(This(clazz), to), from)
550+
val qual = Select(This(clazz), to)
551+
if (to.isSetter) Apply(qual, List(from))
552+
else Assign(qual, from)
552553
}
553554

554555
// Create code to copy parameter to parameter accessor field.
@@ -607,9 +608,9 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
607608
// it goes before the superclass constructor call, otherwise it goes after.
608609
// A lazy val's effect is not moved to the constructor, as it is delayed.
609610
// Returns `true` when a `ValDef` is needed.
610-
def moveEffectToCtor(mods: Modifiers, rhs: Tree, memoized: Boolean): Unit = {
611+
def moveEffectToCtor(mods: Modifiers, rhs: Tree, assignSym: Symbol): Unit = {
611612
val initializingRhs =
612-
if (!memoized || statSym.isLazy) EmptyTree // not memoized, or effect delayed (for lazy val)
613+
if ((assignSym eq NoSymbol) || statSym.isLazy) EmptyTree // not memoized, or effect delayed (for lazy val)
613614
else if (!mods.hasStaticFlag) intoConstructor(statSym, primaryConstr.symbol)(rhs)
614615
else rhs
615616

@@ -619,7 +620,7 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
619620
else if (mods hasFlag PRESUPER | PARAMACCESSOR) constrPrefixBuf
620621
else constrStatBuf
621622

622-
initPhase += mkAssign(statSym, initializingRhs)
623+
initPhase += mkAssign(assignSym, initializingRhs)
623624
}
624625
}
625626

@@ -632,17 +633,19 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
632633
// The primary constructor is dealt with separately (we're massaging it here).
633634
case _: DefDef if statSym.isPrimaryConstructor => ()
634635
case _: DefDef if statSym.isConstructor => auxConstructorBuf += stat
635-
case _: DefDef => defBuf += stat
636+
case stat: DefDef =>
637+
defBuf += stat
638+
636639

637640
// If a val needs a field, an empty valdef goes into the template.
638641
// Except for lazy and ConstantTyped vals, the field is initialized by an assignment in:
639642
// - the class initializer (static),
640643
// - the constructor, before the super call (early initialized or a parameter accessor),
641644
// - the constructor, after the super call (regular val).
642645
case ValDef(mods, _, _, rhs) =>
643-
if (statSym.isLazy) {
646+
if (rhs ne EmptyTree) {
644647
val emitField = memoizeValue(statSym)
645-
moveEffectToCtor(mods, rhs, emitField)
648+
moveEffectToCtor(mods, rhs, statSym)
646649
if (emitField) defBuf += deriveValDef(stat)(_ => EmptyTree)
647650
} else defBuf += stat
648651

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

Lines changed: 30 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -137,14 +137,10 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
137137
// not stored, no side-effect
138138
val pureConstant = tp.isInstanceOf[ConstantType]
139139

140-
val stored = !(pureConstant) // || isUnitType(tp))
141-
142-
143-
// println(s"fieldMemoizationIn $sym $site = $tp")
144-
def assignSym: Symbol =
145-
if (!stored) NoSymbol
146-
else if (accessorOrField hasFlag ACCESSOR) accessorOrField.setterIn(site)
147-
else accessorOrField
140+
// if !stored, may still have a side-effect
141+
// (currently not distinguished -- used to think we could drop unit-typed vals,
142+
// but the memory model cares about writes to unit-typed fields)
143+
val stored = !pureConstant // || isUnitType(tp))
148144
}
149145

150146
private def fieldTypeForGetterIn(getter: Symbol, pre: Type): Type = getter.info.finalResultType.asSeenFrom(pre, getter.owner)
@@ -262,7 +258,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
262258
afterOwnPhase { mixin.info }.decls.toList.filter(accessorImplementedInSubclass)
263259
}
264260

265-
// println(s"mixing in $accessorsMaybeNeedingImpl from ${clazz.mixinClasses}")
261+
// println(s"mixing in for $clazz: $accessorsMaybeNeedingImpl from ${clazz.mixinClasses}")
266262

267263
// TODO: setter conflicts?
268264
def accessorConflictsExistingVal(accessor: Symbol): Boolean = {
@@ -289,7 +285,6 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
289285
// a trait setter for an overridden val will receive a unit body in the tree transform
290286
if (nme.isTraitSetterName(accessor.name)) {
291287
val getter = accessor.getterIn(accessor.owner)
292-
// println(s"mixing in trait setter ${accessor.defString}: $overridden")
293288
val clone = cloneAccessor()
294289

295290
clone filterAnnotations (ai => !ai.matches(TraitSetterAnnotationClass)) // only supposed to be set in trait
@@ -372,7 +367,7 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
372367
else fieldAccess(getter)
373368
}
374369

375-
// println(s"accessorsAndFieldsNeedingTrees: $accessorsAndFieldsNeedingTrees")
370+
// println(s"accessorsAndFieldsNeedingTrees for $templateSym: $accessorsAndFieldsNeedingTrees")
376371
def setterBody(setter: Symbol): Tree = {
377372
// trait setter in trait
378373
if (clazz.isTrait) EmptyTree
@@ -393,48 +388,11 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
393388
def rhsAtOwner(stat: ValOrDefDef, newOwner: Symbol): Tree =
394389
atOwner(newOwner)(super.transform(stat.rhs.changeOwner(stat.symbol -> newOwner)))
395390

396-
private def deriveStoredAccessor(clazz: Symbol, stat: ValOrDefDef, fieldMemoization: FieldMemoization, exprOwner: Symbol): List[Tree] = {
397-
// Which symbol should own `rhs`, as the expression is lifted from `statSym` to the class's template?
398-
// Normally, the local dummy (`exprOwner`) is the owner for stats in the template (not `clazz`!)
399-
// when we get owners wrong, the following fails:
400-
// - stackoverflow in uncurry when bootstrapping
401-
// - compiling scala.util.Properties,
402-
// where some references from the impl class are still pointing to the trait interface, not the impl class
403-
// - test/files/trait-defaults/ultimate-nesting.scala,
404-
// where a nested module class is separated from its module, as well as from the `settings` reference
405-
// (this was due to changeOwner not changing a module's moduleClass's owner in addition to the module's owner)
406-
val initEffect = {
407-
// println(s"initEffect: owner $statSym --> $exprOwner (currentOwner= $currentOwner)")
408-
// println(s"initEffect for $assignSym is $rhs")
409-
410-
val lifted = rhsAtOwner(stat, exprOwner)
411-
import fieldMemoization.assignSym
412-
413-
if (assignSym eq NoSymbol) lifted
414-
else localTyper.typedPos(stat.pos) {
415-
val qual = Select(This(clazz), assignSym)
416-
if (assignSym.isSetter) Apply(qual, List(lifted))
417-
else Assign(qual, lifted)
418-
}
419-
}
420-
421-
// DefDef(computeSym, rhsAtOwner(stat, computeSym))
422-
// def deriveRhs(oldRhs: Tree) = localTyper.typedPos(oldRhs.pos)(Apply(gen.mkAttributedRef(computeSym), Nil))
423-
424-
def deriveRhs(oldRhs: Tree) = EmptyTree
425-
val derivedStat = stat match {
426-
case dd: DefDef => deriveDefDef(stat)(deriveRhs)
427-
case vd: ValDef => deriveValDef(stat)(deriveRhs)
428-
}
429-
430-
derivedStat :: initEffect :: Nil
431-
}
432-
433391
private def transformStat(exprOwner: Symbol)(stat: Tree): List[Tree] = {
434392
val clazz = currentOwner
435393
val statSym = stat.symbol
436394

437-
// println(s"transformStat $statSym in ${exprOwner.ownerChain}")
395+
// println(s"transformStat $statSym in ${exprOwner.ownerChain}")
438396
// currentRun.trackerFactory.snapshot()
439397

440398
/*
@@ -452,29 +410,38 @@ abstract class Fields extends InfoTransform with ast.TreeDSL with TypingTransfor
452410
*/
453411
stat match {
454412
// TODO: consolidate with ValDef case
455-
case stat@DefDef(_, _, _, _, _, rhs) if (rhs ne EmptyTree) && (statSym hasFlag ACCESSOR) && !excludedAccessorOrFieldByFlags(statSym) =>
413+
case stat@DefDef(_, _, _, _, _, rhs) if (statSym hasFlag ACCESSOR) && !excludedAccessorOrFieldByFlags(statSym) =>
456414
/* TODO: defer replacing ConstantTyped tree by the corresponding constant until erasure
457-
(until then, trees should not be constant-folded -- only their type tracks the resulting constant) */
415+
(until then, trees should not be constant-folded -- only their type tracks the resulting constant)
416+
TODO: also remove ACCESSOR flag since there won't be an underlying field to access?
417+
*/
458418
def statInlinedConstantRhs =
459419
if (clazz.isTrait) stat // we've already done this for traits.. the asymmetry will be solved by the above todo
460420
else deriveDefDef(stat)(_ => gen.mkAttributedQualifier(rhs.tpe))
461421

462-
val fieldMemoization = fieldMemoizationIn(statSym, clazz)
422+
if (rhs ne EmptyTree) {
423+
val fieldMemoization = fieldMemoizationIn(statSym, clazz)
463424

464-
// if we decide to have non-stored fields with initialization effects, the stat's RHS should be replaced by unit
465-
// if (!fieldMemoization.stored) deriveUnitDef(stat) else stat
425+
// if we decide to have non-stored fields with initialization effects, the stat's RHS should be replaced by unit
426+
// if (!fieldMemoization.stored) deriveUnitDef(stat) else stat
466427

467-
if (fieldMemoization.pureConstant) statInlinedConstantRhs :: Nil
468-
else if (clazz.isTrait) deriveStoredAccessor(clazz, stat, fieldMemoization, exprOwner)
469-
else stat :: Nil
428+
if (fieldMemoization.pureConstant) statInlinedConstantRhs :: Nil
429+
else super.transform(stat) :: Nil
430+
} else {
431+
stat :: Nil
432+
}
470433

471-
case stat@ValDef(mods, _, _, rhs) if (rhs ne EmptyTree) && !excludedAccessorOrFieldByFlags(statSym) =>
472-
val fieldMemoization = fieldMemoizationIn(statSym, clazz)
434+
case stat@ValDef(mods, _, _, rhs) if !excludedAccessorOrFieldByFlags(statSym) =>
435+
if (rhs ne EmptyTree) {
436+
val fieldMemoization = fieldMemoizationIn(statSym, clazz)
473437

474-
// drop the val for (a) constant (pure & not-stored) and (b) not-stored (but still effectful) fields
475-
if (fieldMemoization.pureConstant) Nil // (a)
476-
else if (fieldMemoization.stored) deriveStoredAccessor(clazz, stat, fieldMemoization, exprOwner)
477-
else rhsAtOwner(stat, exprOwner) :: Nil // (b) -- not used currently
438+
// drop the val for (a) constant (pure & not-stored) and (b) not-stored (but still effectful) fields
439+
if (fieldMemoization.pureConstant) Nil // (a)
440+
else super.transform(stat) :: Nil // if (fieldMemoization.stored)
441+
// else rhsAtOwner(transformStat, exprOwner) :: Nil // (b) -- not used currently
442+
} else {
443+
stat :: Nil
444+
}
478445

479446
case tree => List (
480447
if (exprOwner != currentOwner && tree.isTerm) atOwner(exprOwner)(super.transform(tree))

src/reflect/scala/reflect/internal/Types.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2496,6 +2496,8 @@ trait Types
24962496
override def isJava = true
24972497
}
24982498

2499+
// TODO: rename so it's more appropriate for the type that is for a method without argument lists
2500+
// ("nullary" erroneously implies it has an argument list with zero arguments, it actually has zero argument lists)
24992501
case class NullaryMethodType(override val resultType: Type) extends Type with NullaryMethodTypeApi {
25002502
override def isTrivial = resultType.isTrivial && (resultType eq resultType.withoutAnnotations)
25012503
override def prefix: Type = resultType.prefix

0 commit comments

Comments
 (0)