Skip to content

Commit 6b53fd5

Browse files
authored
Merge pull request #315 from layus/fix-unify-rollback
Fix error message on failed unification
2 parents 61450f5 + b593578 commit 6b53fd5

File tree

4 files changed

+58
-2
lines changed

4 files changed

+58
-2
lines changed

platform-test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ set(BASE_FUNCTORS
1313
#"finalize.oz" "gc.oz"
1414
"state.oz" "thread.oz"
1515
#"vm.oz" #FIXME vm tests are buggy, see #313.
16+
"unify.oz"
1617
"reflection.oz" "serializer.oz"
1718
)
1819
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/base")

platform-test/base/unify.oz

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
functor
2+
import
3+
VM
4+
export
5+
Return
6+
define
7+
Return = unify([vmlist(proc {$}
8+
try
9+
{VM.list} = unit
10+
fail
11+
catch failure(debug:d(info:[eq(unit _)] ...) ...) then
12+
skip
13+
end
14+
end
15+
keys:[unify])
16+
order(proc {$}
17+
fun {Const X} [1 2 X] end
18+
in
19+
try
20+
{Const 1} = {Const 2}
21+
fail
22+
catch failure(debug:d(info:[eq(1 2)] ...) ...) then
23+
skip
24+
end
25+
end
26+
keys:[unify])
27+
])
28+
end

vm/vm/main/store.hh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ void UnstableNode::copy(VM vm, StableNode& from) {
9797
make<Reference>(vm, &from);
9898
}
9999

100+
// WARNING This code is highly sensitive for StructuralDualWalk::rebind in unify.cc
100101
void UnstableNode::copy(VM vm, UnstableNode& from) {
101102
if (from.isCopyable()) {
102103
set(from);
@@ -207,6 +208,7 @@ void RichNode::ensureStable(VM vm) {
207208
}
208209
}
209210

211+
// WARNING This code is highly sensitive for StructuralDualWalk::rebind in unify.cc
210212
void RichNode::reinit(VM vm, StableNode& from) {
211213
if (node() == &from) {
212214
// do nothing
@@ -217,6 +219,7 @@ void RichNode::reinit(VM vm, StableNode& from) {
217219
}
218220
}
219221

222+
// WARNING This code is highly sensitive for StructuralDualWalk::rebind in unify.cc
220223
void RichNode::reinit(VM vm, UnstableNode& from) {
221224
if (node() == &from) {
222225
// do nothing
@@ -231,6 +234,7 @@ void RichNode::reinit(VM vm, UnstableNode&& from) {
231234
node()->set(from);
232235
}
233236

237+
// WARNING This code is highly sensitive for StructuralDualWalk::rebind in unify.cc
234238
void RichNode::reinit(VM vm, RichNode from) {
235239
if (from.isStable())
236240
reinit(vm, from.asStable());

vm/vm/main/unify.cc

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -465,8 +465,31 @@ bool StructuralDualWalk::processPair(VM vm, RichNode left, RichNode right) {
465465
}
466466

467467
void StructuralDualWalk::rebind(VM vm, RichNode left, RichNode right) {
468-
rebindTrail.push_back(vm, left.makeBackup());
469-
left.reinit(vm, right);
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.
477+
if (right.isStable()) {
478+
// `right` is stable, backup `left` because it will be aliased.
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.
482+
left.reinit(vm, right);
483+
} else {
484+
// `right` is unstable, backup it as it will be aliased.
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.
491+
right.reinit(vm, left);
492+
}
470493
}
471494

472495
void StructuralDualWalk::cleanupOnFailure(VM vm) {

0 commit comments

Comments
 (0)