Skip to content

Commit 083f0ea

Browse files
committed
fields phase
1 parent 99076df commit 083f0ea

40 files changed

+800
-250
lines changed

src/compiler/scala/tools/nsc/Global.scala

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -461,10 +461,18 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
461461
val runsRightAfter = None
462462
} with ExtensionMethods
463463

464+
// phaseName = "fields"
465+
object fields extends {
466+
val global: Global.this.type = Global.this
467+
// somewhere between typers, before erasure (so we get bridges for getters) and pickler (separate compilation of trait with fields and subclass)
468+
val runsAfter = List("extmethods")
469+
val runsRightAfter = None
470+
} with Fields
471+
464472
// phaseName = "pickler"
465473
object pickler extends {
466474
val global: Global.this.type = Global.this
467-
val runsAfter = List("extmethods")
475+
val runsAfter = List("fields")
468476
val runsRightAfter = None
469477
} with Pickler
470478

@@ -608,7 +616,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
608616
* This implementation creates a description map at the same time.
609617
*/
610618
protected def computeInternalPhases(): Unit = {
611-
// Note: this fits -Xshow-phases into 80 column width, which it is
619+
// Note: this fits -Xshow-phases into 80 column width, which is
612620
// desirable to preserve.
613621
val phs = List(
614622
syntaxAnalyzer -> "parse source into ASTs, perform simple desugaring",
@@ -618,6 +626,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
618626
patmat -> "translate match expressions",
619627
superAccessors -> "add super accessors in traits and nested classes",
620628
extensionMethods -> "add extension methods for inline classes",
629+
fields -> "synthesize accessors and fields",
621630
pickler -> "serialize symbol tables",
622631
refChecks -> "reference/override checking, translate nested objects",
623632
uncurry -> "uncurry, translate function values to anonymous classes",

src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
162162

163163
def enclosingMethod(sym: Symbol): Option[Symbol] = {
164164
if (sym.isClass || sym == NoSymbol) None
165-
else if (sym.isMethod) {
165+
else if (sym.isMethod && !sym.isGetter) {
166166
if (doesNotExist(sym)) None else Some(sym)
167167
}
168168
else enclosingMethod(nextEnclosing(sym))

src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -665,7 +665,7 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
665665
(((sym.rawflags & symtab.Flags.FINAL) != 0) || isTopLevelModuleClass(sym))
666666
&& !sym.enclClass.isInterface
667667
&& !sym.isClassConstructor
668-
&& !sym.isMutable // lazy vals and vars both
668+
&& (!sym.isMutable || nme.isTraitSetterName(sym.name)) // lazy vals and vars and their setters cannot be final, but trait setters are
669669
)
670670

671671
// Primitives are "abstract final" to prohibit instantiation

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

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure =>
5858
def needsImplMethod(sym: Symbol) = (
5959
sym.isMethod
6060
&& isInterfaceMember(sym)
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))
6164
&& (!sym.hasFlag(DEFERRED | SUPERACCESSOR) || (sym hasFlag lateDEFERRED))
6265
)
6366

@@ -243,13 +246,25 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure =>
243246
}
244247

245248
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]
246250

247-
private def deriveMemberForImplClass(tree: Tree): Tree =
248-
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
249262
else tree
250263

251264
private def deriveMemberForInterface(tree: Tree): Tree =
252-
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
253268
else EmptyTree
254269

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

285-
private def implTemplate(clazz: Symbol, templ: Template): Template = atPos(templ.pos) {
286-
val templ1 = (
287-
Template(templ.parents, noSelfType, addMixinConstructorDef(clazz, templ.body map deriveMemberForImplClass))
288-
setSymbol clazz.newLocalDummy(templ.pos)
289-
)
290-
templ1.changeOwner(templ.symbol.owner -> clazz, templ.symbol -> templ1.symbol)
291-
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
292309
}
293310

294311
def implClassDefs(trees: List[Tree]): List[Tree] = {
295312
trees collect {
296313
case cd: ClassDef if cd.symbol.needsImplClass =>
297314
val clazz = implClass(cd.symbol).initialize
298-
ClassDef(clazz, implTemplate(clazz, cd.impl))
315+
ClassDef(clazz, implTemplate(clazz, cd))
299316
}
300317
}
301318

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

Lines changed: 15 additions & 13 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
@@ -591,7 +590,7 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
591590
case ValDef(mods, name, _, _) if mods hasFlag PRESUPER =>
592591
// stat is the constructor-local definition of the field value
593592
val fields = presupers filter (_.getterName == name)
594-
assert(fields.length == 1)
593+
assert(fields.length == 1, s"expected exactly one field by name $name in $presupers of $clazz's early initializers")
595594
val to = fields.head.symbol
596595

597596
if (memoizeValue(to)) constrStatBuf += mkAssign(to, Ident(stat.symbol))
@@ -607,9 +606,9 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
607606
// it goes before the superclass constructor call, otherwise it goes after.
608607
// A lazy val's effect is not moved to the constructor, as it is delayed.
609608
// Returns `true` when a `ValDef` is needed.
610-
def moveEffectToCtor(mods: Modifiers, rhs: Tree, memoized: Boolean): Unit = {
609+
def moveEffectToCtor(mods: Modifiers, rhs: Tree, assignSym: Symbol): Unit = {
611610
val initializingRhs =
612-
if (!memoized || statSym.isLazy) EmptyTree // not memoized, or effect delayed (for lazy val)
611+
if ((assignSym eq NoSymbol) || statSym.isLazy) EmptyTree // not memoized, or effect delayed (for lazy val)
613612
else if (!mods.hasStaticFlag) intoConstructor(statSym, primaryConstr.symbol)(rhs)
614613
else rhs
615614

@@ -619,7 +618,7 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
619618
else if (mods hasFlag PRESUPER | PARAMACCESSOR) constrPrefixBuf
620619
else constrStatBuf
621620

622-
initPhase += mkAssign(statSym, initializingRhs)
621+
initPhase += mkAssign(assignSym, initializingRhs)
623622
}
624623
}
625624

@@ -632,18 +631,21 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
632631
// The primary constructor is dealt with separately (we're massaging it here).
633632
case _: DefDef if statSym.isPrimaryConstructor => ()
634633
case _: DefDef if statSym.isConstructor => auxConstructorBuf += stat
635-
case _: DefDef => defBuf += stat
634+
case stat: DefDef =>
635+
defBuf += stat
636+
636637

637638
// If a val needs a field, an empty valdef goes into the template.
638639
// Except for lazy and ConstantTyped vals, the field is initialized by an assignment in:
639640
// - the class initializer (static),
640641
// - the constructor, before the super call (early initialized or a parameter accessor),
641642
// - the constructor, after the super call (regular val).
642643
case ValDef(mods, _, _, rhs) =>
643-
val emitField = memoizeValue(statSym)
644-
moveEffectToCtor(mods, rhs, emitField)
645-
646-
if (emitField) defBuf += deriveValDef(stat)(_ => EmptyTree)
644+
if (rhs ne EmptyTree) {
645+
val emitField = memoizeValue(statSym)
646+
moveEffectToCtor(mods, rhs, statSym)
647+
if (emitField) defBuf += deriveValDef(stat)(_ => EmptyTree)
648+
} else defBuf += stat
647649

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

0 commit comments

Comments
 (0)