Skip to content

Commit 51f59df

Browse files
committed
constructors... what a mess
1 parent 93f1197 commit 51f59df

File tree

3 files changed

+149
-176
lines changed

3 files changed

+149
-176
lines changed

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

Lines changed: 77 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -417,9 +417,9 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
417417
}
418418

419419
if (stat1 eq stat) {
420-
assert(ctorParams(genericClazz).length == constrInfo.constrParams.length)
420+
assert(ctorParams(genericClazz).length == primaryConstrParams.length)
421421
// this is just to make private fields public
422-
(new specializeTypes.ImplementationAdapter(ctorParams(genericClazz), constrInfo.constrParams, null, true))(stat1)
422+
(new specializeTypes.ImplementationAdapter(ctorParams(genericClazz), primaryConstrParams, null, true))(stat1)
423423

424424
val stat2 = rewriteArrayUpdate(stat1)
425425
// statements coming from the original class need retyping in the current context
@@ -456,16 +456,16 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
456456
// postfix = postfix.tail
457457
// }
458458

459-
if (shouldGuard && usesSpecializedField && stats.nonEmpty) {
459+
if (guardSpecializedFieldInit && usesSpecializedField && stats.nonEmpty) {
460460
// save them for duplication in the specialized subclass
461461
guardedCtorStats(clazz) = stats
462-
ctorParams(clazz) = constrInfo.constrParams
462+
ctorParams(clazz) = primaryConstrParams
463463

464464
val tree =
465465
If(
466466
Apply(
467467
CODE.NOT (
468-
Apply(gen.mkAttributedRef(specializedFlag), List())),
468+
Apply(gen.mkAttributedRef(hasSpecializedFieldsSym), List())),
469469
List()),
470470
Block(stats, Literal(Constant(()))),
471471
EmptyTree)
@@ -493,32 +493,23 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
493493
with OmittablesHelper
494494
with GuardianOfCtorStmts {
495495

496-
val clazz = impl.symbol.owner // the transformed class
497-
val inTrait = clazz.isTrait
498-
val stats = impl.body // the transformed template body
496+
val clazz = impl.symbol.owner // the transformed class
497+
val inTrait = clazz.isTrait
498+
val stats = impl.body // the transformed template body
499499
val localTyper = typer.atOwner(impl, clazz)
500500

501-
val specializedFlag: Symbol = clazz.info.decl(nme.SPECIALIZED_INSTANCE)
502-
val shouldGuard = (specializedFlag != NoSymbol) && !clazz.hasFlag(SPECIALIZED)
501+
val hasSpecializedFieldsSym = clazz.info.decl(nme.SPECIALIZED_INSTANCE)
502+
// The constructor of non-specialized classes with specialized subclasses should guard
503+
// the initialization of specialized fields on the result of the `hasSpecializedFieldsSym` method.
504+
val guardSpecializedFieldInit = !((hasSpecializedFieldsSym == NoSymbol) || clazz.hasFlag(SPECIALIZED))
503505

504-
val isDelayedInitSubclass = (clazz isSubClass DelayedInitClass)
506+
val isDelayedInitSubclass = clazz isSubClass DelayedInitClass
505507

506-
case class ConstrInfo(
507-
constr: DefDef, // The primary constructor
508-
constrParams: List[Symbol], // ... and its parameters
509-
constrBody: Block // ... and its body
510-
)
511508
// decompose primary constructor into the three entities above.
512-
val constrInfo: ConstrInfo = {
513-
val ddef = (stats find (_.symbol.isPrimaryConstructor))
514-
ddef match {
515-
case Some(ddef @ DefDef(_, _, _, List(vparams), _, rhs @ Block(_, _))) =>
516-
ConstrInfo(ddef, vparams map (_.symbol), rhs)
517-
case x =>
518-
abort("no constructor in template: impl = " + impl)
519-
}
520-
}
521-
import constrInfo._
509+
val (primaryConstr, primaryConstrParams, primaryConstrBody) = stats collectFirst {
510+
case dd@ DefDef(_, _, _, vps :: Nil, _, rhs: Block) if dd.symbol.isPrimaryConstructor => (dd, vps map (_.symbol), rhs)
511+
} getOrElse abort("no constructor in template: impl = " + impl)
512+
522513

523514
// The parameter accessor fields which are members of the class
524515
val paramAccessors = clazz.constrParamAccessors
@@ -531,19 +522,19 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
531522
def parameterNamed(name: Name): Symbol = {
532523
def matchesName(param: Symbol) = param.name == name || param.name.startsWith(name + nme.NAME_JOIN_STRING)
533524

534-
(constrParams filter matchesName) match {
535-
case Nil => abort(name + " not in " + constrParams)
525+
(primaryConstrParams filter matchesName) match {
526+
case Nil => abort(name + " not in " + primaryConstrParams)
536527
case p :: _ => p
537528
}
538529
}
539530

540531
/*
541532
* `usesSpecializedField` makes a difference in deciding whether constructor-statements
542-
* should be guarded in a `shouldGuard` class, ie in a class that's the generic super-class of
533+
* should be guarded in a `guardSpecializedFieldInit` class, ie in a class that's the generic super-class of
543534
* one or more specialized sub-classes.
544535
*
545536
* Given that `usesSpecializedField` isn't read for any other purpose than the one described above,
546-
* we skip setting `usesSpecializedField` in case the current class isn't `shouldGuard` to start with.
537+
* we skip setting `usesSpecializedField` in case the current class isn't `guardSpecializedFieldInit` to start with.
547538
* That way, trips to a map in `specializeTypes` are saved.
548539
*/
549540
var usesSpecializedField: Boolean = false
@@ -588,10 +579,8 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
588579
// references to parameter accessor field of own class become references to parameters
589580
gen.mkAttributedIdent(parameter(tree.symbol)) setPos tree.pos
590581

591-
case Select(_, _) if shouldGuard => // reasoning behind this guard in the docu of `usesSpecializedField`
592-
if (possiblySpecialized(tree.symbol)) {
593-
usesSpecializedField = true
594-
}
582+
case Select(_, _) if guardSpecializedFieldInit && possiblySpecialized(tree.symbol) => // reasoning behind this guard in the docu of `usesSpecializedField`
583+
usesSpecializedField = true
595584
super.transform(tree)
596585

597586
case _ =>
@@ -604,7 +593,7 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
604593

605594
// Move tree into constructor, take care of changing owner from `oldowner` to constructor symbol
606595
def intoConstructor(oldowner: Symbol, tree: Tree) =
607-
intoConstructorTransformer transform tree.changeOwner(oldowner -> constr.symbol)
596+
intoConstructorTransformer transform tree.changeOwner(oldowner -> primaryConstr.symbol)
608597

609598
private def needsCtorEffect(stat: ValOrDefDef): Boolean = !(stat.rhs == EmptyTree || stat.symbol.isLazy)
610599

@@ -685,7 +674,7 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
685674
val classInitStatBuf = new ListBuffer[Tree]
686675

687676
// generate code to copy pre-initialized fields
688-
for (stat <- constrBody.stats) {
677+
for (stat <- primaryConstrBody.stats) {
689678
constrStatBuf += stat
690679
stat match {
691680
case ValDef(mods, name, _, _) if (mods hasFlag PRESUPER) =>
@@ -700,57 +689,57 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
700689
}
701690

702691
// Triage all template definitions to go into defBuf/auxConstructorBuf, constrStatBuf, or constrPrefixBuf.
703-
for (stat <- stats) stat match {
704-
case dd@ DefDef(_,_,_,_,_,rhs) =>
705-
// methods with constant result type get literals as their body
692+
693+
for (stat <- stats) {
694+
val sym = stat.symbol
695+
stat match {
696+
// TODO what is this doing in Constructors? I'd expect it much earlier -- erasure? uncurry?
697+
case dd@ DefDef(_,_,_,_,_,rhs) if sym.info.params.isEmpty && sym.info.resultType.isInstanceOf[ConstantType] =>
698+
defBuf += deriveDefDef(dd)(rhs => gen.mkAttributedQualifier(sym.info.resultType) setPos rhs.pos )
699+
706700
// all methods except the primary constructor go into template
707-
stat.symbol.tpe match {
708-
case MethodType(List(), tp @ ConstantType(c)) =>
709-
defBuf += deriveDefDef(stat)(Literal(c) setPos _.pos setType tp)
710-
case _ =>
711-
if (stat.symbol.isPrimaryConstructor) ()
712-
else if (stat.symbol.isConstructor) auxConstructorBuf += stat
713-
// 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)
714-
else if (inTrait) {
715-
// We can't mark as deferred from the start, as it will be implemented automatically in the subclass
716-
// it should be marked deferred in bytecode, though
717-
718-
var stat_memoizedGetterLosesRhs = stat
719-
720-
// a trait getter with an empty RHS is marked deferred from the start
721-
if (stat.symbol.isGetter && (rhs ne EmptyTree)) {
701+
case _: DefDef if sym.isConstructor => if (sym ne primaryConstr.symbol) auxConstructorBuf += stat
702+
703+
// 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)
704+
// We can't mark as deferred from the start, as it will be implemented automatically in the subclass
705+
// it should be marked deferred in bytecode, though
706+
case dd@ DefDef(_,_,_,_,_,rhs) if inTrait && sym.isAccessor =>
707+
// a trait getter with an empty RHS is marked deferred from the start
708+
defBuf +=
709+
if (sym.isGetter) {
710+
if (rhs ne EmptyTree) {
722711
val memoized = moveEffectToCtor(dd)
723712
if (memoized) {
724-
stat.symbol setFlag (DEFERRED | lateDEFERRED)
725-
stat_memoizedGetterLosesRhs = deriveDefDef(dd)(_ => EmptyTree)
726-
}
727-
} else if (stat.symbol.isSetter) {
728-
stat.symbol setFlag (DEFERRED | lateDEFERRED)
729-
}
730-
731-
defBuf += stat_memoizedGetterLosesRhs
713+
sym setFlag (DEFERRED | lateDEFERRED)
714+
deriveDefDef(dd)(_ => EmptyTree)
715+
} else stat
716+
} else stat
717+
} else {
718+
sym setFlag (DEFERRED | lateDEFERRED)
719+
stat
732720
}
733-
else defBuf += stat
734-
}
735-
case vd@ ValDef(mods, _, _, rhs) if !mods.hasStaticFlag =>
736-
// Move field's effect into the constructor (usually an assignment of rhs to field,
737-
// but it could also be the side-effect of the rhs when no storage is needed).
738-
val needsField = moveEffectToCtor(vd)
739-
740-
// Only emit fields that actually need to be stored
741-
if (needsField) defBuf += deriveValDef(stat)(_ => EmptyTree)
742-
743-
case ValDef(_, _, _, rhs) =>
744-
// Add static initializer statements to classInitStatBuf and remove the rhs from the val def.
745-
classInitStatBuf += mkAssign(stat.symbol, rhs)
746-
defBuf += deriveValDef(stat)(_ => EmptyTree)
747-
748-
case ClassDef(_, _, _, _) =>
749-
// classes are treated recursively, and left in the template
750-
defBuf += new ConstructorTransformer(unit).transform(stat)
751-
case _ =>
752-
// all other statements go into the constructor
753-
constrStatBuf += intoConstructor(impl.symbol, stat)
721+
722+
case _: DefDef => defBuf += stat
723+
case vd@ ValDef(mods, _, _, rhs) if !mods.hasStaticFlag =>
724+
// Move field's effect into the constructor (usually an assignment of rhs to field,
725+
// but it could also be the side-effect of the rhs when no storage is needed).
726+
val needsField = moveEffectToCtor(vd)
727+
728+
// Only emit fields that actually need to be stored
729+
if (needsField) defBuf += deriveValDef(stat)(_ => EmptyTree)
730+
731+
case ValDef(_, _, _, rhs) =>
732+
// Add static initializer statements to classInitStatBuf and remove the rhs from the val def.
733+
classInitStatBuf += mkAssign(sym, rhs)
734+
defBuf += deriveValDef(stat)(_ => EmptyTree)
735+
736+
case ClassDef(_, _, _, _) =>
737+
// classes are treated recursively, and left in the template
738+
defBuf += new ConstructorTransformer(unit).transform(stat)
739+
case _ =>
740+
// all other statements go into the constructor
741+
constrStatBuf += intoConstructor(impl.symbol, stat)
742+
}
754743
}
755744

756745
populateOmittables()
@@ -768,7 +757,7 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
768757
copyParam(acc, parameter(acc))
769758
}
770759

771-
/* Return a pair consisting of (all statements up to and including superclass and trait constr calls, rest) */
760+
/* Return a pair consisting of (all statements up to and including superclass and trait primaryConstr calls, rest) */
772761
def splitAtSuper(stats: List[Tree]) = {
773762
def isConstr(tree: Tree): Boolean = tree match {
774763
case Block(_, expr) => isConstr(expr) // SI-6481 account for named argument blocks
@@ -785,12 +774,11 @@ abstract class Constructors extends Statics with Transform with ast.TreeDSL {
785774
rewriteDelayedInit()
786775

787776
// Assemble final constructor
788-
defBuf += deriveDefDef(constr)(_ =>
777+
defBuf += deriveDefDef(primaryConstr)(_ =>
789778
treeCopy.Block(
790-
constrBody,
791-
paramInits ::: constrPrefixBuf.toList ::: uptoSuperStats :::
792-
guardSpecializedInitializer(remainingConstrStats),
793-
constrBody.expr))
779+
primaryConstrBody,
780+
paramInits ::: constrPrefixBuf.toList ::: uptoSuperStats ::: guardSpecializedInitializer(remainingConstrStats),
781+
primaryConstrBody.expr))
794782

795783
// Followed by any auxiliary constructors
796784
defBuf ++= auxConstructorBuf

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

Lines changed: 12 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -397,42 +397,26 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
397397

398398
// TODO in namers: if(sym.owner.isTrait && sym.isSetter && !sym.isDeferred) sym.addAnnotation(TraitSetterAnnotationClass)
399399

400-
/** Add calls to supermixin constructors
401-
* `super[mix].$init$()`
402-
* to tree, which is assumed to be the body of a constructor of class clazz.
403-
*/
404-
private def addMixinConstructorCalls(clazz: Symbol)(tree: Tree): Tree = {
405-
def mixinConstructorCall(`trait`: Symbol): Tree = typedPos(tree.pos) {
406-
Apply(Select(This(clazz), `trait`.primaryConstructor), List())
407-
}
408-
val mixinConstructorCalls = clazz.mixinClasses.filter(_.isTrait).map(mixinConstructorCall).reverse
409-
tree match {
410-
// TODO???
411-
// case Block(Nil, expr) =>
412-
// // AnyVal constructor - have to provide a real body so the
413-
// // jvm doesn't throw a VerifyError. But we can't add the
414-
// // body until now, because the typer knows that Any has no
415-
// // constructor and won't accept a call to super.init.
416-
// assert((clazz isSubClass AnyValClass) || clazz.info.parents.isEmpty, clazz)
417-
// Block(List(Apply(gen.mkSuperInitCall, Nil)), expr)
418-
419-
case Block(stats, expr) =>
420-
// needs `hasSymbolField` check because `supercall` could be a block (named / default args)
421-
val (presuper, supercall :: rest) = stats span (t => t.hasSymbolWhich(_ hasFlag PRESUPER))
422-
treeCopy.Block(tree, presuper ::: (supercall :: mixinConstructorCalls ::: rest), expr)
423-
}
424-
}
425-
426400
/** The first transform; called in a pre-order traversal at phase mixin
427401
* (that is, every node is processed before its children).
428402
*
429403
* For every non-trait class, add all mixed in members to the class info.
430404
*/
431405
private def preTransform(tree: Tree): Tree = {
432406
val sym = tree.symbol
407+
val clazz = sym.owner
433408
tree match {
434-
case DefDef(_,_,_,_,_,_) if sym.isClassConstructor && sym.isPrimaryConstructor && sym.owner != ArrayClass =>
435-
deriveDefDef(tree)(addMixinConstructorCalls(sym.owner))
409+
// Add super-trait constructor calls `super[mix].$init$()` (in reverse-linearization order) to the body of the ctor
410+
case DefDef(_,_,_,_,_, Block(_ :: _, _)) if sym.isClassConstructor && sym.isPrimaryConstructor && clazz != ArrayClass =>
411+
deriveDefDef(tree) { case tree@Block(stats, expr) =>
412+
def typedCtorCall(`trait`: Symbol): Tree =
413+
typedPos(expr.pos) { Apply(Select(This(clazz), `trait`.primaryConstructor), List()) }
414+
415+
// check `hasExistingSymbol` (via `hasSymbolWhich`) because `supercall` could be a block (named / default args)
416+
val (preSuper, superCall :: postSuperOrig) = stats span (_ hasSymbolWhich (_ hasFlag PRESUPER))
417+
val postSuper = (clazz.mixinClasses map typedCtorCall) reverse_::: postSuperOrig
418+
treeCopy.Block(tree, preSuper ::: (superCall :: postSuper), expr)
419+
}
436420

437421
case Template(parents, self, body) if doImplementTraitMembers(currentOwner) =>
438422
localTyper = erasure.newTyper(rootContext.make(tree, currentOwner))

0 commit comments

Comments
 (0)