Skip to content

Commit 363985e

Browse files
committed
Drop more outer accessors
We now also drop outer accessors of classes that are visible only in one toplevel class. This requires counting accessors in a complete traversal. We therefore need two phases in separate groups to do the job. The first phase counts, the second elimintaes accessors using the info gathered in the first phase.
1 parent 3c138ea commit 363985e

File tree

5 files changed

+83
-36
lines changed

5 files changed

+83
-36
lines changed

compiler/src/dotty/tools/dotc/Compiler.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,9 @@ class Compiler {
111111
new LambdaLift, // Lifts out nested functions to class scope, storing free variables in environments
112112
// Note: in this mini-phase block scopes are incorrect. No phases that rely on scopes should be here
113113
new ElimStaticThis, // Replace `this` references to static objects by global identifiers
114-
new DropOuterAccessors) :: // Drop unused outer accessors
115-
List(new Flatten, // Lift all inner classes to package scope
114+
new CountOuterAccesses) :: // Identify outer accessors that can be dropped
115+
List(new DropOuterAccessors, // Drop unused outer accessors
116+
new Flatten, // Lift all inner classes to package scope
116117
new RenameLifted, // Renames lifted classes to local numbering scheme
117118
new TransformWildcards, // Replace wildcards with default values
118119
new MoveStatics, // Move static methods from companion to the class itself

compiler/src/dotty/tools/dotc/core/Phases.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ object Phases {
230230
private var myErasurePhase: Phase = _
231231
private var myElimErasedValueTypePhase: Phase = _
232232
private var myLambdaLiftPhase: Phase = _
233+
private var myCountOuterAccessesPhase: Phase = _
233234
private var myFlattenPhase: Phase = _
234235
private var myGenBCodePhase: Phase = _
235236

@@ -248,6 +249,7 @@ object Phases {
248249
final def erasurePhase: Phase = myErasurePhase
249250
final def elimErasedValueTypePhase: Phase = myElimErasedValueTypePhase
250251
final def lambdaLiftPhase: Phase = myLambdaLiftPhase
252+
final def countOuterAccessesPhase = myCountOuterAccessesPhase
251253
final def flattenPhase: Phase = myFlattenPhase
252254
final def genBCodePhase: Phase = myGenBCodePhase
253255

@@ -267,6 +269,7 @@ object Phases {
267269
myElimErasedValueTypePhase = phaseOfClass(classOf[ElimErasedValueType])
268270
myPatmatPhase = phaseOfClass(classOf[PatternMatcher])
269271
myLambdaLiftPhase = phaseOfClass(classOf[LambdaLift])
272+
myCountOuterAccessesPhase = phaseOfClass(classOf[CountOuterAccesses])
270273
myFlattenPhase = phaseOfClass(classOf[Flatten])
271274
myExplicitOuterPhase = phaseOfClass(classOf[ExplicitOuter])
272275
myGettersPhase = phaseOfClass(classOf[Getters])
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
package dotty.tools.dotc
2+
package transform
3+
4+
import core._
5+
import MegaPhase.MiniPhase
6+
import dotty.tools.dotc.core.Contexts.Context
7+
import ast._
8+
import Trees._
9+
import Flags._
10+
import Symbols._
11+
import Decorators._
12+
import ExplicitOuter.isOuterParamAccessor
13+
14+
import collection.mutable
15+
16+
object CountOuterAccesses:
17+
val name: String = "countOuterAccesses"
18+
19+
/** Characterizes outer accessors and outer fields that can be dropped
20+
* if there are no references to them from within the toplevel class
21+
* where they are defined.
22+
*/
23+
def mightBeDropped(sym: Symbol)(using Context) =
24+
def isLocal(cls: Symbol) =
25+
cls.isAnonymousClass
26+
|| cls.owner.isTerm
27+
|| cls.accessBoundary(defn.RootClass).isContainedIn(cls.topLevelClass)
28+
(sym.is(OuterAccessor) || sym.isOuterParamAccessor) && isLocal(sym.owner)
29+
30+
/** Counts number of accesses to outer accessors and outer fields of
31+
* classes that are visible only within one source file. The info
32+
* is collected in `outerAccessCount` and used in the subsequent
33+
* DropOuterAccessors phase
34+
*/
35+
class CountOuterAccesses extends MiniPhase:
36+
thisPhase =>
37+
import tpd._
38+
39+
override def phaseName: String = CountOuterAccesses.name
40+
41+
override def runsAfter: Set[String] = Set(LambdaLift.name)
42+
// LambdaLift can create outer paths. These need to be known in this phase.
43+
44+
/** The number of times an outer accessor that might be dropped is accessed */
45+
val outerAccessCount = new mutable.HashMap[Symbol, Int] {
46+
override def default(s: Symbol): Int = 0
47+
}
48+
49+
private def markAccessed(tree: RefTree)(implicit ctx: Context): Tree =
50+
val sym = tree.symbol
51+
if CountOuterAccesses.mightBeDropped(sym) then outerAccessCount(sym) += 1
52+
tree
53+
54+
override def transformIdent(tree: Ident)(using Context): Tree =
55+
markAccessed(tree)
56+
57+
override def transformSelect(tree: Select)(using Context): Tree =
58+
markAccessed(tree)

compiler/src/dotty/tools/dotc/transform/DropOuterAccessors.scala

Lines changed: 16 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,62 +8,44 @@ import ast._
88
import Trees._
99
import Flags._
1010
import Symbols._
11+
import Contexts._
1112
import Decorators._
1213
import DenotTransformers._
13-
import StdNames.nme
14+
import ExplicitOuter.isOuterParamAccessor
15+
import CountOuterAccesses.mightBeDropped
1416
import collection.mutable
17+
import annotation.threadUnsafe
1518

1619
object DropOuterAccessors:
1720
val name: String = "dropOuterAccessors"
1821

19-
/** Drops outer accessors of final classes that are unused */
22+
/** Drops unused outer accessors of inner classes that are visible only in one
23+
* toplevel class. For other classes, we can't tell whether an outer accessor
24+
* is used or not. It could for instance be used in a type test in some other source.
25+
*/
2026
class DropOuterAccessors extends MiniPhase with IdentityDenotTransformer:
2127
thisPhase =>
2228
import tpd._
2329

2430
override def phaseName: String = DropOuterAccessors.name
2531

26-
override def runsAfter: Set[String] = Set(LambdaLift.name)
27-
// LambdaLift can create outer paths. These need to be known in this phase
32+
override def runsAfterGroupsOf: Set[String] = Set(CountOuterAccesses.name)
2833

2934
override def changesMembers: Boolean = true // the phase drops outer accessors
3035

31-
def (sym: Symbol).isOuterParamAccessor(using Context) =
32-
sym.is(ParamAccessor) && sym.name == nme.OUTER
33-
34-
/** Characterizes outer accessors and outer fields that can be dropped
35-
* if there are no references to them from within the class in which they
36-
* are defined.
37-
*/
38-
private def mightBeDropped(sym: Symbol)(using Context) =
39-
(sym.is(OuterAccessor) || sym.isOuterParamAccessor)
40-
&& sym.owner.isAnonymousClass
41-
42-
/** The number of times an outer accessor that might be dropped is accessed */
43-
private val accessCount = new mutable.HashMap[Symbol, Int]:
44-
override def default(key: Symbol): Int = 0
45-
46-
private def markAccessed(tree: RefTree)(implicit ctx: Context): Tree =
47-
val sym = tree.symbol
48-
if mightBeDropped(sym) then accessCount(sym) += 1
49-
tree
50-
51-
override def transformIdent(tree: Ident)(using Context): Tree =
52-
markAccessed(tree)
53-
54-
override def transformSelect(tree: Select)(using Context): Tree =
55-
markAccessed(tree)
56-
5736
override def transformTemplate(impl: Template)(using ctx: Context): Tree =
37+
val outerAccessCount = ctx.base.countOuterAccessesPhase
38+
.asInstanceOf[CountOuterAccesses]
39+
.outerAccessCount
5840

5941
def dropOuterAccessor(stat: Tree): Boolean = stat match
6042
case stat: DefDef
6143
if stat.symbol.is(OuterAccessor)
6244
&& mightBeDropped(stat.symbol)
63-
&& accessCount(stat.symbol) == 0 =>
45+
&& outerAccessCount(stat.symbol) == 0 =>
6446
assert(stat.rhs.isInstanceOf[RefTree], stat)
65-
assert(accessCount(stat.rhs.symbol) > 0)
66-
accessCount(stat.rhs.symbol) -= 1
47+
assert(outerAccessCount(stat.rhs.symbol) > 0)
48+
outerAccessCount(stat.rhs.symbol) -= 1
6749
stat.symbol.dropAfter(thisPhase)
6850
true
6951
case _ =>
@@ -75,7 +57,7 @@ class DropOuterAccessors extends MiniPhase with IdentityDenotTransformer:
7557
case stat: ValDef
7658
if stat.symbol.isOuterParamAccessor
7759
&& mightBeDropped(stat.symbol)
78-
&& accessCount(stat.symbol) == 1 =>
60+
&& outerAccessCount(stat.symbol) == 1 =>
7961
droppedParamAccessors += stat.symbol
8062
stat.symbol.dropAfter(thisPhase)
8163
true

compiler/src/dotty/tools/dotc/transform/ExplicitOuter.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,9 @@ object ExplicitOuter {
323323
tpe
324324
}
325325

326+
def (sym: Symbol).isOuterParamAccessor(using Context): Boolean =
327+
sym.is(ParamAccessor) && sym.name == nme.OUTER
328+
326329
def outer(implicit ctx: Context): OuterOps = new OuterOps(ctx)
327330

328331
/** The operations in this class

0 commit comments

Comments
 (0)