Skip to content

Commit 921f321

Browse files
committed
dotty-style trait fields
1 parent d6db9dd commit 921f321

File tree

3 files changed

+132
-60
lines changed

3 files changed

+132
-60
lines changed

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

Lines changed: 68 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
2828
/** the following two members override abstract members in Transform */
2929
val phaseName: String = "constructors"
3030

31+
// using lateDEFERRED for notDEFERRED for my WIP
32+
// (a lateDEFERRED trait member should not be considered deferred during refchecks,
33+
// as it's automatically implemented in any class inheriting the trait,
34+
// but it should be abstract in bytecode)
35+
override def phaseNewFlags: Long = lateDEFERRED
36+
3137
protected def newTransformer(unit: CompilationUnit): Transformer =
3238
new ConstructorTransformer(unit)
3339

@@ -215,7 +221,10 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
215221
detectUsages walk auxConstructorBuf
216222
}
217223
}
218-
def mustBeKept(sym: Symbol) = !omittables(sym)
224+
def mustBeKept(sym: Symbol) = !omittable(sym)
225+
// omit unused outers and non-memoized fields (I think we're no longer creating symbols for fields in traits, but still including backstop for now)
226+
def omittable(sym: Symbol) = (omittables(sym) ||
227+
(sym.isValue && !sym.isMethod && (!mustMemoize(sym) || sym.owner.isTrait))) // TODO: assert(!sym.owner.isTrait, sym.ownerChain)
219228

220229
} // OmittablesHelper
221230

@@ -577,9 +586,15 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
577586
else transform(tree.changeOwner(oldowner -> primaryConstr.symbol))
578587
}
579588

589+
590+
// TODO: we probably can't emit an Assign tree after typers, need to Apply the setter
580591
// Create an assignment to class field `to` with rhs `from`
581592
def mkAssign(to: Symbol, from: Tree): Tree =
582-
localTyper.typedPos(to.pos) { Assign(Select(This(clazz), to), from) }
593+
localTyper.typedPos(to.pos) {
594+
val qual = Select(This(clazz), to)
595+
if (to.isSetter) Apply(qual, List(from))
596+
else Assign(qual, from)
597+
}
583598

584599
// Create code to copy parameter to parameter accessor field.
585600
// If parameter is $outer, check that it is not null so that we NPE
@@ -630,46 +645,76 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
630645
}
631646
}
632647

