From 63bb7f578d2dfcb3a738654709c2b8e5bcaedb15 Mon Sep 17 00:00:00 2001 From: Fedor Isakov Date: Thu, 3 Mar 2016 16:42:09 +0100 Subject: [PATCH] Ensure the null value is never exposed when merging variable (ground-free) --- .../mps/logic/reactor/core/Logical.kt | 18 +++++++---- reactor/Test/test/TestProgram.kt | 32 ++++++++++++++++++- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Logical.kt b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Logical.kt index 3ad85443..b625a2b1 100644 --- a/reactor/Core/src/jetbrains/mps/logic/reactor/core/Logical.kt +++ b/reactor/Core/src/jetbrains/mps/logic/reactor/core/Logical.kt @@ -104,27 +104,31 @@ class MemLogical : SolverLogical { thisRepr.incRank(); } - otherRepr.setParent(thisRepr) - thisRepr.mergeParentObservers(otherRepr) - val thisVal = thisRepr.value(); val otherVal = otherRepr.value(); + // first copy the value if (thisVal == null && otherVal != null) { // var ground thisRepr.setValue(otherVal); + // TODO: clear the value in the "other" logical after union } else if (thisVal != null && otherVal == null) { // ground var - // otherRepr.setValue(thisRepr.value()); // TODO: no need to copy the value - otherRepr.notifyValueUpdated(); + otherRepr.setValue(thisVal); + } - } else if (thisVal == null && otherVal == null) { + // then set parent + otherRepr.setParent(thisRepr) + thisRepr.mergeParentObservers(otherRepr) + + // last, reconcile the values/merge observers + if (thisVal == null && otherVal == null) { // var var thisRepr.mergeValueObservers(otherRepr); - } else { + } else if (thisVal != null && otherVal != null) { // ground ground reconciler.reconcile(thisVal, otherVal); } diff --git a/reactor/Test/test/TestProgram.kt b/reactor/Test/test/TestProgram.kt index 4b96f107..aba79437 100644 --- a/reactor/Test/test/TestProgram.kt +++ b/reactor/Test/test/TestProgram.kt @@ -166,7 +166,8 @@ class TestProgram { ).session("reactivateOnUnion").run { assertEquals(setOf( ConstraintSymbol("foo", 2), ConstraintSymbol("capture", 1), - ConstraintSymbol("replaced", 0)), constraintSymbols()) + ConstraintSymbol("replaced", 0)), + constraintSymbols()) assertEquals(1, constraintOccurrences(ConstraintSymbol("foo", 2)).count()) // FIXME: this count should be 2 instead as per the "activation history" feature assertEquals(3, constraintOccurrences(ConstraintSymbol("capture", 1)).count()) @@ -174,6 +175,35 @@ class TestProgram { } } + @Test + fun reactivateOnUnionKeepValue() { + val (X,Y,Z) = metaLogical("X", "Y", "Z") + + program( + rule("main", + headReplaced( constraint("main") ), body( statement({ x, y -> x eq y }, X, Y), // rank(X) = 1 + statement({ z -> z.set(42) }, Z), + constraint("foo", Z), + statement({ x, z -> x eq z }, X, Z) ) + ), + rule("capture_foo_free", + headKept( constraint("foo", X) ), guard( expression({x -> x.getNullable() == null }, X) ), + body( constraint("free") ) + ), + rule("capture_foo_assigned", + headKept( constraint("foo", X) ), guard( expression({x -> x.getNullable() != null }, X) ), + body( constraint("assigned") ) + ) + ).session("reactivateOnUnionKeepValue").run { + assertEquals(setOf( ConstraintSymbol("foo", 1), + ConstraintSymbol("assigned", 0)), + constraintSymbols()) + assertEquals(1, constraintOccurrences(ConstraintSymbol("foo", 1)).count()) + // FIXME: this count should be 1 instead as per the "activation history" feature (??? not sure) + assertEquals(2, constraintOccurrences(ConstraintSymbol("assigned", 0)).count()) + } + } + @Test fun gcd() { val (M, N, TMP) = metaLogical("M", "N", "TMP")