Skip to content

Commit a5d2281

Browse files
committed
Try fix assertion errors
1 parent faccf75 commit a5d2281

File tree

1 file changed

+41
-10
lines changed

1 file changed

+41
-10
lines changed

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

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,10 @@ object Semantic:
242242
/** The cache for expression values from last iteration */
243243
private var last: ExprValueCache = Map.empty
244244

245-
/** The updated cache for expression values based on the cache values from the last iteration */
245+
/** The updated cache for expression values based on the cache values from the last iteration
246+
*
247+
* Both `last` and `current` are required to make sure an expression is evaluated once in each iteration.
248+
*/
246249
private var current: ExprValueCache = Map.empty
247250

248251
/** Global cached values for expressions
@@ -294,10 +297,36 @@ object Semantic:
294297
if current.contains(value, expr) then current.get(value, expr)
295298
else stable.get(value, expr)
296299

300+
/** Conditionally perform an operation
301+
*
302+
* If the operation returns true, the changes are commited. Otherwise, the changes are reverted.
303+
*/
304+
def conditionally[T](fn: => (Boolean, T)): T =
305+
val last2 = this.last
306+
val current2 = this.current
307+
val stable2 = this.stable
308+
val heap2 = this.heap
309+
val heapStable2 = this.heapStable
310+
val changed2 = this.changed
311+
val (commit, value) = fn
312+
313+
if commit then
314+
this.last = last2
315+
this.current = current2
316+
this.stable = stable2
317+
this.heap = heap2
318+
this.heapStable = heapStable2
319+
this.changed = changed2
320+
321+
value
322+
297323
/** Copy the value of `(value, expr)` from the last cache to the current cache
298-
* (assuming it's `Hot` if it doesn't exist in the cache).
299324
*
300-
* Then, runs `fun` and update the caches if the values change.
325+
* It assumes the value is `Hot` if it doesn't exist in the last cache.
326+
*
327+
* It update the current caches if the values change.
328+
*
329+
* The two caches are required because we want to make sure in a new iteration, an expression is evaluated once.
301330
*/
302331
def assume(value: Value, expr: Tree, cacheResult: Boolean)(fun: => Value): Contextual[Value] =
303332
val assumeValue: Value =
@@ -312,7 +341,6 @@ object Semantic:
312341
val actual = fun
313342
if actual != assumeValue then
314343
this.changed = true
315-
this.last = this.last.updatedNested(value, expr, actual)
316344
this.current = this.current.updatedNested(value, expr, actual)
317345
else
318346
// It's tempting to cache the value in stable, but it's unsound.
@@ -339,13 +367,13 @@ object Semantic:
339367
*
340368
* 1. Reset changed flag.
341369
*
342-
* 2. Reset current cache (last cache already synced in `assume`).
343-
*
344-
* 3. Revert heap if instable.
370+
* 2. Use current cache as last cache and set current cache to be empty.
345371
*
372+
* 3. Revert heap to stable.
346373
*/
347374
def prepareForNextIteration()(using Context) =
348375
this.changed = false
376+
this.last = this.current
349377
this.current = Map.empty
350378
this.heap = this.heapStable
351379

@@ -542,12 +570,15 @@ object Semantic:
542570
// We may reset the outers or params of a populated warm object.
543571
// This is the case if we need access the field of a warm object, which
544572
// requires population of parameters and outers; and later create an
545-
// instance of the exact warm object, which requires initialization check.
573+
// instance of the exact warm object, whose initialization will reset
574+
// the outer and constructor parameters.
546575
//
547576
// See tests/init/neg/unsound1.scala
548-
assert(!obj.hasField(field) || field.is(Flags.ParamAccessor) && obj.field(field) == value, field.show + " already init, new = " + value + ", old = " + obj.field(field) + ", ref = " + ref)
577+
val changed = !obj.hasField(field) || obj.field(field) != value
578+
def isParamUpdate = field.isOneOf(Flags.ParamAccessor | Flags.Param) && obj.field(field) == value
579+
assert(!obj.hasField(field) || isParamUpdate, field.show + " already init, new = " + value + ", old = " + obj.field(field) + ", ref = " + ref)
549580
val obj2 = obj.copy(fields = obj.fields.updated(field, value))
550-
cache.updateObject(ref, obj2)
581+
if changed then cache.updateObject(ref, obj2)
551582
}
552583

553584
/** Update the immediate outer of the given `klass` of the abstract object

0 commit comments

Comments
 (0)