Skip to content

Commit 1c2a6fb

Browse files
committed
Cleanups
1 parent 8888767 commit 1c2a6fb

File tree

2 files changed

+94
-101
lines changed

2 files changed

+94
-101
lines changed

compiler/src/dotty/tools/dotc/transform/DependencyCollector.scala renamed to compiler/src/dotty/tools/dotc/transform/Dependencies.scala

Lines changed: 77 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,41 @@
11
package dotty.tools.dotc
22
package 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}
229
import 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
273268
end Dependencies

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

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,10 @@ import ast.Trees._
1717
import SymUtils._
1818
import ExplicitOuter.outer
1919
import util.Store
20-
import collection.mutable
21-
import collection.mutable.{ HashMap, HashSet, LinkedHashMap, TreeSet }
20+
import collection.mutable.{HashMap, LinkedHashMap, ListBuffer}
2221

23-
object LambdaLift {
22+
object LambdaLift:
2423
import ast.tpd._
25-
private class NoPath extends Exception
2624

2725
val name: String = "lambdaLift"
2826

@@ -33,11 +31,11 @@ object LambdaLift {
3331
private val outerParam = new HashMap[Symbol, Symbol]
3432

3533
/** Buffers for lifted out classes and methods, indexed by owner */
36-
val liftedDefs: mutable.HashMap[Symbol, mutable.ListBuffer[Tree]] = new HashMap
34+
val liftedDefs: HashMap[Symbol, ListBuffer[Tree]] = new HashMap
3735

38-
val deps = new Dependencies(ctx.withPhase(thisPhase)):
39-
def enclosure(using Context) = ctx.owner.enclosingMethod
36+
val deps = new Dependencies(ctx.compilationUnit.tpdTree, ctx.withPhase(thisPhase)):
4037
def isExpr(sym: Symbol)(using Context): Boolean = sym.is(Method)
38+
def enclosure(using Context) = ctx.owner.enclosingMethod
4139

4240
override def process(tree: Tree)(using Context): Unit =
4341
super.process(tree)
@@ -47,7 +45,7 @@ object LambdaLift {
4745
case Some(vdef) => outerParam(tree.symbol) = vdef.symbol
4846
case _ =>
4947
case tree: Template =>
50-
liftedDefs(tree.symbol.owner) = new mutable.ListBuffer
48+
liftedDefs(tree.symbol.owner) = new ListBuffer
5149
case _ =>
5250
end deps
5351

@@ -94,7 +92,7 @@ object LambdaLift {
9492
}
9593

9694
private def liftLocals()(using Context): Unit = {
97-
for ((local, lOwner) <- deps.dependentOwner) {
95+
for ((local, lOwner) <- deps.logicalOwner) {
9896
val (newOwner, maybeStatic) =
9997
if (lOwner is Package) {
10098
val encClass = local.enclosingClass
@@ -130,16 +128,10 @@ object LambdaLift {
130128
info = liftedInfo(local)).installAfter(thisPhase)
131129
}
132130
for (local <- deps.tracked)
133-
if (!deps.dependentOwner.contains(local))
131+
if (!deps.logicalOwner.contains(local))
134132
local.copySymDenotation(info = liftedInfo(local)).installAfter(thisPhase)
135133
}
136134

137-
// initialization
138-
atPhase(thisPhase.next) {
139-
generateProxies()
140-
liftLocals()
141-
}
142-
143135
def currentEnclosure(using Context): Symbol =
144136
ctx.owner.enclosingMethodOrClass
145137

@@ -148,7 +140,7 @@ object LambdaLift {
148140

149141
private def proxy(sym: Symbol)(using Context): Symbol = {
150142
def liftedEnclosure(sym: Symbol) =
151-
deps.dependentOwner.getOrElse(sym, sym.enclosure)
143+
deps.logicalOwner.getOrElse(sym, sym.enclosure)
152144
def searchIn(enclosure: Symbol): Symbol = {
153145
if (!enclosure.exists) {
154146
def enclosures(encl: Symbol): List[Symbol] =
@@ -228,9 +220,15 @@ object LambdaLift {
228220
EmptyTree
229221
}
230222

231-
def needsLifting(sym: Symbol): Boolean = deps.dependentOwner.contains(sym)
223+
def needsLifting(sym: Symbol): Boolean = deps.logicalOwner.contains(sym)
224+
225+
// initialization
226+
atPhase(thisPhase.next) {
227+
generateProxies()
228+
liftLocals()
229+
}
232230
end Lifter
233-
}
231+
end LambdaLift
234232

235233
/** This phase performs the necessary rewritings to eliminate classes and methods
236234
* nested in other methods. In detail:

0 commit comments

Comments
 (0)