Skip to content

Commit 0d5600e

Browse files
committed
Refactor promotion
1 parent 8bb62a3 commit 0d5600e

File tree

1 file changed

+41
-50
lines changed

1 file changed

+41
-50
lines changed

compiler/src/dotty/tools/dotc/transform/init/Semantic.scala

Lines changed: 41 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -194,16 +194,22 @@ class Semantic {
194194
import Env._
195195

196196
object Promoted {
197+
class PromotionInfo {
198+
var isCurrentObjectPromoted: Boolean = false
199+
val values = mutable.Set.empty[Value]
200+
}
197201
/** Values that have been safely promoted */
198-
opaque type Promoted = mutable.Set[Value]
202+
opaque type Promoted = PromotionInfo
199203

200204
/** Note: don't use `val` to avoid incorrect sharing */
201-
def empty: Promoted = mutable.Set.empty
205+
def empty: Promoted = new PromotionInfo
202206

203207
extension (promoted: Promoted)
204-
def contains(value: Value): Boolean = promoted.contains(value)
205-
def add(value: Value): Unit = promoted += value
206-
def remove(value: Value): Unit = promoted -= value
208+
def isCurrentObjectPromoted: Boolean = promoted.isCurrentObjectPromoted
209+
def promoteCurrent(thisRef: ThisRef): Unit = promoted.isCurrentObjectPromoted = true
210+
def contains(value: Value): Boolean = promoted.values.contains(value)
211+
def add(value: Value): Unit = promoted.values += value
212+
def remove(value: Value): Unit = promoted.values -= value
207213
end extension
208214
}
209215
type Promoted = Promoted.Promoted
@@ -374,6 +380,9 @@ class Semantic {
374380
def call(meth: Symbol, args: List[Value], superType: Type, source: Tree, needResolve: Boolean = true): Contextual[Result] =
375381
def checkArgs = args.flatMap { arg => arg.promote("May only use initialized value as arguments", arg.source) }
376382

383+
// fast track if the current object is already initialized
384+
if promoted.isCurrentObjectPromoted then return Result(Hot, Nil)
385+
377386
value match {
378387
case Hot =>
379388
Result(Hot, checkArgs)
@@ -402,7 +411,7 @@ class Semantic {
402411
if target.isPrimaryConstructor then
403412
given Env = env2
404413
val tpl = cls.defTree.asInstanceOf[TypeDef].rhs.asInstanceOf[Template]
405-
val res = eval(tpl, addr, cls, cacheResult = true)
414+
val res = use(trace.add(cls.defTree)) { eval(tpl, addr, cls, cacheResult = true) }
406415
Result(addr, res.errors)
407416
else if target.isConstructor then
408417
given Env = env2
@@ -451,6 +460,7 @@ class Semantic {
451460
if errors.isEmpty then Hot
452461
else arg.widen
453462
}
463+
454464
if buffer.isEmpty then Result(Hot, Errors.empty)
455465
else
456466
val value = Warm(klass, Hot, ctor, args2)
@@ -496,64 +506,37 @@ class Semantic {
496506
end extension
497507

498508
// ----- Promotion ----------------------------------------------------
499-
500-
extension (value: Value)
501-
/** Can we promote the value by checking the extrinsic values?
502-
*
503-
* The extrinsic values are environment values, e.g. outers for `Warm`
504-
* and `thisV` captured in functions.
505-
*
506-
* This is a fast track for early promotion of values.
507-
*/
508-
def canPromoteExtrinsic: Contextual[Boolean] = log("canPromoteExtrinsic " + value + ", promoted = " + promoted, printer) {
509-
value match
510-
case Hot => true
511-
case Cold => false
512-
513-
case warm: Warm =>
514-
(warm.outer :: warm.args).forall(_.canPromoteExtrinsic) && {
515-
promoted.add(warm)
516-
true
517-
}
518-
519-
case thisRef: ThisRef =>
520-
promoted.contains(thisRef) || {
521-
val obj = heap(thisRef)
522-
// If we have all fields initialized, then we can promote This to hot.
523-
val allFieldsInitialized = thisRef.klass.appliedRef.fields.forall { denot =>
524-
val sym = denot.symbol
525-
sym.isOneOf(Flags.Lazy | Flags.Deferred) || obj.fields.contains(sym)
526-
}
527-
if allFieldsInitialized then promoted.add(thisRef)
528-
allFieldsInitialized
529-
}
530-
531-
case fun: Fun =>
532-
fun.thisV.canPromoteExtrinsic && {
533-
promoted.add(fun)
534-
true
509+
extension (thisRef: ThisRef)
510+
def tryPromoteCurrentObject: Contextual[Boolean] = log("tryPromoteCurrentObject ", printer) {
511+
promoted.isCurrentObjectPromoted || {
512+
val obj = heap(thisRef)
513+
// If we have all fields initialized, then we can promote This to hot.
514+
val allFieldsInitialized = thisRef.klass.appliedRef.fields.forall { denot =>
515+
val sym = denot.symbol
516+
sym.isOneOf(Flags.Lazy | Flags.Deferred) || obj.fields.contains(sym)
535517
}
536-
537-
case RefSet(refs) =>
538-
refs.forall(_.canPromoteExtrinsic)
539-
518+
if allFieldsInitialized then promoted.promoteCurrent(thisRef)
519+
allFieldsInitialized
520+
}
540521
}
541522

523+
extension (value: Value)
542524
/** Promotion of values to hot */
543525
def promote(msg: String, source: Tree): Contextual[List[Error]] = log("promoting " + value + ", promoted = " + promoted, printer) {
544-
value match
526+
if promoted.isCurrentObjectPromoted then Nil else
527+
528+
value.match
545529
case Hot => Nil
546530

547531
case Cold => PromoteError(msg, source, trace.toVector) :: Nil
548532

549533
case thisRef: ThisRef =>
550534
if promoted.contains(thisRef) then Nil
551-
else if thisRef.canPromoteExtrinsic then Nil
535+
else if thisRef.tryPromoteCurrentObject then Nil
552536
else PromoteError(msg, source, trace.toVector) :: Nil
553537

554538
case warm: Warm =>
555539
if promoted.contains(warm) then Nil
556-
else if warm.canPromoteExtrinsic then Nil
557540
else {
558541
promoted.add(warm)
559542
val errors = warm.tryPromote(msg, source)
@@ -902,7 +885,7 @@ class Semantic {
902885
/** Resolve C.this that appear in `klass` */
903886
def resolveThis(target: ClassSymbol, thisV: Value, klass: ClassSymbol, source: Tree): Contextual[Value] = log("resolving " + target.show + ", this = " + thisV.show + " in " + klass.show, printer, res => res.asInstanceOf[Value].show) {
904887
if target == klass then thisV
905-
else if target.is(Flags.Package) || target.isStaticOwner then Hot
888+
else if target.is(Flags.Package) then Hot
906889
else
907890
thisV match
908891
case Hot => Hot
@@ -1005,6 +988,7 @@ class Semantic {
1005988
if ctor.exists then superCall(tref, ctor, Nil, superParent)
1006989
}
1007990

991+
var fieldsChanged = true
1008992

1009993
// class body
1010994
tpl.body.foreach {
@@ -1013,10 +997,17 @@ class Semantic {
1013997
val res = eval(vdef.rhs, thisV, klass, cacheResult = true)
1014998
errorBuffer ++= res.errors
1015999
thisV.updateField(vdef.symbol, res.value)
1000+
fieldsChanged = true
10161001

10171002
case _: MemberDef =>
10181003

10191004
case tree =>
1005+
thisV match
1006+
case thisRef: ThisRef =>
1007+
if fieldsChanged then thisRef.tryPromoteCurrentObject
1008+
fieldsChanged = false
1009+
case _ =>
1010+
10201011
given Env = Env.empty
10211012
errorBuffer ++= eval(tree, thisV, klass).errors
10221013
}

0 commit comments

Comments
 (0)