Skip to content

Commit ef15614

Browse files
committed
Fixes array value in traces for Z3 SMT
This is a fix for how we parse Z3 array output to reconstruct values in traces. This is specific bug fix for Z3 array output.
1 parent 2f9da2b commit ef15614

File tree

2 files changed

+84
-14
lines changed

2 files changed

+84
-14
lines changed

src/solvers/smt2/smt2_conv.cpp

Lines changed: 69 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ exprt smt2_convt::get(const exprt &expr) const
282282

283283
if(it!=identifier_map.end())
284284
return it->second.value;
285+
return expr;
285286
}
286287
else if(expr.id()==ID_nondet_symbol)
287288
{
@@ -472,31 +473,85 @@ constant_exprt smt2_convt::parse_literal(
472473
exprt smt2_convt::parse_array(
473474
const irept &src,
474475
const array_typet &type)
476+
{
477+
std::unordered_map<int64_t, exprt> operands_map;
478+
walk_array_tree(&operands_map, src, type);
479+
exprt::operandst operands;
480+
// Try to find the default value, if there is none then set it
481+
auto maybe_default_op = operands_map.find(-1);
482+
exprt default_op;
483+
if(maybe_default_op == operands_map.end())
484+
default_op = nil_exprt();
485+
else
486+
default_op = maybe_default_op->second;
487+
int64_t i = 0;
488+
auto maybe_size = numeric_cast<std::int64_t>(type.size());
489+
if(maybe_size.has_value())
490+
{
491+
while(i < maybe_size.value())
492+
{
493+
auto found_op = operands_map.find(i);
494+
if(found_op != operands_map.end())
495+
operands.emplace_back(found_op->second);
496+
else
497+
operands.emplace_back(default_op);
498+
i++;
499+
}
500+
}
501+
else
502+
{
503+
// Array size is unknown, keep adding with known indexes in order
504+
// until we fail to find one.
505+
auto found_op = operands_map.find(i);
506+
while(found_op != operands_map.end())
507+
{
508+
operands.emplace_back(found_op->second);
509+
i++;
510+
found_op = operands_map.find(i);
511+
}
512+
operands.emplace_back(default_op);
513+
}
514+
return array_exprt(operands, type);
515+
}
516+
517+
void smt2_convt::walk_array_tree(
518+
std::unordered_map<int64_t, exprt> *operands_map,
519+
const irept &src,
520+
const array_typet &type)
475521
{
476522
if(src.get_sub().size()==4 && src.get_sub()[0].id()=="store")
477523
{
524+
// This is the SMT syntax being parsed here
478525
// (store array index value)
479-
if(src.get_sub().size()!=4)
480-
return nil_exprt();
481-
482-
exprt array=parse_array(src.get_sub()[1], type);
483-
exprt index=parse_rec(src.get_sub()[2], type.size().type());
484-
exprt value=parse_rec(src.get_sub()[3], type.subtype());
485-
486-
return with_exprt(array, index, value);
526+
// Recurse
527+
walk_array_tree(operands_map, src.get_sub()[1], type);
528+
const auto index_expr = parse_rec(src.get_sub()[2], type.size().type());
529+
const constant_exprt index_constant = to_constant_expr(index_expr);
530+
mp_integer tempint;
531+
bool failure = to_integer(index_constant, tempint);
532+
if(failure)
533+
return;
534+
long index = tempint.to_long();
535+
exprt value = parse_rec(src.get_sub()[3], type.subtype());
536+
operands_map->emplace(index, value);
537+
}
538+
else if(src.get_sub().size() == 3 && src.get_sub()[0].id() == "let")
539+
{
540+
// This is produced by Z3
541+
// (let (....) (....))
542+
walk_array_tree(
543+
operands_map, src.get_sub()[1].get_sub()[0].get_sub()[1], type);
544+
walk_array_tree(operands_map, src.get_sub()[2], type);
487545
}
488546
else if(src.get_sub().size()==2 &&
489547
src.get_sub()[0].get_sub().size()==3 &&
490548
src.get_sub()[0].get_sub()[0].id()=="as" &&
491549
src.get_sub()[0].get_sub()[1].id()=="const")
492550
{
493-
// This is produced by Z3.
494-
// ((as const (Array (_ BitVec 64) (_ BitVec 8))) #x00)))
495-
exprt value=parse_rec(src.get_sub()[1], type.subtype());
496-
return array_of_exprt(value, type);
551+
// (as const type_info default_value)
552+
exprt default_value = parse_rec(src.get_sub()[1], type.subtype());
553+
operands_map->emplace(-1, default_value);
497554
}
498-
else
499-
return nil_exprt();
500555
}
501556

502557
exprt smt2_convt::parse_union(

src/solvers/smt2/smt2_conv.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,23 @@ class smt2_convt : public stack_decision_proceduret
159159
constant_exprt parse_literal(const irept &, const typet &type);
160160
struct_exprt parse_struct(const irept &s, const struct_typet &type);
161161
exprt parse_union(const irept &s, const union_typet &type);
162+
/// This function is for parsing array output from SMT solvers
163+
/// when "(get-value |???|)" returns an array object.
164+
/// \param s: is the irept parsed from the SMT output
165+
/// \param type: is the expected type
166+
/// \returns an exprt that represents the array
162167
exprt parse_array(const irept &s, const array_typet &type);
163168
exprt parse_rec(const irept &s, const typet &type);
169+
/// This function walks the SMT output and populates a
170+
/// map with index/value pairs for the array
171+
/// \param operands_map: is a map of the operands to the array
172+
/// being constructed indexed by their index.
173+
/// \param src: is the irept source for the SMT output
174+
/// \param type: is the type of the array
175+
void walk_array_tree(
176+
std::unordered_map<int64_t, exprt> *operands_map,
177+
const irept &src,
178+
const array_typet &type);
164179

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

0 commit comments

Comments
 (0)