Skip to content

SMT Z3 trace fix #6210

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jul 6, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion regression/cbmc/enum-trace1/test_json.desc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
CORE broken-smt-backend
CORE
main.c
--json-ui --function test --trace
activate-multi-line-match
Expand Down
2 changes: 1 addition & 1 deletion regression/cbmc/json-interface1/test.desc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
CORE broken-smt-backend
CORE
--json-interface
< test.json
activate-multi-line-match
Expand Down
2 changes: 1 addition & 1 deletion regression/cbmc/json1/test.desc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
CORE broken-smt-backend
CORE
main.c
--json-ui --stop-on-fail
activate-multi-line-match
Expand Down
5 changes: 5 additions & 0 deletions regression/cbmc/z3/trace-char.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
void main(int argc, char **argv)
{
char arr[] = {'0', '1', '2', '3', '4', '5', '6', '7'};
assert(arr[0] == '2');
}
13 changes: 13 additions & 0 deletions regression/cbmc/z3/trace-char.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
CORE smt-backend broken-cprover-smt-backend
trace-char.c
--trace --smt2 --z3
^EXIT=10$
^SIGNAL=0$
arr=\{ arr
arr=\{ '0', '1', '2', '3', '4', '5', '6', '7' \}
--
arr=(assignment removed)
error running SMT2 solver
--
Run cbmc with the --z3 and --trace options with arrays to confirm that
char arrays are displayed in traces.
5 changes: 5 additions & 0 deletions regression/cbmc/z3/trace.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
void main(int argc, char **argv)
{
int arr[] = {0, 1, 2, 3, 4, 5, 6, 17};
assert(arr[0] == 2);
}
13 changes: 13 additions & 0 deletions regression/cbmc/z3/trace.desc
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
CORE smt-backend broken-cprover-smt-backend
trace.c
--trace --smt2 --z3
^EXIT=10$
^SIGNAL=0$
arr=\{ arr
arr=\{ 0, 1, 2, 3, 4, 5, 6, 17 \}
--
arr=(assignment removed)
error running SMT2 solver
--
Run cbmc with the --z3 and --trace options with arrays to confirm that
int arrays are displayed in traces.
92 changes: 78 additions & 14 deletions src/solvers/smt2/smt2_conv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ exprt smt2_convt::get(const exprt &expr) const

if(it!=identifier_map.end())
return it->second.value;
return expr;
}
else if(expr.id()==ID_nondet_symbol)
{
Expand Down Expand Up @@ -318,6 +319,15 @@ exprt smt2_convt::get(const exprt &expr) const
}
else if(expr.is_constant())
return expr;
else if(const auto &array = expr_try_dynamic_cast<array_exprt>(expr))
{
exprt array_copy = *array;
for(auto &element : array_copy.operands())
{
element = get(element);
}
return array_copy;
}

return nil_exprt();
}
Expand Down Expand Up @@ -463,31 +473,85 @@ constant_exprt smt2_convt::parse_literal(
exprt smt2_convt::parse_array(
const irept &src,
const array_typet &type)
{
std::unordered_map<int64_t, exprt> operands_map;
walk_array_tree(&operands_map, src, type);
exprt::operandst operands;
// Try to find the default value, if there is none then set it
auto maybe_default_op = operands_map.find(-1);
exprt default_op;
if(maybe_default_op == operands_map.end())
default_op = nil_exprt();
else
default_op = maybe_default_op->second;
int64_t i = 0;
auto maybe_size = numeric_cast<std::int64_t>(type.size());
if(maybe_size.has_value())
{
while(i < maybe_size.value())
{
auto found_op = operands_map.find(i);
if(found_op != operands_map.end())
operands.emplace_back(found_op->second);
else
operands.emplace_back(default_op);
i++;
}
}
else
{
// Array size is unknown, keep adding with known indexes in order
// until we fail to find one.
auto found_op = operands_map.find(i);
while(found_op != operands_map.end())
{
operands.emplace_back(found_op->second);
i++;
found_op = operands_map.find(i);
}
operands.emplace_back(default_op);
}
return array_exprt(operands, type);
}

void smt2_convt::walk_array_tree(
std::unordered_map<int64_t, exprt> *operands_map,
const irept &src,
const array_typet &type)
{
if(src.get_sub().size()==4 && src.get_sub()[0].id()=="store")
{
// This is the SMT syntax being parsed here
// (store array index value)
if(src.get_sub().size()!=4)
return nil_exprt();

exprt array=parse_array(src.get_sub()[1], type);
exprt index=parse_rec(src.get_sub()[2], type.size().type());
exprt value=parse_rec(src.get_sub()[3], type.subtype());

return with_exprt(array, index, value);
// Recurse
walk_array_tree(operands_map, src.get_sub()[1], type);
const auto index_expr = parse_rec(src.get_sub()[2], type.size().type());
const constant_exprt index_constant = to_constant_expr(index_expr);
mp_integer tempint;
bool failure = to_integer(index_constant, tempint);
if(failure)
return;
long index = tempint.to_long();
exprt value = parse_rec(src.get_sub()[3], type.subtype());
operands_map->emplace(index, value);
}
else if(src.get_sub().size() == 3 && src.get_sub()[0].id() == "let")
{
// This is produced by Z3
// (let (....) (....))
walk_array_tree(
operands_map, src.get_sub()[1].get_sub()[0].get_sub()[1], type);
walk_array_tree(operands_map, src.get_sub()[2], type);
}
else if(src.get_sub().size()==2 &&
src.get_sub()[0].get_sub().size()==3 &&
src.get_sub()[0].get_sub()[0].id()=="as" &&
src.get_sub()[0].get_sub()[1].id()=="const")
{
// This is produced by Z3.
// ((as const (Array (_ BitVec 64) (_ BitVec 8))) #x00)))
exprt value=parse_rec(src.get_sub()[1], type.subtype());
return array_of_exprt(value, type);
// (as const type_info default_value)
exprt default_value = parse_rec(src.get_sub()[1], type.subtype());
operands_map->emplace(-1, default_value);
}
else
return nil_exprt();
}

exprt smt2_convt::parse_union(
Expand Down
15 changes: 15 additions & 0 deletions src/solvers/smt2/smt2_conv.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,23 @@ class smt2_convt : public stack_decision_proceduret
constant_exprt parse_literal(const irept &, const typet &type);
struct_exprt parse_struct(const irept &s, const struct_typet &type);
exprt parse_union(const irept &s, const union_typet &type);
/// This function is for parsing array output from SMT solvers
/// when "(get-value |???|)" returns an array object.
/// \param s: is the irept parsed from the SMT output
/// \param type: is the expected type
/// \returns an exprt that represents the array
exprt parse_array(const irept &s, const array_typet &type);
exprt parse_rec(const irept &s, const typet &type);
/// This function walks the SMT output and populates a
/// map with index/value pairs for the array
/// \param operands_map: is a map of the operands to the array
/// being constructed indexed by their index.
/// \param src: is the irept source for the SMT output
/// \param type: is the type of the array
void walk_array_tree(
std::unordered_map<int64_t, exprt> *operands_map,
const irept &src,
const array_typet &type);

// we use this to build a bit-vector encoding of the FPA theory
void convert_floatbv(const exprt &expr);
Expand Down