Skip to content

Commit 5e80233

Browse files
committed
Merge pull request #1075 from dotty-staging/fix-t920-test
Drop empty companion objects
2 parents afec2a7 + d3c3317 commit 5e80233

File tree

8 files changed

+160
-37
lines changed

8 files changed

+160
-37
lines changed

src/dotty/tools/dotc/Compiler.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +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,
8080
new Flatten,
81+
new DropEmptyCompanions,
8182
new RestoreScopes),
82-
List(/*new PrivateToStatic,*/
83-
new ExpandPrivate,
83+
List(new ExpandPrivate,
8484
new CollectEntryPoints,
8585
new LabelDefs),
8686
List(new GenBCode)

src/dotty/tools/dotc/core/Flags.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,7 @@ object Flags {
606606
final val AbstractFinal = allOf(Abstract, Final)
607607
final val AbstractSealed = allOf(Abstract, Sealed)
608608
final val SyntheticArtifact = allOf(Synthetic, Artifact)
609+
final val SyntheticModule = allOf(Synthetic, Module)
609610
final val SyntheticTermParam = allOf(Synthetic, TermParam)
610611
final val SyntheticTypeParam = allOf(Synthetic, TypeParam)
611612
final val SyntheticCase = allOf(Synthetic, Case)

src/dotty/tools/dotc/core/NameOps.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ object NameOps {
8282
def isModuleVarName(name: Name): Boolean =
8383
name.stripAnonNumberSuffix endsWith MODULE_VAR_SUFFIX
8484
def isSelectorName = name.startsWith(" ") && name.tail.forall(_.isDigit)
85+
def isLazyLocal = name.endsWith(nme.LAZY_LOCAL)
8586

8687
/** Is name a variable name? */
8788
def isVariableName: Boolean = name.length > 0 && {
@@ -423,5 +424,11 @@ object NameOps {
423424
case NO_NAME => primitivePostfixMethodName
424425
case name => name
425426
}
427+
428+
def lazyLocalName = name ++ nme.LAZY_LOCAL
429+
def nonLazyName = {
430+
assert(name.isLazyLocal)
431+
name.dropRight(nme.LAZY_LOCAL.length)
432+
}
426433
}
427434
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
package dotty.tools.dotc
2+
package transform
3+
4+
import core._
5+
import DenotTransformers.SymTransformer
6+
import Phases.Phase
7+
import Contexts.Context
8+
import Flags._
9+
import Symbols._
10+
import SymDenotations.SymDenotation
11+
import ast.Trees._
12+
import collection.mutable
13+
import Decorators._
14+
import NameOps._
15+
import TreeTransforms.MiniPhaseTransform
16+
import dotty.tools.dotc.transform.TreeTransforms.TransformerInfo
17+
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 =>
32+
import ast.tpd._
33+
override def phaseName = "dropEmpty"
34+
override def runsAfter: Set[Class[_ <: Phase]] = Set(classOf[Flatten])
35+
36+
override def transformPackageDef(pdef: PackageDef)(implicit ctx: Context, info: TransformerInfo) = {
37+
38+
/** Is `tree` an empty companion object? */
39+
def isEmptyCompanion(tree: Tree) = tree match {
40+
case TypeDef(_, impl: Template) if tree.symbol.is(SyntheticModule) &&
41+
tree.symbol.companionClass.exists &&
42+
impl.body.forall(_.symbol.isPrimaryConstructor) =>
43+
println(i"removing ${tree.symbol}")
44+
true
45+
case _ =>
46+
false
47+
}
48+
49+
val dropped = pdef.stats.filter(isEmptyCompanion).map(_.symbol).toSet
50+
51+
/** Symbol is a $lzy field representing a module */
52+
def isLazyModuleVar(sym: Symbol) =
53+
sym.name.isLazyLocal &&
54+
sym.owner.info.decl(sym.name.asTermName.nonLazyName).symbol.is(Module)
55+
56+
/** Symbol should be dropped together with a dropped companion object.
57+
* Such symbols are:
58+
* - lzy fields pointing to modules,
59+
* - vals and getters representing modules.
60+
*/
61+
def symIsDropped(sym: Symbol): Boolean =
62+
(sym.is(Module) || isLazyModuleVar(sym)) &&
63+
dropped.contains(sym.info.resultType.typeSymbol)
64+
65+
/** Tree should be dropped because it (is associated with) an empty
66+
* companion object. Such trees are
67+
* - module classes of empty companion objects
68+
* - definitions of lazy module variables or assignments to them.
69+
* - vals and getters for empty companion objects
70+
*/
71+
def toDrop(stat: Tree): Boolean = stat match {
72+
case stat: TypeDef => dropped.contains(stat.symbol)
73+
case stat: ValOrDefDef => symIsDropped(stat.symbol)
74+
case stat: Assign => symIsDropped(stat.lhs.symbol)
75+
case _ => false
76+
}
77+
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))
97+
}
98+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import collection.mutable
1111
import TreeTransforms.MiniPhaseTransform
1212
import dotty.tools.dotc.transform.TreeTransforms.TransformerInfo
1313

14+
/** Lift nested classes to toplevel */
1415
class Flatten extends MiniPhaseTransform with SymTransformer { thisTransform =>
1516
import ast.tpd._
1617
override def phaseName = "flatten"

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer {
6363

6464
if (isField) {
6565
if (sym.isVolatile ||
66-
(sym.is(Flags.Module) && !sym.is(Flags.Synthetic)))
67-
// module class is user-defined.
66+
(sym.is(Flags.Module) && !sym.is(Flags.Synthetic)))
67+
// module class is user-defined.
6868
// Should be threadsafe, to mimic safety guaranteed by global object
6969
transformMemberDefVolatile(tree)
7070
else if (sym.is(Flags.Module)) { // synthetic module
@@ -101,7 +101,7 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer {
101101
*/
102102
def transformSyntheticModule(tree: ValOrDefDef)(implicit ctx: Context) = {
103103
val sym = tree.symbol
104-
val holderSymbol = ctx.newSymbol(sym.owner, sym.asTerm.name ++ nme.LAZY_LOCAL,
104+
val holderSymbol = ctx.newSymbol(sym.owner, sym.asTerm.name.lazyLocalName,
105105
Flags.Synthetic, sym.info.widen.resultType).enteredAfter(this)
106106
val field = ValDef(holderSymbol, tree.rhs.changeOwnerAfter(sym, holderSymbol, this))
107107
val getter = DefDef(sym.asTerm, ref(holderSymbol))
@@ -114,7 +114,7 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer {
114114
*/
115115
def transformLocalDef(x: ValOrDefDef)(implicit ctx: Context) = {
116116
val valueInitter = x.rhs
117-
val holderName = ctx.freshName(x.name ++ StdNames.nme.LAZY_LOCAL).toTermName
117+
val holderName = ctx.freshName(x.name.asTermName.lazyLocalName).toTermName
118118
val initName = ctx.freshName(x.name ++ StdNames.nme.LAZY_LOCAL_INIT).toTermName
119119
val tpe = x.tpe.widen.resultType.widen
120120

@@ -206,7 +206,7 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer {
206206
val claz = x.symbol.owner.asClass
207207
val tpe = x.tpe.widen.resultType.widen
208208
assert(!(x.mods is Flags.Mutable))
209-
val containerName = ctx.freshName(x.name ++ StdNames.nme.LAZY_LOCAL).toTermName
209+
val containerName = ctx.freshName(x.name.asTermName.lazyLocalName).toTermName
210210
val containerSymbol = ctx.newSymbol(claz, containerName,
211211
x.symbol.flags &~ containerFlagsMask | containerFlags | Flags.Private,
212212
tpe, coord = x.symbol.coord
@@ -367,7 +367,7 @@ class LazyVals extends MiniPhaseTransform with IdentityDenotTransformer {
367367
appendOffsetDefs += (companion.moduleClass -> new OffsetInfo(List(offsetTree), ord))
368368
}
369369

370-
val containerName = ctx.freshName(x.name ++ StdNames.nme.LAZY_LOCAL).toTermName
370+
val containerName = ctx.freshName(x.name.asTermName.lazyLocalName).toTermName
371371
val containerSymbol = ctx.newSymbol(claz, containerName, (x.mods &~ containerFlagsMask | containerFlags).flags, tpe, coord = x.symbol.coord).enteredAfter(this)
372372
val containerTree = ValDef(containerSymbol, defaultValue(tpe))
373373

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+

tests/run/t920.scala renamed to tests/pending/run/t920.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,9 @@ object Test {
1717
def main(args : Array[String]) : Unit = {
1818
b.initialize;
1919
}
20+
class XYZ
2021
}
22+
23+
24+
25+

0 commit comments

Comments
 (0)