Skip to content

Commit 1f72487

Browse files
committed
Revert heap changes if cache has changed
1 parent b6913e1 commit 1f72487

File tree

1 file changed

+38
-35
lines changed

1 file changed

+38
-35
lines changed

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

Lines changed: 38 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -65,44 +65,51 @@ object Semantic {
6565
sealed abstract class Ref extends Value {
6666
def klass: ClassSymbol
6767
def outer: Value
68-
def objekt: Objekt
68+
def objekt(using Heap): Objekt = heap(this)
69+
70+
def ensureObjectExists()(using Heap) =
71+
if heap.contains(this) then heap(this)
72+
else {
73+
val obj = Objekt(this.klass, fields = Map.empty, outers = Map(this.klass -> this.outer))
74+
heap.update(this, obj)
75+
obj
76+
}
77+
6978

7079
/** Update field value of the abstract object
7180
*
7281
* Invariant: fields are immutable and only set once
7382
*/
74-
def updateField(field: Symbol, value: Value)(using Context): Unit =
75-
objekt.updateField(field, value)
83+
def updateField(field: Symbol, value: Value)(using Heap, Context): Unit =
84+
val obj = objekt
85+
assert(!obj.hasField(field), field.show + " already init, new = " + value + ", old = " + obj.field(field))
86+
val obj2 = obj.copy(fields = obj.fields.updated(field, value))
87+
heap.update(this, obj2)
7688

7789
/** Update the immediate outer of the given `klass` of the abstract object
7890
*
7991
* Invariant: outers are immutable and only set once
8092
*/
81-
def updateOuter(klass: ClassSymbol, value: Value)(using Context): Unit =
82-
objekt.updateOuter(klass, value)
93+
def updateOuter(klass: ClassSymbol, value: Value)(using Heap, Context): Unit =
94+
val obj = objekt
95+
assert(!obj.hasOuter(klass), klass.show + " already init, new = " + value + ", old = " + obj.outer(klass))
96+
val obj2 = obj.copy(outers = obj.outers.updated(klass, value))
97+
heap.update(this, obj2)
8398
}
8499

85100
/** A reference to the object under initialization pointed by `this` */
86-
case class ThisRef(klass: ClassSymbol) extends Ref {
101+
case class ThisRef(klass: ClassSymbol)(using Heap) extends Ref {
87102
val outer = Hot
88-
/** Caches initialized fields */
89-
val objekt = Objekt(klass, fields = mutable.Map.empty, outers = mutable.Map(klass -> outer))
103+
104+
ensureObjectExists()
90105
}
91106

92107
/** An object with all fields initialized but reaches objects under initialization
93108
*
94109
* We need to restrict nesting levels of `outer` to finitize the domain.
95110
*/
96111
case class Warm(klass: ClassSymbol, outer: Value, ctor: Symbol, args: List[Value])(using Heap) extends Ref {
97-
val objekt = getCachedObject()
98-
99-
private def getCachedObject()(using Heap) =
100-
if heap.contains(this) then heap(this)
101-
else {
102-
val obj = Objekt(this.klass, fields = mutable.Map.empty, outers = mutable.Map(this.klass -> this.outer))
103-
heap.update(this, obj)
104-
obj
105-
}
112+
ensureObjectExists()
106113
}
107114

108115
/** A function value */
@@ -123,40 +130,33 @@ object Semantic {
123130
*
124131
* Note: Object is NOT a value.
125132
*/
126-
class Objekt(val klass: ClassSymbol, fields: mutable.Map[Symbol, Value], outers: mutable.Map[ClassSymbol, Value]) {
133+
case class Objekt(val klass: ClassSymbol, val fields: Map[Symbol, Value], val outers: Map[ClassSymbol, Value]) {
127134
def field(f: Symbol): Value = fields(f)
128135

129136
def outer(klass: ClassSymbol) = outers(klass)
130137

131138
def hasOuter(klass: ClassSymbol) = outers.contains(klass)
132139

133140
def hasField(f: Symbol) = fields.contains(f)
134-
135-
def updateField(field: Symbol, value: Value)(using Context): Unit =
136-
assert(!fields.contains(field), field.show + " already init, new = " + value + ", old = " + fields(field))
137-
fields(field) = value
138-
139-
def updateOuter(klass: ClassSymbol, value: Value)(using Context): Unit =
140-
assert(!outers.contains(klass), klass.show + " already init, new = " + value + ", old = " + outers(klass))
141-
outers(klass) = value
142141
}
143142

144143
/** Abstract heap stores abstract warm objects
145144
*
146145
* The heap serves as cache of summaries for warm objects and is shared for checking all classes.
147146
*/
148147
object Heap {
149-
opaque type Heap = mutable.Map[Warm, Objekt]
148+
class Heap(private var map: Map[Ref, Objekt]) {
149+
def contains(ref: Ref): Boolean = map.contains(ref)
150+
def apply(ref: Ref): Objekt = map(ref)
151+
def update(ref: Ref, obj: Objekt): Unit =
152+
map = map.updated(ref, obj)
153+
154+
def snapshot(): Heap = new Heap(map)
155+
def restore(h: Heap) = this.map = h.map
156+
}
150157

151158
/** Note: don't use `val` to avoid incorrect sharing */
152-
private[Semantic] def empty: Heap = mutable.Map.empty
153-
154-
extension (heap: Heap)
155-
def contains(ref: Warm): Boolean = heap.contains(ref)
156-
def apply(ref: Warm): Objekt = heap(ref)
157-
def update(ref: Warm, obj: Objekt): Unit =
158-
heap(ref) = obj
159-
end extension
159+
private[Semantic] def empty: Heap = new Heap(Map.empty)
160160
}
161161
type Heap = Heap.Heap
162162

@@ -784,12 +784,15 @@ object Semantic {
784784
val res = doTask(task)
785785
res.errors.foreach(_.issue)
786786

787+
val heapBefore = heap.snapshot()
788+
787789
if res.errors.nonEmpty then
788790
pendingTasks = rest
789791
checkedTasks = checkedTasks + task
790792
else
791793
// discard heap changes and copy cache.out to cache.in
792794
cache.update()
795+
heap.restore(heapBefore)
793796

794797
work()
795798
case _ =>

0 commit comments

Comments
 (0)