@@ -12,70 +12,87 @@ import ast.Trees._
12
12
import collection .mutable
13
13
import Decorators ._
14
14
import NameOps ._
15
- import TreeTransforms .{ TreeTransform , MiniPhase }
15
+ import TreeTransforms .MiniPhaseTransform
16
16
import dotty .tools .dotc .transform .TreeTransforms .TransformerInfo
17
17
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 =>
20
32
import ast .tpd ._
21
33
override def phaseName = " dropEmpty"
22
- val treeTransform = new Transform ( Set () )
34
+ override def runsAfter : Set [ Class [_ <: Phase ]] = Set (classOf [ Flatten ] )
23
35
24
- class Transform (dropped : Set [Symbol ]) extends TreeTransform {
25
- def phase = thisTransform
36
+ override def transformPackageDef (pdef : PackageDef )(implicit ctx : Context , info : TransformerInfo ) = {
26
37
27
38
/** 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 ) &&
31
41
tree.symbol.companionClass.exists &&
32
42
impl.body.forall(_.symbol.isPrimaryConstructor) =>
33
- // println(i"removing ${tree.symbol}")
43
+ println(i " removing ${tree.symbol}" )
34
44
true
35
45
case _ =>
36
46
false
37
47
}
38
48
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
50
50
51
51
/** Symbol is a $lzy field representing a module */
52
- private def isLazyModuleVar (sym : Symbol )( implicit ctx : Context ) =
52
+ def isLazyModuleVar (sym : Symbol ) =
53
53
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 )
55
55
56
56
/** Symbol should be dropped together with a dropped companion object.
57
57
* Such symbols are:
58
58
* - lzy fields pointing to modules,
59
59
* - vals and getters representing modules.
60
60
*/
61
- private def toDrop (sym : Symbol )( implicit ctx : Context ): Boolean =
61
+ def symIsDropped (sym : Symbol ): Boolean =
62
62
(sym.is(Module ) || isLazyModuleVar(sym)) &&
63
- dropped.contains(sym.info.resultType.typeSymbol)
63
+ dropped.contains(sym.info.resultType.typeSymbol)
64
64
65
65
/** Tree should be dropped because it (is associated with) an empty
66
66
* companion object. Such trees are
67
67
* - module classes of empty companion objects
68
68
* - definitions of lazy module variables or assignments to them.
69
69
* - vals and getters for empty companion objects
70
70
*/
71
- private def toDrop (stat : Tree )( implicit ctx : Context ): Boolean = stat match {
71
+ def toDrop (stat : Tree ): Boolean = stat match {
72
72
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)
75
75
case _ => false
76
76
}
77
77
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))
80
97
}
81
98
}
0 commit comments