1
1
package dotty .tools .dotc
2
2
package transform
3
3
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 }
22
9
import annotation .constructorOnly
23
10
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 `.
26
13
*/
27
- abstract class Dependencies (@ constructorOnly rootContext : Context ):
14
+ abstract class Dependencies (root : ast.tpd. Tree , @ constructorOnly rootContext : Context ):
28
15
import ast .tpd ._
29
16
30
- protected def enclosure ( using Context ) : Symbol
17
+ /** The symbol is a method or a lazy val that will be mapped to a method */
31
18
protected def isExpr (sym : Symbol )(using Context ): Boolean
32
19
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 ]
34
36
35
37
/** 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
37
39
38
40
/** A hashtable storing calls between functions */
39
41
private val called = new LinkedHashMap [Symbol , SymSet ]
@@ -45,26 +47,20 @@ abstract class Dependencies(@constructorOnly rootContext: Context):
45
47
* Note: During tree transform (which runs at phase LambdaLift + 1), liftedOwner
46
48
* is also used to decide whether a method had a term owner before.
47
49
*/
48
- private val depOwner = new LinkedHashMap [Symbol , Symbol ]
50
+ private val logicOwner = new LinkedHashMap [Symbol , Symbol ]
49
51
50
52
/** A flag to indicate whether new free variables have been found */
51
53
private var changedFreeVars : Boolean = _
52
54
53
55
/** A flag to indicate whether lifted owners have changed */
54
- private var changedLiftedOwner : Boolean = _
56
+ private var changedLogicOwner : Boolean = _
55
57
56
58
private val ord : Ordering [Symbol ] = Ordering .by(_.id)
57
59
private def newSymSet = TreeSet .empty[Symbol ](ord)
58
60
59
61
private def symSet (f : LinkedHashMap [Symbol , SymSet ], sym : Symbol ): SymSet =
60
62
f.getOrElseUpdate(sym, newSymSet)
61
63
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
-
68
64
/** A symbol is local if it is owned by a term or a local trait,
69
65
* or if it is a constructor of a local symbol.
70
66
* Note: we count members of local traits as local since their free variables
@@ -80,16 +76,14 @@ abstract class Dependencies(@constructorOnly rootContext: Context):
80
76
/** Set `liftedOwner(sym)` to `owner` if `owner` is more deeply nested
81
77
* than the previous value of `liftedowner(sym)`.
82
78
*/
83
- private def narrowLiftedOwner (sym : Symbol , owner : Symbol )(using Context ): Unit =
79
+ private def narrowLogicOwner (sym : Symbol , owner : Symbol )(using Context ): Unit =
84
80
if sym.maybeOwner.isTerm
85
- && owner.isProperlyContainedIn(depOwner (sym))
81
+ && owner.isProperlyContainedIn(logicOwner (sym))
86
82
&& owner != sym
87
83
then
88
84
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
93
87
94
88
/** Mark symbol `sym` as being free in `enclosure`, unless `sym` is defined
95
89
* in `enclosure` or there is an intermediate class properly containing `enclosure`
@@ -135,42 +129,40 @@ abstract class Dependencies(@constructorOnly rootContext: Context):
135
129
* }
136
130
* }
137
131
*/
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 =>
171
164
println(i " error lambda lifting ${ctx.compilationUnit}: $sym is not visible from $enclosure" )
172
165
throw ex
173
- }
174
166
175
167
private def markCalled (callee : Symbol , caller : Symbol )(using Context ): Unit = {
176
168
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):
184
176
def narrowTo (thisClass : ClassSymbol ) =
185
177
val enclMethod = enclosure
186
178
val enclClass = enclMethod.enclosingClass
187
- narrowLiftedOwner (enclMethod,
179
+ narrowLogicOwner (enclMethod,
188
180
if enclClass.isContainedIn(thisClass) then thisClass
189
181
else enclClass) // unknown this reference, play it safe and assume the narrowest possible owner
190
182
@@ -204,7 +196,7 @@ abstract class Dependencies(@constructorOnly rootContext: Context):
204
196
narrowTo(tree.symbol.asClass)
205
197
case tree : DefDef =>
206
198
if sym.owner.isTerm then
207
- depOwner (sym) = sym.enclosingPackageClass
199
+ logicOwner (sym) = sym.enclosingPackageClass
208
200
// this will make methods in supercall constructors of top-level classes owned
209
201
// by the enclosing package, which means they will be static.
210
202
// On the other hand, all other methods will be indirectly owned by their
@@ -217,7 +209,7 @@ abstract class Dependencies(@constructorOnly rootContext: Context):
217
209
// the free variables of the class.
218
210
symSet(called, sym) += sym.owner
219
211
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
221
213
case _ =>
222
214
end process
223
215
@@ -245,29 +237,32 @@ abstract class Dependencies(@constructorOnly rootContext: Context):
245
237
do ()
246
238
247
239
/** Compute final liftedOwner map by closing over caller dependencies */
248
- private def computeLiftedOwners ()(using Context ): Unit =
240
+ private def computeLogicOwners ()(using Context ): Unit =
249
241
while
250
- changedLiftedOwner = false
242
+ changedLogicOwner = false
251
243
for
252
244
caller <- called.keys
253
245
callee <- called(caller)
254
246
do
255
247
val normalizedCallee = callee.skipConstructor
256
248
val calleeOwner = normalizedCallee.owner
257
- if calleeOwner.isTerm then narrowLiftedOwner (caller, depOwner (normalizedCallee))
249
+ if calleeOwner.isTerm then narrowLogicOwner (caller, logicOwner (normalizedCallee))
258
250
else
259
251
assert(calleeOwner.is(Trait ))
260
252
// methods nested inside local trait methods cannot be lifted out
261
253
// beyond the trait. Note that we can also call a trait method through
262
254
// a qualifier; in that case no restriction to lifted owner arises.
263
255
if caller.isContainedIn(calleeOwner) then
264
- narrowLiftedOwner (caller, calleeOwner)
265
- changedLiftedOwner
256
+ narrowLogicOwner (caller, calleeOwner)
257
+ changedLogicOwner
266
258
do ()
267
259
260
+ // initialization
268
261
inContext(rootContext) {
269
- CollectDependencies ().traverse(rootContext.compilationUnit.tpdTree )
262
+ CollectDependencies ().traverse(root )
270
263
computeFreeVars()
271
- computeLiftedOwners ()
264
+ computeLogicOwners ()
272
265
}
266
+ object Dependencies :
267
+ private class NoPath extends Exception
273
268
end Dependencies
0 commit comments