@@ -264,55 +264,125 @@ object Checking {
264
264
val Summary (pots, effs) = expand(pot)
265
265
val effs2 = pots.map(FieldAccess (_, field)(eff.source))
266
266
(effs2 ++ effs).toList.flatMap(check(_))
267
+ }
268
+
269
+ /**
270
+ * Check if we can just directly promote a potential.
271
+ * A potential can be (currently) directly promoted if and only if:
272
+ * - `pot == this` and all fields of this are initialized, or
273
+ * - `pot == Warm(C, outer)` where `outer` can be directly promoted.
274
+ */
275
+ private def canDirectlyPromote (pot : Potential , visited : Set [Potential ] = Set .empty)(using state : State ): Boolean = trace(" checking direct promotion of " + pot.show, init) {
276
+ if (state.safePromoted.contains(pot)) true
277
+ // If this potential's promotion depends on itself, we cannot directly promote it.
278
+ else if (visited.contains(pot)) false
279
+ else pot match {
280
+ case pot : ThisRef =>
281
+ // If we have all fields initialized, then we can promote This to hot.
282
+ val classRef = state.thisClass.info.asInstanceOf [ClassInfo ].appliedRef
283
+ classRef.fields.forall { denot =>
284
+ val sym = denot.symbol
285
+ sym.isOneOf(Flags .Lazy | Flags .Deferred ) || state.fieldsInited.contains(sym)
286
+ }
287
+ case Warm (cls, outer) =>
288
+ canDirectlyPromote(outer)
289
+ case _ =>
290
+ val summary = expand(pot)
291
+ if (! summary.effs.isEmpty)
292
+ false // max depth of expansion reached
293
+ else summary.pots.forall(canDirectlyPromote(_, visited + pot))
294
+ }
295
+ }
296
+
297
+ /**
298
+ * Check the Promotion of a Warm object, according to "Rule 2":
299
+ *
300
+ * Rule 2: Promote(pot)
301
+ *
302
+ * for all concrete methods `m` of D
303
+ * pot.m!, Promote(pot.m)
304
+ *
305
+ * for all concrete fields `f` of D
306
+ * Promote(pot.f)
307
+ *
308
+ * for all inner classes `F` of D
309
+ * Warm[F, pot].init!, Promote(Warm[F, pot])
310
+ */
311
+ private def checkPromoteWarm (warm : Warm , eff : Effect )(using state : State ): Errors =
312
+ val Warm (cls, outer) = warm
313
+ val source = eff.source
314
+ // Errors.empty
315
+ val classRef = cls.info.asInstanceOf [ClassInfo ].appliedRef
316
+ // All members of class must be promotable.
317
+ val buffer = new mutable.ArrayBuffer [Effect ]
318
+ val excludedFlags = Flags .Deferred | Flags .Private | Flags .Protected
319
+
320
+ classRef.fields.foreach { denot =>
321
+ val f = denot.symbol
322
+ if ! f.isOneOf(excludedFlags) && f.hasSource then
323
+ buffer += Promote (FieldReturn (warm, f)(source))(source)
324
+ buffer += FieldAccess (warm, f)(source)
325
+ }
326
+
327
+ classRef.membersBasedOnFlags(Flags .Method , Flags .Deferred ).foreach { denot =>
328
+ val m = denot.symbol
329
+ if ! m.isConstructor && m.hasSource && ! theEnv.canIgnoreMethod(m) then
330
+ buffer += MethodCall (warm, m)(source)
331
+ buffer += Promote (MethodReturn (warm, m)(source))(source)
332
+ }
267
333
334
+ classRef.memberClasses.foreach { denot =>
335
+ val cls = denot.symbol.asClass
336
+ if cls.hasSource then
337
+ val potInner = Potentials .asSeenFrom(Warm (cls, ThisRef ()(source))(source), warm)
338
+ buffer += MethodCall (potInner, cls.primaryConstructor)(source)
339
+ buffer += Promote (potInner)(source)
268
340
}
269
341
342
+ for (eff <- buffer.toList) {
343
+ val errs = check(eff)
344
+ if ! errs.isEmpty then
345
+ return UnsafePromotion (warm, eff.source, state.path, errs.toList).toErrors
346
+ }
347
+ Errors .empty
270
348
271
349
private def checkPromote (eff : Promote )(using state : State ): Errors =
272
350
if (state.safePromoted.contains(eff.potential)) Errors .empty
273
351
else {
274
352
val pot = eff.potential
275
- val errs = pot match {
276
- case pot : ThisRef =>
277
- // If we have all fields initialized, then we can promote This to hot.
278
- val classRef = state.thisClass.info.asInstanceOf [ClassInfo ].appliedRef
279
- val allFieldsInited = classRef.fields.forall { denot =>
280
- val sym = denot.symbol
281
- sym.isOneOf(Flags .Lazy | Flags .Deferred ) || state.fieldsInited.contains(sym)
282
- }
283
- if (allFieldsInited)
284
- Errors .empty
285
- else
286
- PromoteThis (pot, eff.source, state.path).toErrors
287
-
288
- case _ : Cold =>
289
- PromoteCold (eff.source, state.path).toErrors
290
-
291
- case pot @ Warm (cls, outer) =>
292
- val errors = state.test { checkPromote(Promote (outer)(eff.source)) }
293
- if (errors.isEmpty) Errors .empty
294
- else PromoteWarm (pot, eff.source, state.path).toErrors
295
-
296
- case Fun (pots, effs) =>
297
- val errs1 = state.test {
298
- effs.toList.flatMap(check(_))
299
- }
300
- val errs2 = state.test {
301
- pots.toList.flatMap { pot =>
302
- checkPromote(Promote (pot)(eff.source))
353
+ val errs =
354
+ if canDirectlyPromote(pot) then
355
+ Errors .empty
356
+ else pot match {
357
+ case pot : ThisRef =>
358
+ PromoteThis (pot, eff.source, state.path).toErrors
359
+
360
+ case _ : Cold =>
361
+ PromoteCold (eff.source, state.path).toErrors
362
+
363
+ case pot @ Warm (cls, outer) =>
364
+ checkPromoteWarm(pot, eff)
365
+
366
+ case Fun (pots, effs) =>
367
+ val errs1 = state.test {
368
+ effs.toList.flatMap(check(_))
369
+ }
370
+ val errs2 = state.test {
371
+ pots.toList.flatMap { pot =>
372
+ checkPromote(Promote (pot)(eff.source))
373
+ }
303
374
}
304
- }
305
375
306
- if (errs1.nonEmpty || errs2.nonEmpty)
307
- UnsafePromotion (pot, eff.source, state.path, errs1 ++ errs2).toErrors
308
- else
309
- Errors .empty
376
+ if (errs1.nonEmpty || errs2.nonEmpty)
377
+ UnsafePromotion (pot, eff.source, state.path, errs1 ++ errs2).toErrors
378
+ else
379
+ Errors .empty
310
380
311
- case pot =>
312
- val Summary (pots, effs) = expand(pot)
313
- val effs2 = pots.map(Promote (_)(eff.source))
314
- (effs2 ++ effs).toList.flatMap(check(_))
315
- }
381
+ case pot =>
382
+ val Summary (pots, effs) = expand(pot)
383
+ val effs2 = pots.map(Promote (_)(eff.source))
384
+ (effs2 ++ effs).toList.flatMap(check(_))
385
+ }
316
386
// If we can safely promote, then we don't need to check again
317
387
if (errs.isEmpty)
318
388
state.safePromoted += pot
0 commit comments