Skip to content

Commit 3c26e4c

Browse files
#5 Use better test for null
Update changelog to make it clear that record_access needs to be guarded.
1 parent 6158db1 commit 3c26e4c

4 files changed

Lines changed: 89 additions & 9 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,12 @@
2323

2424
* A new expression `Record_Null_Check` can be used to check if a
2525
given term is equal to the null constructor. This can only be used
26-
on recursive records.
26+
on recursive records. Note that if you have a recursive record,
27+
then this is a pre-condition that you need to check on any access;
28+
otherwise you might get unexpected results (it is perfectly
29+
possible to access a record null constructor, you just get an
30+
uninterpreted function result). This checking has to happen inside
31+
the program driving PyVCG, it cannot happen inside PyVCG.
2732

2833
* Move to CVC 1.3.1.
2934

pyvcg/driver/cvc5_api.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -506,10 +506,9 @@ def visit_record_null_check(self, node, tr_record):
506506
s_dt = s_record_sort.getDatatype()
507507
s_cons = s_dt.getConstructor(node.record.sort.name + "__null")
508508
return self.solver.mkTerm(
509-
cvc5.Kind.EQUAL,
510-
tr_record,
511-
self.solver.mkTerm(cvc5.Kind.APPLY_CONSTRUCTOR,
512-
s_cons.getTerm()))
509+
cvc5.Kind.APPLY_TESTER,
510+
s_cons.getTesterTerm(),
511+
tr_record)
513512

514513
def visit_function_application(self, node, tr_function, tr_args):
515514
assert isinstance(node, smt.Function_Application)

pyvcg/driver/file_smtlib.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -332,9 +332,9 @@ def visit_record_access(self, node, tr_record):
332332

333333
def visit_record_null_check(self, node, tr_record):
334334
assert isinstance(node, smt.Record_Null_Check)
335-
return "(= %s %s)" % (
336-
tr_record,
337-
self.escape_name(node.record.sort.name + "__null"))
335+
return "((_ is %s) %s)" % (
336+
self.escape_name(node.record.sort.name + "__null"),
337+
tr_record)
338338

339339
def visit_function_application(self, node, tr_function, tr_args):
340340
assert isinstance(node, smt.Function_Application)

tests/testSmt.py

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1030,14 +1030,90 @@ def test_Recursive_Record(self):
10301030
(declare-const a List)
10311031
(declare-const b List)
10321032
(assert (= (value (next a)) 42))
1033-
(assert (not (= b List__null)))
1033+
(assert (not ((_ is List__null) b)))
10341034
(check-sat)
10351035
(get-value (a))
10361036
(get-value (b))
10371037
(exit)
10381038
"""
10391039
)
10401040

1041+
def test_Recursive_Record_Loop(self):
1042+
s_sort = smt.Record("List")
1043+
s_sort.add_component("value", smt.BUILTIN_INTEGER)
1044+
s_sort.add_component("next", s_sort)
1045+
self.script.add_statement(smt.Record_Declaration(s_sort))
1046+
1047+
sym_a = smt.Constant(s_sort, "a")
1048+
self.script.add_statement(
1049+
smt.Constant_Declaration(sym_a,
1050+
relevant=True))
1051+
sym_b = smt.Constant(s_sort, "b")
1052+
self.script.add_statement(
1053+
smt.Constant_Declaration(sym_b,
1054+
relevant=True))
1055+
sym_c = smt.Constant(s_sort, "c")
1056+
self.script.add_statement(
1057+
smt.Constant_Declaration(sym_c,
1058+
relevant=True))
1059+
1060+
self.script.add_statement(
1061+
smt.Assertion(
1062+
smt.Boolean_Negation(
1063+
smt.Record_Null_Check(sym_a))))
1064+
self.script.add_statement(
1065+
smt.Assertion(
1066+
smt.Boolean_Negation(
1067+
smt.Record_Null_Check(sym_b))))
1068+
self.script.add_statement(
1069+
smt.Assertion(
1070+
smt.Boolean_Negation(
1071+
smt.Record_Null_Check(sym_c))))
1072+
1073+
self.script.add_statement(
1074+
smt.Assertion(
1075+
smt.Comparison("=",
1076+
smt.Record_Access(sym_a, "next"),
1077+
sym_b)))
1078+
self.script.add_statement(
1079+
smt.Assertion(
1080+
smt.Comparison("=",
1081+
smt.Record_Access(sym_b, "next"),
1082+
sym_c)))
1083+
self.script.add_statement(
1084+
smt.Assertion(
1085+
smt.Comparison("=",
1086+
smt.Record_Access(sym_c, "next"),
1087+
sym_a)))
1088+
1089+
self.assertResult(
1090+
"unsat",
1091+
"""
1092+
(set-logic QF_DTLIA)
1093+
(set-option :produce-models true)
1094+
1095+
(declare-datatype List (
1096+
(List__null)
1097+
(List__cons
1098+
(value Int)
1099+
(next List))))
1100+
(declare-const a List)
1101+
(declare-const b List)
1102+
(declare-const c List)
1103+
(assert (not ((_ is List__null) a)))
1104+
(assert (not ((_ is List__null) b)))
1105+
(assert (not ((_ is List__null) c)))
1106+
(assert (= (next a) b))
1107+
(assert (= (next b) c))
1108+
(assert (= (next c) a))
1109+
(check-sat)
1110+
(get-value (a))
1111+
(get-value (b))
1112+
(get-value (c))
1113+
(exit)
1114+
"""
1115+
)
1116+
10411117
def test_Void_Records(self):
10421118
s_sort = smt.Record("Foo")
10431119
self.script.add_statement(smt.Record_Declaration(s_sort))

0 commit comments

Comments
 (0)