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 DenotTransformers ._
13+ import StdNames .nme
14+ import collection .mutable
15+
16+ object DropOuterAccessors :
17+ val name : String = " dropOuterAccessors"
18+
19+ /** Drops outer accessors of final classes that are unused */
20+ class DropOuterAccessors extends MiniPhase with IdentityDenotTransformer :
21+ thisPhase =>
22+ import tpd ._
23+
24+ override def phaseName : String = DropOuterAccessors .name
25+
26+ override def runsAfter : Set [String ] = Set (LambdaLift .name)
27+ // LambdaLift can create outer paths. These need to be known in this phase
28+
29+ override def changesMembers : Boolean = true // the phase drops outer accessors
30+
31+ def (sym : Symbol ).isOuterParamAccessor(using Context ) =
32+ sym.is(ParamAccessor ) && sym.name == nme.OUTER
33+
34+ private def mightBeDropped (sym : Symbol )(using Context ) =
35+ (sym.is(OuterAccessor ) || sym.isOuterParamAccessor)
36+ && ! sym.owner.isExtensibleClass
37+
38+ /** The number of times an outer accessor that might be dropped is accessed */
39+ private val accessCount = new mutable.HashMap [Symbol , Int ]:
40+ override def default (key : Symbol ): Int = 0
41+
42+ private def markAccessed (tree : RefTree )(implicit ctx : Context ): Tree =
43+ val sym = tree.symbol
44+ if mightBeDropped(sym) then accessCount(sym) += 1
45+ tree
46+
47+ override def transformIdent (tree : Ident )(using Context ): Tree =
48+ markAccessed(tree)
49+
50+ override def transformSelect (tree : Select )(using Context ): Tree =
51+ markAccessed(tree)
52+
53+ override def transformTemplate (impl : Template )(using ctx : Context ): Tree =
54+
55+ def dropOuterAccessor (stat : Tree ): Boolean = stat match
56+ case stat : DefDef
57+ if stat.symbol.is(OuterAccessor )
58+ && mightBeDropped(stat.symbol)
59+ && accessCount(stat.symbol) == 0 =>
60+ assert(stat.rhs.isInstanceOf [RefTree ], stat)
61+ assert(accessCount(stat.rhs.symbol) > 0 )
62+ accessCount(stat.rhs.symbol) -= 1
63+ stat.symbol.dropAfter(thisPhase)
64+ true
65+ case _ =>
66+ false
67+
68+ val droppedParamAccessors = mutable.Set [Symbol ]()
69+
70+ def dropOuterParamAccessor (stat : Tree ): Boolean = stat match
71+ case stat : ValDef
72+ if stat.symbol.isOuterParamAccessor
73+ && mightBeDropped(stat.symbol)
74+ && accessCount(stat.symbol) == 1 =>
75+ droppedParamAccessors += stat.symbol
76+ stat.symbol.dropAfter(thisPhase)
77+ true
78+ case _ =>
79+ false
80+
81+ def dropOuterInit (stat : Tree ): Boolean = stat match
82+ case Assign (lhs, rhs) => droppedParamAccessors.remove(lhs.symbol)
83+ case _ => false
84+
85+ val body1 = impl.body
86+ .filterNot(dropOuterAccessor)
87+ .filterNot(dropOuterParamAccessor)
88+ val constr1 =
89+ if droppedParamAccessors.isEmpty then impl.constr
90+ else cpy.DefDef (impl.constr)(
91+ rhs = impl.constr.rhs match {
92+ case rhs @ Block (inits, expr) =>
93+ cpy.Block (rhs)(inits.filterNot(dropOuterInit), expr)
94+ })
95+ assert(droppedParamAccessors.isEmpty,
96+ i """ Failed to eliminate: $droppedParamAccessors
97+ when dropping outer accessors for ${ctx.owner} with
98+ $impl""" )
99+ cpy.Template (impl)(constr = constr1, body = body1)
100+ end transformTemplate
0 commit comments