@@ -264,55 +264,125 @@ object Checking {
264264 val Summary (pots, effs) = expand(pot)
265265 val effs2 = pots.map(FieldAccess (_, field)(eff.source))
266266 (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+ }
267333
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)
268340 }
269341
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
270348
271349 private def checkPromote (eff : Promote )(using state : State ): Errors =
272350 if (state.safePromoted.contains(eff.potential)) Errors .empty
273351 else {
274352 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+ }
303374 }
304- }
305375
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
310380
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+ }
316386 // If we can safely promote, then we don't need to check again
317387 if (errs.isEmpty)
318388 state.safePromoted += pot
0 commit comments