Skip to content

Commit d3c3317

Browse files
committed
Fix problems in DropEmptyCompanions
1 parent 1839369 commit d3c3317

File tree

3 files changed

+89
-62
lines changed

3 files changed

+89
-62
lines changed

src/dotty/tools/dotc/Compiler.scala

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,13 @@ class Compiler {
7474
new CapturedVars, // capturedVars has a transformUnit: no phases should introduce local mutable vars here
7575
new Constructors, // constructors changes decls in transformTemplate, no InfoTransformers should be added after it
7676
new FunctionalInterfaces,
77-
new GetClass), // getClass transformation should be applied to specialized methods
77+
new GetClass), // getClass transformation should be applied to specialized methods
7878
List(new LambdaLift, // in this mini-phase block scopes are incorrect. No phases that rely on scopes should be here
7979
new ElimStaticThis,
80-
new DropEmptyCompanions,
8180
new Flatten,
81+
new DropEmptyCompanions,
8282
new RestoreScopes),
83-
List(/*new PrivateToStatic,*/
84-
new ExpandPrivate,
83+
List(new ExpandPrivate,
8584
new CollectEntryPoints,
8685
new LabelDefs),
8786
List(new GenBCode)

src/dotty/tools/dotc/transform/DropEmptyCompanions.scala

Lines changed: 47 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -12,70 +12,87 @@ import ast.Trees._
1212
import collection.mutable
1313
import Decorators._
1414
import NameOps._
15-
import TreeTransforms.{TreeTransform, MiniPhase}
15+
import TreeTransforms.MiniPhaseTransform
1616
import dotty.tools.dotc.transform.TreeTransforms.TransformerInfo
1717

18-
/** Remove companion objects that are empty */
19-
class DropEmptyCompanions extends MiniPhase { thisTransform =>
18+
/** Remove companion objects that are empty
19+
* Lots of constraints here:
20+
* 1. It's impractical to place DropEmptyCompanions before lambda lift because dropped
21+
* modules can be anywhere and have hard to trace references.
22+
* 2. DropEmptyCompanions cannot be interleaved with LambdaLift or Flatten because
23+
* they put things in liftedDefs sets which cause them to surface later. So
24+
* removed modules resurface.
25+
* 3. DropEmptyCompanions has to be before RestoreScopes.
26+
* The solution to the constraints is to put DropEmptyCompanions between Flatten
27+
* and RestoreScopes and to only start working once we are back on PackageDef
28+
* level, so we know that all objects moved by LambdaLift and Flatten have arrived
29+
* at their destination.
30+
*/
31+
class DropEmptyCompanions extends MiniPhaseTransform { thisTransform =>
2032
import ast.tpd._
2133
override def phaseName = "dropEmpty"
22-
val treeTransform = new Transform(Set())
34+
override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Flatten])
2335

24-
class Transform(dropped: Set[Symbol]) extends TreeTransform {
25-
def phase = thisTransform
36+
override def transformPackageDef(pdef: PackageDef)(implicit ctx: Context, info: TransformerInfo) = {
2637

2738
/** Is `tree` an empty companion object? */
28-
private def isEmptyCompanion(tree: Tree)(implicit ctx: Context) = tree match {
29-
case TypeDef(_, impl: Template) if
30-
tree.symbol.is(SyntheticModule) &&
39+
def isEmptyCompanion(tree: Tree) = tree match {
40+
case TypeDef(_, impl: Template) if tree.symbol.is(SyntheticModule) &&
3141
tree.symbol.companionClass.exists &&
3242
impl.body.forall(_.symbol.isPrimaryConstructor) =>
33-
//println(i"removing ${tree.symbol}")
43+
println(i"removing ${tree.symbol}")
3444
true
3545
case _ =>
3646
false
3747
}
3848

39-
/** A transform which has all empty companion objects in `stats`
40-
* recorded in its `dropped` set.
41-
*/
42-
private def localTransform(stats: List[Tree])(implicit ctx: Context) =
43-
new Transform(stats.filter(isEmptyCompanion).map(_.symbol).toSet)
44-
45-
override def prepareForTemplate(tree: Template)(implicit ctx: Context) =
46-
localTransform(tree.body)
47-
48-
override def prepareForStats(trees: List[Tree])(implicit ctx: Context) =
49-
if (ctx.owner is Package) localTransform(trees) else this
49+
val dropped = pdef.stats.filter(isEmptyCompanion).map(_.symbol).toSet
5050

5151
/** Symbol is a $lzy field representing a module */
52-
private def isLazyModuleVar(sym: Symbol)(implicit ctx: Context) =
52+
def isLazyModuleVar(sym: Symbol) =
5353
sym.name.isLazyLocal &&
54-
sym.owner.info.decl(sym.name.asTermName.nonLazyName).symbol.is(Module)
54+
sym.owner.info.decl(sym.name.asTermName.nonLazyName).symbol.is(Module)
5555

5656
/** Symbol should be dropped together with a dropped companion object.
5757
* Such symbols are:
5858
* - lzy fields pointing to modules,
5959
* - vals and getters representing modules.
6060
*/
61-
private def toDrop(sym: Symbol)(implicit ctx: Context): Boolean =
61+
def symIsDropped(sym: Symbol): Boolean =
6262
(sym.is(Module) || isLazyModuleVar(sym)) &&
63-
dropped.contains(sym.info.resultType.typeSymbol)
63+
dropped.contains(sym.info.resultType.typeSymbol)
6464

6565
/** Tree should be dropped because it (is associated with) an empty
6666
* companion object. Such trees are
6767
* - module classes of empty companion objects
6868
* - definitions of lazy module variables or assignments to them.
6969
* - vals and getters for empty companion objects
7070
*/
71-
private def toDrop(stat: Tree)(implicit ctx: Context): Boolean = stat match {
71+
def toDrop(stat: Tree): Boolean = stat match {
7272
case stat: TypeDef => dropped.contains(stat.symbol)
73-
case stat: ValOrDefDef => toDrop(stat.symbol)
74-
case stat: Assign => toDrop(stat.lhs.symbol)
73+
case stat: ValOrDefDef => symIsDropped(stat.symbol)
74+
case stat: Assign => symIsDropped(stat.lhs.symbol)
7575
case _ => false
7676
}
7777

78-
override def transformStats(stats: List[Tree])(implicit ctx: Context, info: TransformerInfo) =
79-
stats.filterNot(toDrop)
78+
def prune(tree: Tree): Tree = tree match {
79+
case tree @ TypeDef(name, impl @ Template(constr, _, _, _)) =>
80+
cpy.TypeDef(tree)(
81+
rhs = cpy.Template(impl)(
82+
constr = cpy.DefDef(constr)(rhs = pruneLocals(constr.rhs)),
83+
body = pruneStats(impl.body)))
84+
case _ =>
85+
tree
86+
}
87+
88+
def pruneStats(stats: List[Tree]) =
89+
stats.filterConserve(!toDrop(_)).mapConserve(prune)
90+
91+
def pruneLocals(expr: Tree) = expr match {
92+
case Block(stats, expr) => cpy.Block(expr)(pruneStats(stats), expr)
93+
case _ => expr
94+
}
95+
96+
cpy.PackageDef(pdef)(pdef.pid, pruneStats(pdef.stats))
8097
}
8198
}

src/dotty/tools/dotc/transform/RestoreScopes.scala

Lines changed: 39 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,35 +23,46 @@ class RestoreScopes extends MiniPhaseTransform with IdentityDenotTransformer { t
2323
import ast.tpd._
2424
override def phaseName = "restoreScopes"
2525

26-
override def transformTypeDef(tree: TypeDef)(implicit ctx: Context, info: TransformerInfo) = {
27-
val TypeDef(_, impl: Template) = tree
28-
//
29-
val restoredDecls = newScope
30-
for (stat <- impl.constr :: impl.body)
31-
if (stat.isInstanceOf[MemberDef] && stat.symbol.exists)
32-
restoredDecls.enter(stat.symbol)
26+
/* Note: We need to wait until we see a package definition because
27+
* DropEmptyConstructors changes template members when analyzing the
28+
* enclosing package definitions. So by the time RestoreScopes gets to
29+
* see a typedef or template, it still might be changed by DropEmptyConstructors.
30+
*/
31+
override def transformPackageDef(pdef: PackageDef)(implicit ctx: Context, info: TransformerInfo) = {
32+
pdef.stats.foreach(restoreScope)
33+
pdef
34+
}
35+
36+
private def restoreScope(tree: Tree)(implicit ctx: Context, info: TransformerInfo) = tree match {
37+
case TypeDef(_, impl: Template) =>
38+
val restoredDecls = newScope
39+
for (stat <- impl.constr :: impl.body)
40+
if (stat.isInstanceOf[MemberDef] && stat.symbol.exists)
41+
restoredDecls.enter(stat.symbol)
3342
// Enter class in enclosing package scope, in case it was an inner class before flatten.
3443
// For top-level classes this does nothing.
35-
val cls = tree.symbol.asClass
36-
val pkg = cls.owner.asClass
37-
38-
// Bring back companion links
39-
val companionClass = cls.info.decls.lookup(nme.COMPANION_CLASS_METHOD)
40-
val companionModule = cls.info.decls.lookup(nme.COMPANION_MODULE_METHOD)
41-
42-
if (companionClass.exists) {
43-
restoredDecls.enter(companionClass)
44-
}
45-
46-
if (companionModule.exists) {
47-
restoredDecls.enter(companionModule)
48-
}
49-
50-
pkg.enter(cls)
51-
val cinfo = cls.classInfo
52-
tree.symbol.copySymDenotation(
53-
info = cinfo.derivedClassInfo( // Dotty deviation: Cannot expand cinfo inline without a type error
54-
decls = restoredDecls: Scope)).installAfter(thisTransform)
55-
tree
44+
val cls = tree.symbol.asClass
45+
val pkg = cls.owner.asClass
46+
47+
// Bring back companion links
48+
val companionClass = cls.info.decls.lookup(nme.COMPANION_CLASS_METHOD)
49+
val companionModule = cls.info.decls.lookup(nme.COMPANION_MODULE_METHOD)
50+
51+
if (companionClass.exists) {
52+
restoredDecls.enter(companionClass)
53+
}
54+
55+
if (companionModule.exists) {
56+
restoredDecls.enter(companionModule)
57+
}
58+
59+
pkg.enter(cls)
60+
val cinfo = cls.classInfo
61+
tree.symbol.copySymDenotation(
62+
info = cinfo.derivedClassInfo( // Dotty deviation: Cannot expand cinfo inline without a type error
63+
decls = restoredDecls: Scope)).installAfter(thisTransform)
64+
tree
65+
case tree => tree
5666
}
5767
}
68+

0 commit comments

Comments
 (0)