diff --git a/regression/cbmc/enum-trace1/test_json.desc b/regression/cbmc/enum-trace1/test_json.desc index 255c95b8c1c..6619fc73eea 100644 --- a/regression/cbmc/enum-trace1/test_json.desc +++ b/regression/cbmc/enum-trace1/test_json.desc @@ -1,4 +1,4 @@ -CORE broken-smt-backend +CORE main.c --json-ui --function test --trace activate-multi-line-match diff --git a/regression/cbmc/json-interface1/test.desc b/regression/cbmc/json-interface1/test.desc index 5ea8fe3d74c..a15a7b20943 100644 --- a/regression/cbmc/json-interface1/test.desc +++ b/regression/cbmc/json-interface1/test.desc @@ -1,4 +1,4 @@ -CORE broken-smt-backend +CORE --json-interface < test.json activate-multi-line-match diff --git a/regression/cbmc/json1/test.desc b/regression/cbmc/json1/test.desc index bf66d83d928..b9eb6a30118 100644 --- a/regression/cbmc/json1/test.desc +++ b/regression/cbmc/json1/test.desc @@ -1,4 +1,4 @@ -CORE broken-smt-backend +CORE main.c --json-ui --stop-on-fail activate-multi-line-match diff --git a/regression/cbmc/z3/trace-char.c b/regression/cbmc/z3/trace-char.c new file mode 100644 index 00000000000..18308105a75 --- /dev/null +++ b/regression/cbmc/z3/trace-char.c @@ -0,0 +1,5 @@ +void main(int argc, char **argv) +{ + char arr[] = {'0', '1', '2', '3', '4', '5', '6', '7'}; + assert(arr[0] == '2'); +} diff --git a/regression/cbmc/z3/trace-char.desc b/regression/cbmc/z3/trace-char.desc new file mode 100644 index 00000000000..ee19e04f73b --- /dev/null +++ b/regression/cbmc/z3/trace-char.desc @@ -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. diff --git a/regression/cbmc/z3/trace.c b/regression/cbmc/z3/trace.c new file mode 100644 index 00000000000..7e842774d2c --- /dev/null +++ b/regression/cbmc/z3/trace.c @@ -0,0 +1,5 @@ +void main(int argc, char **argv) +{ + int arr[] = {0, 1, 2, 3, 4, 5, 6, 17}; + assert(arr[0] == 2); +} diff --git a/regression/cbmc/z3/trace.desc b/regression/cbmc/z3/trace.desc new file mode 100644 index 00000000000..3900217e56b --- /dev/null +++ b/regression/cbmc/z3/trace.desc @@ -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. diff --git a/src/solvers/smt2/smt2_conv.cpp b/src/solvers/smt2/smt2_conv.cpp index 289beb85509..da5e1aa3a41 100644 --- a/src/solvers/smt2/smt2_conv.cpp +++ b/src/solvers/smt2/smt2_conv.cpp @@ -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) { @@ -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(expr)) + { + exprt array_copy = *array; + for(auto &element : array_copy.operands()) + { + element = get(element); + } + return array_copy; + } return nil_exprt(); } @@ -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 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(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 *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( diff --git a/src/solvers/smt2/smt2_conv.h b/src/solvers/smt2/smt2_conv.h index e1a65ff2fb5..0f5cb30c768 100644 --- a/src/solvers/smt2/smt2_conv.h +++ b/src/solvers/smt2/smt2_conv.h @@ -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 *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);