@@ -242,7 +242,10 @@ object Semantic:
242
242
/** The cache for expression values from last iteration */
243
243
private var last : ExprValueCache = Map .empty
244
244
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
+ */
246
249
private var current : ExprValueCache = Map .empty
247
250
248
251
/** Global cached values for expressions
@@ -294,10 +297,36 @@ object Semantic:
294
297
if current.contains(value, expr) then current.get(value, expr)
295
298
else stable.get(value, expr)
296
299
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
+
297
323
/** 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).
299
324
*
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.
301
330
*/
302
331
def assume (value : Value , expr : Tree , cacheResult : Boolean )(fun : => Value ): Contextual [Value ] =
303
332
val assumeValue : Value =
@@ -312,7 +341,6 @@ object Semantic:
312
341
val actual = fun
313
342
if actual != assumeValue then
314
343
this .changed = true
315
- this .last = this .last.updatedNested(value, expr, actual)
316
344
this .current = this .current.updatedNested(value, expr, actual)
317
345
else
318
346
// It's tempting to cache the value in stable, but it's unsound.
@@ -339,13 +367,13 @@ object Semantic:
339
367
*
340
368
* 1. Reset changed flag.
341
369
*
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.
345
371
*
372
+ * 3. Revert heap to stable.
346
373
*/
347
374
def prepareForNextIteration ()(using Context ) =
348
375
this .changed = false
376
+ this .last = this .current
349
377
this .current = Map .empty
350
378
this .heap = this .heapStable
351
379
@@ -542,12 +570,15 @@ object Semantic:
542
570
// We may reset the outers or params of a populated warm object.
543
571
// This is the case if we need access the field of a warm object, which
544
572
// 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.
546
575
//
547
576
// 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)
549
580
val obj2 = obj.copy(fields = obj.fields.updated(field, value))
550
- cache.updateObject(ref, obj2)
581
+ if changed then cache.updateObject(ref, obj2)
551
582
}
552
583
553
584
/** Update the immediate outer of the given `klass` of the abstract object
0 commit comments