633-
634648
for (stat <- stats) {
635649
val statSym = stat.symbol
636650

651+
// Move the RHS of a Val/Def (traits don't have vals, their getter holds the RHS) to the appropriate part of the ctor.
652+
// If the val def is an early initialized or a parameter accessor,
653+
// it goes before the superclass constructor call, otherwise it goes after.
654+
// A lazy val's effect is not moved to the constructor, as it is delayed.
655+
def moveEffectToCtor(mods: Modifiers, rhs: Tree) = {
656+
val initializingRhs =
657+
if (statSym.isLazy || rhs.isInstanceOf[Literal]) EmptyTree
658+
else if (!mods.hasStaticFlag) intoConstructor(statSym, rhs)
659+
else rhs
660+
661+
if (initializingRhs ne EmptyTree) {
662+
val initPhase =
663+
if (mods hasFlag STATIC) classInitStatBuf
664+
else if (mods hasFlag PRESUPER | PARAMACCESSOR) constrPrefixBuf
665+
else constrStatBuf
666+
667+
val memoized = mustMemoize(statSym)
668+
669+
val effect =
670+
if (memoized) {
671+
val assignSym =
672+
if (statSym.isGetter) statSym.asInstanceOf[MethodSymbol].referenced //setterIn(clazz, hasExpandedName = false) // not yet expanded -- see Mixin
673+
else statSym
674+
675+
assert(assignSym ne NoSymbol, stat)
676+
mkAssign(assignSym, initializingRhs)
677+
} else initializingRhs
678+
679+
initPhase += effect
680+
memoized
681+
} else false
682+
}
683+
637684
stat match {
638685
// recurse on class definition, store in defBuf
639686
case _: ClassDef => defBuf += new ConstructorTransformer(unit).transform(stat)
640687

641-
case _: DefDef if inTrait && statSym.isAccessor => defBuf += deriveDefDef(dd)(_ => EmptyTree)
688+
// a concrete trait getter's RHS is treated like a ValDef
689+
// (the actual field isn't emitted until Mixin augments the class that inherits the trait)
690+
case DefDef(mods, _, _, _, _, rhs) if inTrait && statSym.isAccessor =>
691+
defBuf +=
692+
if (statSym.isGetter && (rhs ne EmptyTree)) {
693+
val memoized = moveEffectToCtor(mods, rhs)
694+
if (memoized) {
695+
statSym setFlag (DEFERRED | lateDEFERRED)
696+
deriveDefDef(dd)(_ => EmptyTree)
697+
} else stat
698+
} else {
699+
if (statSym.isSetter) statSym setFlag (DEFERRED | lateDEFERRED)
700+
stat
701+
}
642702

643703
// methods (except primary constructor) go into template
644704
// (non-primary ctors --> auxConstructorBuf / regular defs --> defBuf)
645705
case _: DefDef if statSym.isPrimaryConstructor => ()
646706
case _: DefDef if statSym.isConstructor => auxConstructorBuf += stat
647707
case _: DefDef => defBuf += stat
648708

649-
// val defs with constant right-hand sides are eliminated.
650-
case _: ValDef if statSym.info.isInstanceOf[ConstantType] => ()
651-
652-
// For all other val defs, an empty valdef goes into the template.
653-
// Additionally, non-lazy vals are initialized by an assignment in:
709+
// If a val needs a field, an empty valdef goes into the template.
710+
// Except for lazy and ConstantTyped vals, the field is initialized by an assignment in:
654711
// - the class initializer (static),
655712
// - the constructor, before the super call (early initialized or a parameter accessor),
656713
// - the constructor, after the super call (regular val).
657714
case ValDef(mods, _, _, rhs) =>
658-
val initializingRhs =
659-
if (statSym.isLazy) EmptyTree
660-
else if (!mods.hasStaticFlag) intoConstructor(statSym, rhs)
661-
else rhs
662-
663-
if (initializingRhs ne EmptyTree) {
664-
val initPhase =
665-
if (mods hasFlag STATIC) classInitStatBuf
666-
else if (mods hasFlag PRESUPER | PARAMACCESSOR) constrPrefixBuf
667-
else constrStatBuf
668-
669-
initPhase += mkAssign(statSym, initializingRhs)
670-
}
715+
val memoized = moveEffectToCtor(mods, rhs)
671716

672-
defBuf += deriveValDef(stat)(_ => EmptyTree)
717+
if (memoized) defBuf += deriveValDef(stat)(_ => EmptyTree)
673718

674719
// all other statements go into the constructor
675720
case _ => constrStatBuf += intoConstructor(impl.symbol, stat)

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

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

137+
// TODO: why is all this symbol creation disconnected from addDerivedTrees,
138+
// which creates another list of Field/Getter/Setter factories???
137139
def enterGetterSetter(tree: ValDef) {
138140
val ValDef(mods, name, _, _) = tree
139141
if (nme.isSetterName(name))
@@ -149,12 +151,15 @@ trait MethodSynthesis {
149151
val getter = Getter(tree)
150152
val getterSym = getter.createAndEnterSymbol()
151153
// Create the setter if necessary.
152-
if (mods.isMutable)
153-
Setter(tree).createAndEnterSymbol()
154+
if (getter.needsSetter) {
155+
val setterSym = Setter(tree).createAndEnterSymbol()
156+
getterSym.referenced = setterSym
157+
setterSym.referenced = getterSym
158+
}
154159

155160
// Create a field if the getter requires storage, otherwise,
156161
// the getter's abstract and the tree gets the getter's symbol.
157-
if (getter.noFieldNeeded) getterSym setPos tree.pos
162+
if (getter.noFieldHere) getterSym setPos tree.pos
158163
else enterStrictVal(tree)
159164
}
160165

@@ -224,9 +229,12 @@ trait MethodSynthesis {
224229
}
225230

226231
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))
232+
if (vd.mods.isLazy) List(LazyValGetter(vd))
233+
else {
234+
val getter = Getter(vd)
235+
if (getter.needsSetter) List(getter, Setter(vd))
236+
else List(getter)
237+
}
230238

231239
def beanAccessors(vd: ValDef): List[DerivedFromValDef] = {
232240
val setter = if (vd.mods.isMutable) List(BeanSetter(vd)) else Nil
@@ -237,10 +245,6 @@ trait MethodSynthesis {
237245
else Nil
238246
}
239247

240-
// Take into account annotations so that we keep annotated unit lazy val
241-
// to get better error message already from the cps plugin itself
242-
def hasUnitType(sym: Symbol) = (sym.tpe.typeSymbol == UnitClass) && sym.tpe.annotations.isEmpty
243-
244248
/** This trait assembles what's needed for synthesizing derived methods.
245249
* Important: Typically, instances of this trait are created TWICE for each derived
246250
* symbol; once form Namers in an enter method, and once from Typers in addDerivedTrees.
@@ -257,7 +261,6 @@ trait MethodSynthesis {
257261
def name: TermName
258262

259263
/** The flags that are retained from the original symbol */
260-
261264
def flagsMask: Long
262265

263266
/** The flags that the derived symbol has in addition to those retained from
@@ -269,7 +272,10 @@ trait MethodSynthesis {
269272
def completer(sym: Symbol): Type
270273

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

275281
/** The definition tree of the derived symbol. */
@@ -281,8 +287,9 @@ trait MethodSynthesis {
281287
def enclClass: Symbol
282288

283289
// Final methods to make the rest easier to reason about.
284-
final def mods = tree.mods
285-
final def basisSym = tree.symbol
290+
final def mods = tree.mods
291+
final def basisSym = tree.symbol
292+
final def derivedMods = mods & flagsMask | flagsExtra
286293
}
287294

288295
sealed trait DerivedFromClassDef extends DerivedFromMemberDef {
@@ -302,19 +309,27 @@ trait MethodSynthesis {
302309
/* Explicit isSetter required for bean setters (beanSetterSym.isSetter is false) */
303310
final def completer(sym: Symbol) = namerOf(sym).accessorTypeCompleter(tree, isSetter)
304311
final def fieldSelection = Select(This(enclClass), basisSym)
305-
final def derivedMods: Modifiers = mods & flagsMask | flagsExtra mapAnnotations (_ => Nil)
306312

307313
def derivedSym: Symbol = tree.symbol
308314
def derivedTree: Tree = EmptyTree
309315

310-
def noFieldNeeded = isDeferred || (mods.isLazy && hasUnitType(basisSym))
316+
// No field for these vals:
317+
// - abstract vals have no value we could store (until they become concrete, potentially)
318+
// - a concrete val with a statically known value (Unit / ConstantType)
319+
// performs its side effect according to lazy/strict semantics, but doesn't need to store its value
320+
// each access will "evaluate" the RHS (a literal) again
321+
// - concrete vals in traits don't yield a field here either (their getter's RHS has the initial value)
322+
// Constructors will move the assignment to the constructor, abstracting over the field using the field setter,
323+
// and Mixin will add a field to the class that mixes in the trait, implementing the accessors in terms of it
324+
def noFieldHere = nothingToMemoize || owner.isTrait
325+
protected def nothingToMemoize = isDeferred // TODO: we don't know the val's type at this point... || typeNeedsNoStorage(???)
311326

312327
def isSetter = false
313328
def isDeferred = mods.isDeferred
314329
def keepClean = false // whether annotations whose definitions are not meta-annotated should be kept.
315330
def validate() { }
316-
def createAndEnterSymbol(): Symbol = {
317-
val sym = owner.newMethod(name, tree.pos.focus, (tree.mods.flags & flagsMask) | flagsExtra)
331+
def createAndEnterSymbol(): MethodSymbol = {
332+
val sym = owner.newMethod(name, tree.pos.focus, derivedMods.flags)
318333
setPrivateWithin(tree, sym)
319334
enterInScope(sym)
320335
sym setInfo completer(sym)
@@ -332,16 +347,24 @@ trait MethodSynthesis {
332347
}
333348
}
334349
sealed trait DerivedGetter extends DerivedFromValDef {
335-
// TODO
350+
// A getter obviously must be accompanied by a setter if the ValDef is mutable.
351+
// We also need a setter for any val/var defined in a trait,
352+
// since an interface in Java can't define a field,
353+
// so we shall set the initial value indirectly in the trait's init method using the trait setter,
354+
// which will be implemented in the class
355+
def needsSetter = mods.isMutable || (owner.isTrait && !nothingToMemoize)
336356
}
337357
sealed trait DerivedSetter extends DerivedFromValDef {
338358
override def isSetter = true
339359
private def setterParam = derivedSym.paramss match {
340360
case (p :: Nil) :: _ => p
341361
case _ => NoSymbol
342362
}
363+
// TODO: when is `derivedSym.isOverloaded`??? is it always an error?
364+
// `noFieldHere` does not imply DEFERRED (ask isDeferred if you must know)
365+
// we automatically implement trait setters (where !isDeferred) in the subclass
343366
private def setterRhs =
344-
if (isDeferred || derivedSym.isOverloaded) EmptyTree
367+
if (noFieldHere || derivedSym.isOverloaded) EmptyTree
345368
else Assign(fieldSelection, Ident(setterParam))
346369

347370
private def setterDef = DefDef(derivedSym, setterRhs)
@@ -362,8 +385,7 @@ trait MethodSynthesis {
362385
context.error(tree.pos, s"Internal error: Unable to find the synthetic factory method corresponding to implicit class $name in $enclClass / ${enclClass.info.decls}")
363386
result
364387
}
365-
def derivedTree: DefDef =
366-
factoryMeth(mods & flagsMask | flagsExtra, name, tree)
388+
def derivedTree: DefDef = factoryMeth(derivedMods, name, tree)
367389
def flagsExtra: Long = METHOD | IMPLICIT | SYNTHETIC
368390
def flagsMask: Long = AccessFlags
369391
def name: TermName = tree.name.toTermName
@@ -385,7 +407,8 @@ trait MethodSynthesis {
385407
}
386408
case class Getter(tree: ValDef) extends BaseGetter(tree) {
387409
override def derivedSym = if (isDeferred) basisSym else basisSym.getterIn(enclClass)
388-
private def derivedRhs = if (isDeferred) EmptyTree else fieldSelection
410+
private def derivedRhs = if (noFieldHere) tree.rhs else fieldSelection
411+
389412
private def derivedTpt = {
390413
// For existentials, don't specify a type for the getter, even one derived
391414
// from the symbol! This leads to incompatible existentials for the field and
@@ -406,12 +429,14 @@ trait MethodSynthesis {
406429
}
407430
override def derivedTree: DefDef = newDefDef(derivedSym, derivedRhs)(tpt = derivedTpt)
408431
}
432+
409433
/** Implements lazy value accessors:
410-
* - for lazy values of type Unit and all lazy fields inside traits,
411-
* the rhs is the initializer itself
412-
* - for all other lazy values z the accessor is a block of this form:
413-
* { z = <rhs>; z } where z can be an identifier or a field.
414-
*/
434+
* - for lazy values of type Unit and all lazy fields inside traits,
435+
* the rhs is the initializer itself, because we'll just "compute" the result on every access
436+
* ("computing" unit / constant type is free -- the side-effect is still only run once, using the init bitmap)
437+
* - for all other lazy values z the accessor is a block of this form:
438+
* { z = <rhs>; z } where z can be an identifier or a field.
439+
*/
415440
case class LazyValGetter(tree: ValDef) extends BaseGetter(tree) {
416441
class ChangeOwnerAndModuleClassTraverser(oldowner: Symbol, newowner: Symbol)
417442
extends ChangeOwnerTraverser(oldowner, newowner) {
@@ -431,7 +456,7 @@ trait MethodSynthesis {
431456
override def derivedTree: DefDef = {
432457
val ValDef(_, _, tpt0, rhs0) = tree
433458
val rhs1 = context.unit.transformed.getOrElse(rhs0, rhs0)
434-
val body = if (noFieldNeeded) rhs1
459+
val body = if (noFieldHere) rhs1
435460
else gen.mkAssignAndReturn(basisSym, rhs1)
436461

437462
derivedSym setPos tree.pos // cannot set it at createAndEnterSymbol because basisSym can possibly still have NoPosition
@@ -450,7 +475,7 @@ trait MethodSynthesis {
450475
def flagsMask = SetterFlags
451476
def flagsExtra = ACCESSOR
452477

453-
override def derivedSym = basisSym.setterIn(enclClass)
478+
override def derivedSym = basisSym.setterIn(enclClass, hasExpandedName = false)
454479
}
455480
case class Field(tree: ValDef) extends DerivedFromValDef {
456481
def name = tree.localName
@@ -461,12 +486,8 @@ trait MethodSynthesis {
461486
// generated for a class parameter (PARAMACCESSOR).
462487
override def keepClean = !mods.isParamAccessor
463488

464-
override def derivedSym =
465-
if (noFieldNeeded) NoSymbol
466-
else super.derivedSym
467-
468489
override def derivedTree = (
469-
if (derivedSym eq NoSymbol) EmptyTree
490+
if (noFieldHere) EmptyTree
470491
else if (mods.isLazy) copyValDef(tree)(mods = mods | flagsExtra, name = this.name, rhs = EmptyTree).setPos(tree.pos.focus)
471492
else copyValDef(tree)(mods = mods | flagsExtra, name = this.name)
472493
)
@@ -504,12 +525,12 @@ trait MethodSynthesis {
504525
// Derives a tree without attempting to use the original tree's symbol.
505526
override def derivedTree = {
506527
atPos(tree.pos.focus) {
507-
DefDef(derivedMods, name, Nil, ListOfNil, tree.tpt.duplicate,
528+
DefDef(derivedMods mapAnnotations (_ => Nil), name, Nil, ListOfNil, tree.tpt.duplicate,
508529
if (isDeferred) EmptyTree else Select(This(owner), tree.name)
509530
)
510531
}
511532
}
512-
override def createAndEnterSymbol(): Symbol = enterSyntheticSym(derivedTree)
533+
override def createAndEnterSymbol(): MethodSymbol = enterSyntheticSym(derivedTree).asInstanceOf[MethodSymbol]
513534
}
514535
case class BooleanBeanGetter(tree: ValDef) extends BeanAccessor("is") with AnyBeanGetter { }
515536
case class BeanGetter(tree: ValDef) extends BeanAccessor("get") with AnyBeanGetter { }

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,12 @@ trait Definitions extends api.StandardDefinitions {
233233
|| tp =:= AnyRefTpe
234234
)
235235

236+
// Take into account annotations so that we keep annotated unit lazy val
237+
// to get better error message already from the cps plugin itself
238+
def isUnitType(tp: Type) = tp.typeSymbol == UnitClass && tp.annotations.isEmpty // you never know with annotations...
239+
def memoizedType(tp: Type) = !(tp.isInstanceOf[ConstantType] || isUnitType(tp))
240+
def mustMemoize(sym: Symbol): Boolean = memoizedType(enteringPhase(erasurePhase)(sym.info).resultType)
241+
236242
def hasMultipleNonImplicitParamLists(member: Symbol): Boolean = hasMultipleNonImplicitParamLists(member.info)
237243
def hasMultipleNonImplicitParamLists(info: Type): Boolean = info match {
238244
case PolyType(_, restpe) => hasMultipleNonImplicitParamLists(restpe)

0 commit comments

Comments
 (0)