@@ -465,14 +465,29 @@ bool StructuralDualWalk::processPair(VM vm, RichNode left, RichNode right) {
465
465
}
466
466
467
467
void StructuralDualWalk::rebind (VM vm, RichNode left, RichNode right) {
468
- // XXX: The test is to work around `a.reinit(vm, b)` where `b` is sometimes
469
- // modified. It is better to reinit Unstable nodes than Stable ones anyway.
470
- // See #312 for details.
468
+ // When given a Stable and an Unstable node, reinit will always alias the
469
+ // unstable node to the stable one, even if it is called on the stable one.
470
+ // In particular, it means that it can modify its argument, and not this.
471
+ // In that case, we need to backup the variable that will be modified.
472
+ // See #312 and #315 for details.
473
+ //
474
+ // In particular, StableNode.reinit(vm, UnstableNode) must never be called as
475
+ // it updates both `left` and `right` and breaks our backup system.
476
+ // To avoid that, we always call reinit on the less stable node we have.
471
477
if (right.isStable ()) {
478
+ // `right` is stable, backup `left` because it will be aliased.
472
479
rebindTrail.push_back (vm, left.makeBackup ());
480
+ // If both are stable, `left` must be modified (because that's the one we
481
+ // have a backup for). That is why we call `left`'s reinit.
473
482
left.reinit (vm, right);
474
483
} else {
484
+ // `right` is unstable, backup it as it will be aliased.
475
485
rebindTrail.push_back (vm, right.makeBackup ());
486
+ // If both are unstable, they are both aliased to a stable node
487
+ // initialised with reinit's second arg (`left` in this case).
488
+ // By calling `right`'s reinit, we ensure that `left` is still valid
489
+ // after a rollback. This leaves around an unnecessary indirection from
490
+ // `left` to the new stable node, but it is the price to pay for eagerness.
476
491
right.reinit (vm, left);
477
492
}
478
493
}
0 commit comments