Skip to content

Commit f67aff0

Browse files
committed
WIP: dotty-style trait fields
namers wip
1 parent fd1a1c3 commit f67aff0

File tree

2 files changed

+56
-24
lines changed

2 files changed

+56
-24
lines changed

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

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -597,21 +597,32 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
597597
def intoConstructor(oldowner: Symbol, tree: Tree) =
598598
intoConstructorTransformer transform tree.changeOwner(oldowner -> constr.symbol)
599599

600-
private def assignmentIntoCtor(assignSym: Symbol, stat: ValOrDefDef): Unit = {
601-
val rhs1 = intoConstructor(assignSym, stat.rhs)
600+
private def assignmentIntoCtor(stat: ValOrDefDef): Unit = {
601+
val rhs1 = intoConstructor(stat.symbol, stat.rhs)
602602

603603
// Should tree be moved in front of super constructor call?
604604
// (I.e., is it derived from an early definition?)
605605
val constrBufPart =
606606
if (stat.mods hasFlag PRESUPER | PARAMACCESSOR) constrPrefixBuf
607607
else constrStatBuf
608608

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
612+
613+
assert(assignSym ne NoSymbol, stat)
614+
609615
constrBufPart += mkAssign(assignSym, rhs1)
610616
}
611617

618+
// TODO: we probably can't emit an Assign tree after typers, need to Apply the setter
612619
// Create an assignment to class field `to` with rhs `from`
613620
def mkAssign(to: Symbol, from: Tree): Tree =
614-
localTyper.typedPos(to.pos) { Assign(Select(This(clazz), to), from) }
621+
localTyper.typedPos(to.pos) {
622+
val qual = Select(This(clazz), to)
623+
if (to.isSetter) Apply(qual, List(from))
624+
else Assign(qual, from)
625+
}
615626

616627
// Create code to copy parameter to parameter accessor field.
617628
// If parameter is $outer, check that it is not null so that we NPE
@@ -665,6 +676,12 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
665676
// Triage all template definitions to go into defBuf/auxConstructorBuf, constrStatBuf, or constrPrefixBuf.
666677
for (stat <- stats) stat match {
667678
case dd@ DefDef(_,_,_,_,_,rhs) =>
679+
// We can't mark as deferred from the start, as it will be implemented automatically in the subclass
680+
// it should be marked deferred in bytecode, though
681+
if (inTrait && stat.symbol.isAccessor) {
682+
stat.symbol setFlag DEFERRED
683+
}
684+
668685
// methods with constant result type get literals as their body
669686
// all methods except the primary constructor go into template
670687
stat.symbol.tpe match {
@@ -673,13 +690,14 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
673690
case _ =>
674691
if (stat.symbol.isPrimaryConstructor) ()
675692
else if (stat.symbol.isConstructor) auxConstructorBuf += stat
676-
else if (inTrait && stat.symbol.isAccessor) {
677-
assignmentIntoCtor(stat)
693+
// 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)
694+
else if (inTrait && stat.symbol.isGetter && (rhs ne EmptyTree)) {
695+
assignmentIntoCtor(dd)
678696
defBuf += deriveDefDef(dd)(_ => EmptyTree)
679697
}
680698
else defBuf += stat
681699
}
682-
case ValDef(mods, _, _, rhs) if !mods.hasStaticFlag =>
700+
case vd@ ValDef(mods, _, _, rhs) if !mods.hasStaticFlag =>
683701
// val defs with constant right-hand sides are eliminated.
684702
// for all other val defs, an empty valdef goes into the template and
685703
// the initializer goes as an assignment into the constructor
@@ -688,7 +706,7 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
688706
// Lazy vals don't get the assignment in the constructor.
689707
if (!stat.symbol.tpe.isInstanceOf[ConstantType]) { // TODO: MethodSynthesis should no longer emit ValDefs with ConstantTypes
690708
if (rhs != EmptyTree && !stat.symbol.isLazy) {
691-
assignmentIntoCtor(stat.symbol, stat)
709+
assignmentIntoCtor(vd)
692710
}
693711
defBuf += deriveValDef(stat)(_ => EmptyTree)
694712
}

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

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ trait MethodSynthesis {
134134
ImplicitClassWrapper(tree).createAndEnterSymbol()
135135
}
136136

137+
// TODO: see standardAccessors
137138
def enterGetterSetter(tree: ValDef) {
138139
val ValDef(mods, name, _, _) = tree
139140
if (nme.isSetterName(name))
@@ -149,8 +150,11 @@ trait MethodSynthesis {
149150
val getter = Getter(tree)
150151
val getterSym = getter.createAndEnterSymbol()
151152
// Create the setter if necessary.
152-
if (getter.needsSetter)
153-
Setter(tree).createAndEnterSymbol()
153+
if (getter.needsSetter) {
154+
val setterSym = Setter(tree).createAndEnterSymbol()
155+
getterSym.referenced = setterSym
156+
setterSym.referenced = getterSym
157+
}
154158

155159
// Create a field if the getter requires storage, otherwise,
156160
// the getter's abstract and the tree gets the getter's symbol.
@@ -224,9 +228,12 @@ trait MethodSynthesis {
224228
}
225229

226230
def standardAccessors(vd: ValDef): List[DerivedFromValDef] =
227-
if (vd.mods.isMutable && !vd.mods.isLazy) List(Getter(vd), Setter(vd))
228-
else if (vd.mods.isLazy) List(LazyValGetter(vd))
229-
else List(Getter(vd))
231+
if (vd.mods.isLazy) List(LazyValGetter(vd))
232+
else {
233+
val getter = Getter(vd)
234+
if (getter.needsSetter) List(getter, Setter(vd))
235+
else List(getter)
236+
}
230237

231238
def beanAccessors(vd: ValDef): List[DerivedFromValDef] = {
232239
val setter = if (vd.mods.isMutable) List(BeanSetter(vd)) else Nil
@@ -264,7 +271,10 @@ trait MethodSynthesis {
264271
def completer(sym: Symbol): Type
265272

266273
/** The derived symbol. It is assumed that this symbol already exists and has been
267-
* entered in the parent scope when derivedSym is called */
274+
* entered in the parent scope when derivedSym is called
275+
* this derived symbol business is super shady -- we're creating them,
276+
* so why do we need to look them up with setterIn/getterIn??
277+
*/
268278
def derivedSym: Symbol
269279

270280
/** The definition tree of the derived symbol. */
@@ -310,14 +320,14 @@ trait MethodSynthesis {
310320
// - concrete vals in traits don't yield a field here either (their getter's RHS has the initial value)
311321
// Constructors will move the assignment to the constructor, abstracting over the field using the field setter,
312322
// and Mixin will add a field to the class that mixes in the trait, implementing the accessors in terms of it
313-
def noFieldHere = nothingToMemoize || derivedSym.owner.isTrait
314-
protected def nothingToMemoize = isDeferred || typeNeedsNoStorage(basisSym.tpe)
323+
def noFieldHere = nothingToMemoize || owner.isTrait
324+
protected def nothingToMemoize = isDeferred || typeNeedsNoStorage(basisSym.tpe.resultType)
315325

316326
def isSetter = false
317327
def isDeferred = mods.isDeferred
318328
def keepClean = false // whether annotations whose definitions are not meta-annotated should be kept.
319329
def validate() { }
320-
def createAndEnterSymbol(): Symbol = {
330+
def createAndEnterSymbol(): MethodSymbol = {
321331
val sym = owner.newMethod(name, tree.pos.focus, derivedMods.flags)
322332
setPrivateWithin(tree, sym)
323333
enterInScope(sym)
@@ -336,14 +346,22 @@ trait MethodSynthesis {
336346
}
337347
}
338348
sealed trait DerivedGetter extends DerivedFromValDef {
339-
def needsSetter = mods.isMutable || (derivedSym.owner.isTrait && !nothingToMemoize)
349+
// A getter obviously must be accompanied by a setter if the ValDef is mutable.
350+
// We also need a setter for any val/var defined in a trait,
351+
// since an interface in Java can't define a field,
352+
// so we shall set the initial value indirectly in the trait's init method using the trait setter,
353+
// which will be implemented in the class
354+
def needsSetter = mods.isMutable || (owner.isTrait && !nothingToMemoize)
340355
}
341356
sealed trait DerivedSetter extends DerivedFromValDef {
342357
override def isSetter = true
343358
private def setterParam = derivedSym.paramss match {
344359
case (p :: Nil) :: _ => p
345360
case _ => NoSymbol
346361
}
362+
// TODO: when is `derivedSym.isOverloaded`??? is it always an error?
363+
// `noFieldHere` does not imply DEFERRED (ask isDeferred if you must know)
364+
// we automatically implement trait setters (where !isDeferred) in the subclass
347365
private def setterRhs =
348366
if (noFieldHere || derivedSym.isOverloaded) EmptyTree
349367
else Assign(fieldSelection, Ident(setterParam))
@@ -456,7 +474,7 @@ trait MethodSynthesis {
456474
def flagsMask = SetterFlags
457475
def flagsExtra = ACCESSOR
458476

459-
override def derivedSym = basisSym.setterIn(enclClass)
477+
override def derivedSym = basisSym.setterIn(enclClass, hasExpandedName = false)
460478
}
461479
case class Field(tree: ValDef) extends DerivedFromValDef {
462480
def name = tree.localName
@@ -467,12 +485,8 @@ trait MethodSynthesis {
467485
// generated for a class parameter (PARAMACCESSOR).
468486
override def keepClean = !mods.isParamAccessor
469487

470-
override def derivedSym =
471-
if (noFieldHere) NoSymbol
472-
else super.derivedSym
473-
474488
override def derivedTree = (
475-
if (derivedSym eq NoSymbol) EmptyTree
489+
if (noFieldHere) EmptyTree
476490
else if (mods.isLazy) copyValDef(tree)(mods = mods | flagsExtra, name = this.name, rhs = EmptyTree).setPos(tree.pos.focus)
477491
else copyValDef(tree)(mods = mods | flagsExtra, name = this.name)
478492
)
@@ -515,7 +529,7 @@ trait MethodSynthesis {
515529
)
516530
}
517531
}
518-
override def createAndEnterSymbol(): Symbol = enterSyntheticSym(derivedTree)
532+
override def createAndEnterSymbol(): MethodSymbol = enterSyntheticSym(derivedTree).asInstanceOf[MethodSymbol]
519533
}
520534
case class BooleanBeanGetter(tree: ValDef) extends BeanAccessor("is") with AnyBeanGetter { }
521535
case class BeanGetter(tree: ValDef) extends BeanAccessor("get") with AnyBeanGetter { }

0 commit comments

Comments
 (0)