Skip to content

Commit e097d51

Browse files
committed
support merging companion objects in expanded trees
When there are multiple transform before typer, there might be companion objects that only exist in the expanded trees. This PR simplifies original companion object merging logic and provides support for multi-level expansion.
1 parent c90ea6b commit e097d51

File tree

1 file changed

+66
-31
lines changed

1 file changed

+66
-31
lines changed

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

Lines changed: 66 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -484,46 +484,60 @@ 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 -> (tree, module class) (in the expanded tree of which the module appear)
488+
val moduleDef = mutable.Map[TypeName, (Tree, TypeDef)]()
489+
val moduleValDef = mutable.Set[TermName]()
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]): Unit = {
499+
val Thicket(trees) = expanded(mdef)
500+
val merged = trees.map { tree =>
501+
if (tree == modCls) {
502+
val impl = modCls.rhs.asInstanceOf[Template]
503+
cpy.TypeDef(modCls)(rhs = cpy.Template(impl)(body = stats ++ impl.body))
504+
}
505+
else tree
506+
}
507+
508+
mdef.putAttachment(ExpandedTree, Thicket(merged))
509+
}
489510

490511
/** Merge the definitions of a synthetic companion generated by a case class
491512
* and the real companion, if both exist.
492513
*/
493514
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))
515+
def valid(mdef: MemberDef): Boolean = !mdef.mods.is(Flags.Package) && mdef.mods.is(Flags.Module)
516+
517+
for (stat <- stats)
518+
expanded(stat) match {
519+
case Thicket(trees) => // companion object always expands to thickets
520+
trees.map {
521+
case mcls @ TypeDef(name, impl: Template) if valid(mcls) =>
522+
if (moduleDef.contains(name)) {
523+
val (stat1, modCls) = moduleDef(name)
524+
removeInExpanded(stat, mcls)
525+
mergeModuleClass(stat1, modCls, impl.body)
526+
}
527+
else moduleDef(name) = (stat, mcls)
528+
529+
case vdef @ ValDef(name, _, _) if valid(vdef) =>
530+
if (moduleValDef.contains(name)) removeInExpanded(stat, vdef)
531+
else moduleValDef += name
532+
520533
case _ =>
521534
}
522-
case none =>
535+
case _ =>
536+
523537
}
524-
}
525538
}
526539

540+
/** Create links between companion object and companion class */
527541
def createLinks(classTree: TypeDef, moduleTree: TypeDef)(implicit ctx: Context) = {
528542
val claz = ctx.denotNamed(classTree.name.encode).symbol
529543
val modl = ctx.denotNamed(moduleTree.name.encode).symbol
@@ -532,8 +546,29 @@ class Namer { typer: Typer =>
532546
}
533547

534548
def createCompanionLinks(implicit ctx: Context): Unit = {
549+
val classDef = mutable.Map[TypeName, TypeDef]()
550+
val moduleDef = mutable.Map[TypeName, TypeDef]()
551+
552+
def updateCache(cdef: TypeDef): Unit = {
553+
if (!cdef.isClassDef || cdef.mods.is(Flags.Package)) return
554+
555+
if (cdef.mods.is(Flags.ModuleClass)) moduleDef(cdef.name) = cdef
556+
else classDef(cdef.name) = cdef
557+
}
558+
559+
for (stat <- stats)
560+
expanded(stat) match {
561+
case cdef : TypeDef => updateCache(cdef)
562+
case Thicket(trees) =>
563+
trees.map {
564+
case cdef: TypeDef => updateCache(cdef)
565+
case _ =>
566+
}
567+
case _ =>
568+
}
569+
535570
for (cdef @ TypeDef(name, _) <- classDef.values) {
536-
moduleDef.getOrElse(name, EmptyTree) match {
571+
moduleDef.getOrElse(name.moduleClassName, EmptyTree) match {
537572
case t: TypeDef =>
538573
createLinks(cdef, t)
539574
case EmptyTree =>

0 commit comments

Comments
 (0)