Skip to content

Commit 7c63258

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 7c63258

File tree

1 file changed

+88
-31
lines changed

1 file changed

+88
-31
lines changed

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

Lines changed: 88 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -484,46 +484,82 @@ 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)) { // 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 {
534+
assert(mcls1.mods.is(Synthetic))
535+
removeInExpanded(stat1, mcls1)
536+
val mcls2 = mergeModuleClass(stat, mcls, impl1.body)
537+
moduleClsDef(name) = (stat, mcls2)
538+
}
539+
}
540+
else moduleClsDef(name) = (stat, mcls)
541+
542+
case vdef @ ValDef(name, _, _) if valid(vdef) =>
543+
if (moduleValDef.contains(name)) {
544+
val (stat1, vdef1) = moduleValDef(name)
545+
if (vdef.mods.is(Synthetic)) // merge synthetic into user-defined module
546+
removeInExpanded(stat, vdef)
547+
else {
548+
assert(vdef1.mods.is(Synthetic))
549+
removeInExpanded(stat1, vdef1)
550+
moduleValDef(name) = (stat, vdef)
551+
}
552+
}
553+
else moduleValDef(name) = (stat, vdef)
554+
520555
case _ =>
521556
}
522-
case none =>
557+
case _ =>
558+
523559
}
524-
}
525560
}
526561

562+
/** Create links between companion object and companion class */
527563
def createLinks(classTree: TypeDef, moduleTree: TypeDef)(implicit ctx: Context) = {
528564
val claz = ctx.denotNamed(classTree.name.encode).symbol
529565
val modl = ctx.denotNamed(moduleTree.name.encode).symbol
@@ -532,8 +568,29 @@ class Namer { typer: Typer =>
532568
}
533569

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

0 commit comments

Comments
 (0)