11package dotty .tools .dotc
22package transform
33
4- import MegaPhase ._
5- import core .Denotations .NonSymSingleDenotation
6- import core .DenotTransformers ._
7- import core .Symbols ._
8- import core .Contexts ._
9- import core .Types ._
10- import core .Flags ._
11- import core .Decorators ._
12- import core .StdNames .nme
13- import core .Names ._
14- import core .NameOps ._
15- import core .NameKinds .ExpandPrefixName
16- import ast .Trees ._
17- import SymUtils ._
18- import ExplicitOuter .outer
19- import util .Store
20- import collection .mutable
21- import collection .mutable .{ HashMap , HashSet , LinkedHashMap , TreeSet }
4+ import core .*
5+ import Symbols .* , Contexts .* , Types .* , Flags .* , Decorators .*
6+ import ast .Trees .*
7+ import SymUtils .*
8+ import collection .mutable .{LinkedHashMap , TreeSet }
229import annotation .constructorOnly
2310
24- /** Exposes the dependencies of the typed tree in the current compilation unit
25- * in sets `freeVars`, `liftedOwner `.
11+ /** Exposes the dependencies of the `root` tree in three functions or maps:
12+ * `freeVars`, `tracked`, and `logicalOwner `.
2613 */
27- abstract class Dependencies (@ constructorOnly rootContext : Context ):
14+ abstract class Dependencies (root : ast.tpd. Tree , @ constructorOnly rootContext : Context ):
2815 import ast .tpd ._
2916
30- protected def enclosure ( using Context ) : Symbol
17+ /** The symbol is a method or a lazy val that will be mapped to a method */
3118 protected def isExpr (sym : Symbol )(using Context ): Boolean
3219
33- type SymSet = TreeSet [Symbol ]
20+ /** The closest enclosing symbol in the current context for which `isExpr` is true */
21+ protected def enclosure (using Context ): Symbol
22+
23+ /** The set of free variables of a function, including free variables of its callees */
24+ def freeVars (sym : Symbol ): collection.Set [Symbol ] = free.getOrElse(sym, Set .empty)
25+
26+ /** The set of functions that have free variables, i.e for which `freeVars` is non-empty */
27+ def tracked : Iterable [Symbol ] = free.keys
28+
29+ /** The outermost class that captures all free variables of a function
30+ * that are captured by enclosinh classes (this means that the function could
31+ * be placed in that class without having to add more environment parameters)
32+ */
33+ def logicalOwner : collection.Map [Symbol , Symbol ] = logicOwner
34+
35+ private type SymSet = TreeSet [Symbol ]
3436
3537 /** A map storing free variables of functions and classes */
36- private val free : mutable. LinkedHashMap [Symbol , SymSet ] = new LinkedHashMap
38+ private val free : LinkedHashMap [Symbol , SymSet ] = new LinkedHashMap
3739
3840 /** A hashtable storing calls between functions */
3941 private val called = new LinkedHashMap [Symbol , SymSet ]
@@ -45,26 +47,20 @@ abstract class Dependencies(@constructorOnly rootContext: Context):
4547 * Note: During tree transform (which runs at phase LambdaLift + 1), liftedOwner
4648 * is also used to decide whether a method had a term owner before.
4749 */
48- private val depOwner = new LinkedHashMap [Symbol , Symbol ]
50+ private val logicOwner = new LinkedHashMap [Symbol , Symbol ]
4951
5052 /** A flag to indicate whether new free variables have been found */
5153 private var changedFreeVars : Boolean = _
5254
5355 /** A flag to indicate whether lifted owners have changed */
54- private var changedLiftedOwner : Boolean = _
56+ private var changedLogicOwner : Boolean = _
5557
5658 private val ord : Ordering [Symbol ] = Ordering .by(_.id)
5759 private def newSymSet = TreeSet .empty[Symbol ](ord)
5860
5961 private def symSet (f : LinkedHashMap [Symbol , SymSet ], sym : Symbol ): SymSet =
6062 f.getOrElseUpdate(sym, newSymSet)
6163
62- def freeVars (sym : Symbol ): collection.Set [Symbol ] = free.getOrElse(sym, Set .empty)
63-
64- def tracked : Iterable [Symbol ] = free.keys
65-
66- def dependentOwner : collection.Map [Symbol , Symbol ] = depOwner
67-
6864 /** A symbol is local if it is owned by a term or a local trait,
6965 * or if it is a constructor of a local symbol.
7066 * Note: we count members of local traits as local since their free variables
@@ -80,16 +76,14 @@ abstract class Dependencies(@constructorOnly rootContext: Context):
8076 /** Set `liftedOwner(sym)` to `owner` if `owner` is more deeply nested
8177 * than the previous value of `liftedowner(sym)`.
8278 */
83- private def narrowLiftedOwner (sym : Symbol , owner : Symbol )(using Context ): Unit =
79+ private def narrowLogicOwner (sym : Symbol , owner : Symbol )(using Context ): Unit =
8480 if sym.maybeOwner.isTerm
85- && owner.isProperlyContainedIn(depOwner (sym))
81+ && owner.isProperlyContainedIn(logicOwner (sym))
8682 && owner != sym
8783 then
8884 report.log(i " narrow lifted $sym to $owner" )
89- changedLiftedOwner = true
90- depOwner(sym) = owner
91-
92- private class NoPath extends Exception
85+ changedLogicOwner = true
86+ logicOwner(sym) = owner
9387
9488 /** Mark symbol `sym` as being free in `enclosure`, unless `sym` is defined
9589 * in `enclosure` or there is an intermediate class properly containing `enclosure`
@@ -135,42 +129,40 @@ abstract class Dependencies(@constructorOnly rootContext: Context):
135129 * }
136130 * }
137131 */
138- private def markFree (sym : Symbol , enclosure : Symbol )(using Context ): Symbol = try {
139- if (! enclosure.exists) throw new NoPath
140- if (enclosure == sym.enclosure) NoSymbol
141- else {
142- def nestedInConstructor (sym : Symbol ): Boolean =
143- sym.isConstructor ||
144- sym.isTerm && nestedInConstructor(sym.enclosure)
145- report.debuglog(i " mark free: ${sym.showLocated} with owner ${sym.maybeOwner} marked free in $enclosure" )
146- val intermediate =
147- if (enclosure.is(PackageClass )) enclosure
148- else if (enclosure.isConstructor) markFree(sym, enclosure.owner.enclosure)
149- else markFree(sym, enclosure.enclosure)
150- if (intermediate.exists) narrowLiftedOwner(enclosure, intermediate)
151- if ! intermediate.isRealClass || nestedInConstructor(enclosure) then
152- // Constructors and methods nested inside traits get the free variables
153- // of the enclosing trait or class.
154- // Conversely, local traits do not get free variables.
155- // Methods inside constructors also don't have intermediates,
156- // need to get all their free variables passed directly.
157- if (! enclosure.is(Trait ))
158- if (symSet(free, enclosure).add(sym)) {
159- changedFreeVars = true
160- report.log(i " $sym is free in $enclosure" )
161- }
162- if (intermediate.isRealClass) intermediate
163- else if (enclosure.isRealClass) enclosure
164- else if (intermediate.isClass) intermediate
165- else if (enclosure.isClass) enclosure
166- else NoSymbol
167- }
168- }
169- catch {
170- case ex : NoPath =>
132+ private def markFree (sym : Symbol , enclosure : Symbol )(using Context ): Symbol =
133+ import Dependencies .NoPath
134+ try
135+ if ! enclosure.exists then throw NoPath ()
136+ if enclosure == sym.enclosure then NoSymbol
137+ else
138+ def nestedInConstructor (sym : Symbol ): Boolean =
139+ sym.isConstructor
140+ || sym.isTerm && nestedInConstructor(sym.enclosure)
141+ report.debuglog(i " mark free: ${sym.showLocated} with owner ${sym.maybeOwner} marked free in $enclosure" )
142+ val intermediate =
143+ if enclosure.is(PackageClass ) then enclosure
144+ else if enclosure.isConstructor then markFree(sym, enclosure.owner.enclosure)
145+ else markFree(sym, enclosure.enclosure)
146+ if intermediate.exists then
147+ narrowLogicOwner(enclosure, intermediate)
148+ if ! intermediate.isRealClass || nestedInConstructor(enclosure) then
149+ // Constructors and methods nested inside traits get the free variables
150+ // of the enclosing trait or class.
151+ // Conversely, local traits do not get free variables.
152+ // Methods inside constructors also don't have intermediates,
153+ // need to get all their free variables passed directly.
154+ if ! enclosure.is(Trait ) then
155+ if symSet(free, enclosure).add(sym) then
156+ changedFreeVars = true
157+ report.log(i " $sym is free in $enclosure" )
158+ if intermediate.isRealClass then intermediate
159+ else if enclosure.isRealClass then enclosure
160+ else if intermediate.isClass then intermediate
161+ else if enclosure.isClass then enclosure
162+ else NoSymbol
163+ catch case ex : NoPath =>
171164 println(i " error lambda lifting ${ctx.compilationUnit}: $sym is not visible from $enclosure" )
172165 throw ex
173- }
174166
175167 private def markCalled (callee : Symbol , caller : Symbol )(using Context ): Unit = {
176168 report.debuglog(i " mark called: $callee of ${callee.owner} is called by $caller in ${caller.owner}" )
@@ -184,7 +176,7 @@ abstract class Dependencies(@constructorOnly rootContext: Context):
184176 def narrowTo (thisClass : ClassSymbol ) =
185177 val enclMethod = enclosure
186178 val enclClass = enclMethod.enclosingClass
187- narrowLiftedOwner (enclMethod,
179+ narrowLogicOwner (enclMethod,
188180 if enclClass.isContainedIn(thisClass) then thisClass
189181 else enclClass) // unknown this reference, play it safe and assume the narrowest possible owner
190182
@@ -204,7 +196,7 @@ abstract class Dependencies(@constructorOnly rootContext: Context):
204196 narrowTo(tree.symbol.asClass)
205197 case tree : DefDef =>
206198 if sym.owner.isTerm then
207- depOwner (sym) = sym.enclosingPackageClass
199+ logicOwner (sym) = sym.enclosingPackageClass
208200 // this will make methods in supercall constructors of top-level classes owned
209201 // by the enclosing package, which means they will be static.
210202 // On the other hand, all other methods will be indirectly owned by their
@@ -217,7 +209,7 @@ abstract class Dependencies(@constructorOnly rootContext: Context):
217209 // the free variables of the class.
218210 symSet(called, sym) += sym.owner
219211 case tree : TypeDef =>
220- if sym.owner.isTerm then depOwner (sym) = sym.topLevelClass.owner
212+ if sym.owner.isTerm then logicOwner (sym) = sym.topLevelClass.owner
221213 case _ =>
222214 end process
223215
@@ -245,29 +237,32 @@ abstract class Dependencies(@constructorOnly rootContext: Context):
245237 do ()
246238
247239 /** Compute final liftedOwner map by closing over caller dependencies */
248- private def computeLiftedOwners ()(using Context ): Unit =
240+ private def computeLogicOwners ()(using Context ): Unit =
249241 while
250- changedLiftedOwner = false
242+ changedLogicOwner = false
251243 for
252244 caller <- called.keys
253245 callee <- called(caller)
254246 do
255247 val normalizedCallee = callee.skipConstructor
256248 val calleeOwner = normalizedCallee.owner
257- if calleeOwner.isTerm then narrowLiftedOwner (caller, depOwner (normalizedCallee))
249+ if calleeOwner.isTerm then narrowLogicOwner (caller, logicOwner (normalizedCallee))
258250 else
259251 assert(calleeOwner.is(Trait ))
260252 // methods nested inside local trait methods cannot be lifted out
261253 // beyond the trait. Note that we can also call a trait method through
262254 // a qualifier; in that case no restriction to lifted owner arises.
263255 if caller.isContainedIn(calleeOwner) then
264- narrowLiftedOwner (caller, calleeOwner)
265- changedLiftedOwner
256+ narrowLogicOwner (caller, calleeOwner)
257+ changedLogicOwner
266258 do ()
267259
260+ // initialization
268261 inContext(rootContext) {
269- CollectDependencies ().traverse(rootContext.compilationUnit.tpdTree )
262+ CollectDependencies ().traverse(root )
270263 computeFreeVars()
271- computeLiftedOwners ()
264+ computeLogicOwners ()
272265 }
266+ object Dependencies :
267+ private class NoPath extends Exception
273268end Dependencies
0 commit comments