Skip to content

Commit 391aaa1

Browse files
authored
Merge pull request #2057 from dotty-staging/merge-companion
support merging companion objects in expanded trees
2 parents 54f9206 + a845928 commit 391aaa1

File tree

2 files changed

+98
-31
lines changed

2 files changed

+98
-31
lines changed

compiler/src/dotty/tools/dotc/ast/untpd.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
149149

150150
def is(fs: FlagSet): Boolean = flags is fs
151151
def is(fc: FlagConjunction): Boolean = flags is fc
152+
def is(fc: FlagSet, butNot: FlagSet): Boolean = flags.is(fc, butNot = butNot)
152153

153154
def | (fs: FlagSet): Modifiers = withFlags(flags | fs)
154155
def & (fs: FlagSet): Modifiers = withFlags(flags & fs)

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

Lines changed: 97 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -484,46 +484,91 @@ 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+
}
514+
515+
/** Merge `fromCls` of `fromStat` into `toCls` of `toStat`
516+
* if the former is synthetic and the latter not.
517+
*
518+
* Note:
519+
* 1. `fromStat` and `toStat` could be the same stat
520+
* 2. `fromCls` and `toCls` are necessarily different
521+
*/
522+
def mergeIfSynthetic(fromStat: Tree, fromCls: TypeDef, toStat: Tree, toCls: TypeDef): Unit =
523+
if (fromCls.mods.is(Synthetic) && !toCls.mods.is(Synthetic)) {
524+
removeInExpanded(fromStat, fromCls)
525+
val mcls = mergeModuleClass(toStat, toCls, fromCls.rhs.asInstanceOf[Template].body)
526+
moduleClsDef(fromCls.name) = (toStat, mcls)
527+
}
489528

490529
/** Merge the definitions of a synthetic companion generated by a case class
491530
* and the real companion, if both exist.
492531
*/
493532
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))
533+
def valid(mdef: MemberDef): Boolean = mdef.mods.is(Module, butNot = Package)
534+
535+
for (stat <- stats)
536+
expanded(stat) match {
537+
case Thicket(trees) => // companion object always expands to thickets
538+
trees.map {
539+
case mcls @ TypeDef(name, impl: Template) if valid(mcls) =>
540+
moduleClsDef.get(name) match {
541+
case Some((stat1, mcls1@TypeDef(_, impl1: Template))) =>
542+
mergeIfSynthetic(stat, mcls, stat1, mcls1)
543+
mergeIfSynthetic(stat1, mcls1, stat, mcls)
544+
case None =>
545+
moduleClsDef(name) = (stat, mcls)
546+
}
547+
548+
case vdef @ ValDef(name, _, _) if valid(vdef) =>
549+
moduleValDef.get(name) match {
550+
case Some((stat1, vdef1)) =>
551+
if (vdef.mods.is(Synthetic) && !vdef1.mods.is(Synthetic))
552+
removeInExpanded(stat, vdef)
553+
else if (!vdef.mods.is(Synthetic) && vdef1.mods.is(Synthetic)) {
554+
removeInExpanded(stat1, vdef1)
555+
moduleValDef(name) = (stat, vdef)
556+
}
557+
else {
558+
// double definition of objects or case classes, handled elsewhere
559+
}
560+
case None =>
561+
moduleValDef(name) = (stat, vdef)
562+
}
563+
520564
case _ =>
521565
}
522-
case none =>
566+
case _ =>
567+
523568
}
524-
}
525569
}
526570

571+
/** Create links between companion object and companion class */
527572
def createLinks(classTree: TypeDef, moduleTree: TypeDef)(implicit ctx: Context) = {
528573
val claz = ctx.denotNamed(classTree.name.encode).symbol
529574
val modl = ctx.denotNamed(moduleTree.name.encode).symbol
@@ -532,8 +577,29 @@ class Namer { typer: Typer =>
532577
}
533578

534579
def createCompanionLinks(implicit ctx: Context): Unit = {
580+
val classDef = mutable.Map[TypeName, TypeDef]()
581+
val moduleDef = mutable.Map[TypeName, TypeDef]()
582+
583+
def updateCache(cdef: TypeDef): Unit = {
584+
if (!cdef.isClassDef || cdef.mods.is(Package)) return
585+
586+
if (cdef.mods.is(ModuleClass)) moduleDef(cdef.name) = cdef
587+
else classDef(cdef.name) = cdef
588+
}
589+
590+
for (stat <- stats)
591+
expanded(stat) match {
592+
case cdef : TypeDef => updateCache(cdef)
593+
case Thicket(trees) =>
594+
trees.map {
595+
case cdef: TypeDef => updateCache(cdef)
596+
case _ =>
597+
}
598+
case _ =>
599+
}
600+
535601
for (cdef @ TypeDef(name, _) <- classDef.values) {
536-
moduleDef.getOrElse(name, EmptyTree) match {
602+
moduleDef.getOrElse(name.moduleClassName, EmptyTree) match {
537603
case t: TypeDef =>
538604
createLinks(cdef, t)
539605
case EmptyTree =>

0 commit comments

Comments
 (0)