@@ -70,6 +70,11 @@ case class StaticCall(cls: ClassSymbol, symbol: Symbol)(val source: Tree) extend
70
70
def show (using Context ): String = " StaticCall(" + cls.show + " , " + symbol.show + " )"
71
71
}
72
72
73
+ /** A static method call result is used */
74
+ case class ProxyUsage (cls : ClassSymbol , symbol : Symbol )(val source : Tree ) extends Dependency {
75
+ def show (using Context ): String = " ProxyUsage(" + cls.show + " , " + symbol.show + " )"
76
+ }
77
+
73
78
/** A class is used
74
79
*
75
80
* This is a coarse-grained abstraction
@@ -81,9 +86,10 @@ case class ClassUsage(symbol: Symbol)(val source: Tree) extends Dependency {
81
86
82
87
class CycleChecker (cache : Cache ) {
83
88
private val summaryCache = mutable.Map .empty[Symbol , List [Dependency ]]
89
+ private val proxyCache = mutable.Map .empty[Symbol , List [Dependency ]]
84
90
85
- private val classesInCurrentRun = mutable.Set .empty[Symbol ]
86
- private val objectsInCurrentRun = mutable.Set .empty[Symbol ]
91
+ val classesInCurrentRun = mutable.Set .empty[Symbol ]
92
+ val objectsInCurrentRun = mutable.Set .empty[Symbol ]
87
93
88
94
/** Checking state */
89
95
case class State (visited : mutable.Set [Dependency ], path : Vector [Symbol ], trace : Vector [Dependency ]) {
@@ -122,6 +128,7 @@ class CycleChecker(cache: Cache) {
122
128
dep match
123
129
case dep : InstanceUsage => checkInstanceUsage(dep)
124
130
case dep : StaticCall => checkStaticCall(dep)
131
+ case dep : ProxyUsage => checkProxyUsage(dep)
125
132
case dep : ClassUsage => checkClassUsage(dep)
126
133
}
127
134
}
@@ -167,6 +174,15 @@ class CycleChecker(cache: Cache) {
167
174
deps.flatMap(check(_))
168
175
}
169
176
177
+ private def checkProxyUsage (dep : ProxyUsage )(using Context , State ): List [Error ] =
178
+ if ! classesInCurrentRun.contains(dep.cls) then
179
+ Util .traceIndented(" skip " + dep.cls.show + " which is not in current run " , init)
180
+ Nil
181
+ else {
182
+ val deps = proxyDependencies(dep)
183
+ deps.flatMap(check(_))
184
+ }
185
+
170
186
private def checkClassUsage (dep : ClassUsage )(using Context , State ): List [Error ] =
171
187
if ! classesInCurrentRun.contains(dep.symbol) then
172
188
Util .traceIndented(" skip " + dep.symbol.show + " which is not in current run " , init)
@@ -184,7 +200,6 @@ class CycleChecker(cache: Cache) {
184
200
summaryCache(constr) = deps
185
201
val cls = constr.owner
186
202
187
- classesInCurrentRun += cls
188
203
if cls.is(Flags .Module ) && cls.isStatic then
189
204
objectsInCurrentRun += cls.sourceModule
190
205
@@ -206,14 +221,44 @@ class CycleChecker(cache: Cache) {
206
221
}
207
222
}
208
223
224
+ private def proxyDependencies (dep : ProxyUsage )(using Context ): List [Dependency ] = trace(" dependencies of " + dep.symbol.show, init, _.asInstanceOf [List [Dependency ]].map(_.show).toString) {
225
+ if (proxyCache.contains(dep.symbol)) summaryCache(dep.symbol)
226
+ else trace(" summary for " + dep.symbol.show) {
227
+ val env = Env (ctx.withOwner(dep.cls), cache)
228
+ val state = new Checking .State (
229
+ visited = Set .empty,
230
+ path = Vector .empty,
231
+ thisClass = dep.cls,
232
+ fieldsInited = mutable.Set .empty,
233
+ parentsInited = mutable.Set .empty,
234
+ safePromoted = mutable.Set (ThisRef ()(dep.cls.defTree)),
235
+ dependencies = mutable.Set .empty,
236
+ env = env
237
+ ) {
238
+ override def isFieldInitialized (field : Symbol ): Boolean = true
239
+ }
240
+
241
+ val pot = Hot (dep.cls)(dep.source)
242
+ val target = Util .resolve(dep.cls, dep.symbol)
243
+ val effs = pot.potentialsOf(target)(using env).promote(dep.source)
244
+
245
+ val errs = effs.flatMap(Checking .check(_)(using state))
246
+ assert(errs.isEmpty, " unexpected errors: " + Errors .show(errs.toList))
247
+
248
+ val deps = state.dependencies.toList
249
+ proxyCache(dep.symbol) = deps
250
+ deps
251
+ }
252
+ }
253
+
209
254
def isStaticObjectRef (sym : Symbol )(using Context ) =
210
255
sym.isTerm && ! sym.is(Flags .Package ) && sym.is(Flags .Module ) && sym.isStatic
211
256
212
257
private def analyzeClass (cls : ClassSymbol , excludeInit : Boolean )(using Context ): List [Dependency ] = {
213
258
val cdef = cls.defTree.asInstanceOf [TypeDef ]
214
259
val tpl = cdef.rhs.asInstanceOf [Template ]
215
260
216
- var deps : List [Dependency ] = Nil
261
+ var deps = new mutable. ListBuffer [Dependency ]
217
262
218
263
val traverser = new TreeTraverser {
219
264
override def traverse (tree : Tree )(using Context ): Unit =
@@ -222,7 +267,7 @@ class CycleChecker(cache: Cache) {
222
267
if ! excludeInit then
223
268
parents.foreach(traverse(_))
224
269
tree.body.foreach {
225
- case ddef : DefDef if ddef.symbol.isConstructor =>
270
+ case ddef : DefDef if ! ddef.symbol.isConstructor =>
226
271
traverse(ddef)
227
272
case vdef : ValDef if vdef.symbol.is(Flags .Lazy ) =>
228
273
traverse(vdef)
@@ -231,10 +276,10 @@ class CycleChecker(cache: Cache) {
231
276
}
232
277
233
278
case tree : RefTree if tree.isTerm =>
234
- analyzeType(tree.tpe, tree, exclude = cls)
279
+ deps ++= analyzeType(tree.tpe, tree, exclude = cls)
235
280
236
281
case tree : This =>
237
- analyzeType(tree.tpe, tree, exclude = cls)
282
+ deps ++= analyzeType(tree.tpe, tree, exclude = cls)
238
283
239
284
case tree : ValOrDefDef =>
240
285
traverseChildren(tree.rhs)
@@ -243,7 +288,7 @@ class CycleChecker(cache: Cache) {
243
288
// don't go into nested classes
244
289
245
290
case tree : New =>
246
- deps = ClassUsage (tree.tpe.classSymbol)(tree) :: deps
291
+ deps + = ClassUsage (tree.tpe.classSymbol)(tree)
247
292
248
293
case _ =>
249
294
traverseChildren(tree)
@@ -257,15 +302,15 @@ class CycleChecker(cache: Cache) {
257
302
if excludeInit then InstanceUsage (tp.classSymbol)(tree)
258
303
else ClassUsage (tp.classSymbol)(tree)
259
304
260
- deps = dep :: deps
305
+ deps + = dep
261
306
}
262
307
263
308
traverser.traverse(tpl)
264
- deps
309
+ deps.toList
265
310
}
266
311
267
- private def analyzeType (tp : Type , source : Tree , exclude : Symbol )(using Context ): Unit = tp match {
268
- case (_ : ConstantType ) | NoPrefix =>
312
+ private def analyzeType (tp : Type , source : Tree , exclude : Symbol )(using Context ): List [ Dependency ] = tp match {
313
+ case (_ : ConstantType ) | NoPrefix => Nil
269
314
270
315
case tmref : TermRef if isStaticObjectRef(tmref.symbol) =>
271
316
val obj = tmref.symbol
@@ -280,12 +325,15 @@ class CycleChecker(cache: Cache) {
280
325
val cls = tref.symbol
281
326
val obj = cls.sourceModule
282
327
ObjectAccess (obj)(source) :: InstanceUsage (cls)(source) :: Nil
328
+ else
329
+ Nil
283
330
284
331
case SuperType (thisTp, _) =>
285
332
analyzeType(thisTp, source, exclude)
286
333
287
334
case _ : TypeRef | _ : AppliedType =>
288
335
// possible from parent list
336
+ Nil
289
337
290
338
case _ =>
291
339
throw new Exception (" unexpected type: " + tp)
@@ -307,7 +355,8 @@ class CycleChecker(cache: Cache) {
307
355
}
308
356
309
357
val pot = Hot (dep.cls)(dep.source)
310
- val effs = pot.effectsOf(dep.symbol)(using env)
358
+ val target = Util .resolve(dep.cls, dep.symbol)
359
+ val effs = pot.effectsOf(target)(using env)
311
360
312
361
val errs = effs.flatMap(Checking .check(_)(using state))
313
362
assert(errs.isEmpty, " unexpected errors: " + Errors .show(errs.toList))
0 commit comments