Skip to content

Commit d2c0f12

Browse files
committed
support merging companion objects in expanded trees
Previous implementation is problematic when there are multiple transforms before typer: 1. There might be objects needing merging that only exist in the expanded trees, which cannot be handled by the previous algorithm. 2. There may be companion object and class defined only in the expanded trees, the previous algorithm doesn't create links for them. This PR simplifies the companion object merging logic and fixes the problems above. In fact, this PR supports multiple pre-typer transform during expansion. The protocol is that the expansion of a MemberDef is either a flattened thicket or a non-thicket tree.
1 parent c90ea6b commit d2c0f12

File tree

1 file changed

+92
-31
lines changed

1 file changed

+92
-31
lines changed

compiler/src/dotty/tools/dotc/typer/Namer.scala

Lines changed: 92 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -484,46 +484,86 @@ class Namer { typer: Typer =>
484484
/** Create top-level symbols for statements and enter them into symbol table */
485485
def index(stats: List[Tree])(implicit ctx: Context): Context = {
486486

487-
val classDef = mutable.Map[TypeName, TypeDef]()
488-
val moduleDef = mutable.Map[TypeName, TypeDef]()
487+
// module name -> (stat, moduleCls / moduleVal)
488+
val moduleClsDef = mutable.Map[TypeName, (Tree, TypeDef)]()
489+
val moduleValDef = mutable.Map[TermName, (Tree, ValDef)]()
490+
491+
/** Remove the subtree `tree` from the expanded tree of `mdef` */
492+
def removeInExpanded(mdef: Tree, tree: Tree): Unit = {
493+
val Thicket(trees) = expanded(mdef)
494+
mdef.putAttachment(ExpandedTree, Thicket(trees.filter(_ != tree)))
495+
}
496+
497+
/** Merge the module class `modCls` in the expanded tree of `mdef` with the given stats */
498+
def mergeModuleClass(mdef: Tree, modCls: TypeDef, stats: List[Tree]): TypeDef = {
499+
var res: TypeDef = null
500+
val Thicket(trees) = expanded(mdef)
501+
val merged = trees.map { tree =>
502+
if (tree == modCls) {
503+
val impl = modCls.rhs.asInstanceOf[Template]
504+
res = cpy.TypeDef(modCls)(rhs = cpy.Template(impl)(body = stats ++ impl.body))
505+
res
506+
}
507+
else tree
508+
}
509+
510+
mdef.putAttachment(ExpandedTree, Thicket(merged))
511+
512+
res
513+
}
489514

490515
/** Merge the definitions of a synthetic companion generated by a case class
491516
* and the real companion, if both exist.
492517
*/
493518
def mergeCompanionDefs() = {
494-
for (cdef @ TypeDef(name, _) <- stats)
495-
if (cdef.isClassDef) {
496-
classDef(name) = cdef
497-
cdef.attachmentOrElse(ExpandedTree, cdef) match {
498-
case Thicket(cls :: mval :: (mcls @ TypeDef(mname, _: Template)) :: crest)
499-
if name.moduleClassName == mname =>
500-
moduleDef(name) = mcls
501-
case _ =>
502-
}
503-
}
504-
for (mdef @ ModuleDef(name, _) <- stats if !mdef.mods.is(Flags.Package)) {
505-
val typName = name.toTypeName
506-
// Expansion of object is a flattened thicket with the first two elements being:
507-
// module val :: module class :: rest
508-
val Thicket(vdef :: (mcls @ TypeDef(_, impl: Template)) :: rest) = expanded(mdef)
509-
moduleDef(typName) = mcls
510-
classDef get name.toTypeName match {
511-
case Some(cdef) =>
512-
cdef.attachmentOrElse(ExpandedTree, cdef) match {
513-
case Thicket(cls :: mval :: TypeDef(mname, compimpl: Template) :: crest)
514-
if name.moduleClassName == mname =>
515-
val mcls1 = cpy.TypeDef(mcls)(
516-
rhs = cpy.Template(impl)(body = compimpl.body ++ impl.body))
517-
mdef.putAttachment(ExpandedTree, Thicket(vdef :: mcls1 :: rest))
518-
moduleDef(typName) = mcls1
519-
cdef.putAttachment(ExpandedTree, Thicket(cls :: crest))
519+
def valid(mdef: MemberDef): Boolean = !mdef.mods.is(Package) && mdef.mods.is(Module)
520+
521+
for (stat <- stats)
522+
expanded(stat) match {
523+
case Thicket(trees) => // companion object always expands to thickets
524+
trees.map {
525+
case mcls @ TypeDef(name, impl: Template) if valid(mcls) =>
526+
if (moduleClsDef.contains(name)) {
527+
val (stat1, mcls1 @ TypeDef(_, impl1: Template)) = moduleClsDef(name)
528+
if (mcls.mods.is(Synthetic) && !mcls1.mods.is(Synthetic)) { // merge synthetic into user-defined module
529+
removeInExpanded(stat, mcls)
530+
val mcls2 = mergeModuleClass(stat1, mcls1, impl.body)
531+
moduleClsDef(name) = (stat1, mcls2)
532+
}
533+
else if (!mcls.mods.is(Synthetic) && mcls1.mods.is(Synthetic)) {
534+
removeInExpanded(stat1, mcls1)
535+
val mcls2 = mergeModuleClass(stat, mcls, impl1.body)
536+
moduleClsDef(name) = (stat, mcls2)
537+
}
538+
else {
539+
// redefinition of objects or case classes, handled elsewhere
540+
}
541+
}
542+
else moduleClsDef(name) = (stat, mcls)
543+
544+
case vdef @ ValDef(name, _, _) if valid(vdef) =>
545+
if (moduleValDef.contains(name)) {
546+
val (stat1, vdef1) = moduleValDef(name)
547+
if (vdef.mods.is(Synthetic) && !vdef1.mods.is(Synthetic)) // merge synthetic into user-defined module
548+
removeInExpanded(stat, vdef)
549+
else if (!vdef.mods.is(Synthetic) && vdef1.mods.is(Synthetic)) {
550+
removeInExpanded(stat1, vdef1)
551+
moduleValDef(name) = (stat, vdef)
552+
}
553+
else {
554+
// redefinition of objects or case classes, handled elsewhere
555+
}
556+
}
557+
else moduleValDef(name) = (stat, vdef)
558+
520559
case _ =>
521560
}
522-
case none =>
561+
case _ =>
562+
523563
}
524-
}
525564
}
526565

566+
/** Create links between companion object and companion class */
527567
def createLinks(classTree: TypeDef, moduleTree: TypeDef)(implicit ctx: Context) = {
528568
val claz = ctx.denotNamed(classTree.name.encode).symbol
529569
val modl = ctx.denotNamed(moduleTree.name.encode).symbol
@@ -532,8 +572,29 @@ class Namer { typer: Typer =>
532572
}
533573

534574
def createCompanionLinks(implicit ctx: Context): Unit = {
575+
val classDef = mutable.Map[TypeName, TypeDef]()
576+
val moduleDef = mutable.Map[TypeName, TypeDef]()
577+
578+
def updateCache(cdef: TypeDef): Unit = {
579+
if (!cdef.isClassDef || cdef.mods.is(Package)) return
580+
581+
if (cdef.mods.is(ModuleClass)) moduleDef(cdef.name) = cdef
582+
else classDef(cdef.name) = cdef
583+
}
584+
585+
for (stat <- stats)
586+
expanded(stat) match {
587+
case cdef : TypeDef => updateCache(cdef)
588+
case Thicket(trees) =>
589+
trees.map {
590+
case cdef: TypeDef => updateCache(cdef)
591+
case _ =>
592+
}
593+
case _ =>
594+
}
595+
535596
for (cdef @ TypeDef(name, _) <- classDef.values) {
536-
moduleDef.getOrElse(name, EmptyTree) match {
597+
moduleDef.getOrElse(name.moduleClassName, EmptyTree) match {
537598
case t: TypeDef =>
538599
createLinks(cdef, t)
539600
case EmptyTree =>

0 commit comments

Comments
 (0)