Skip to content

Commit 117fd9f

Browse files
Merge pull request #7078 from thomasspriggs/tas/smt_array_trace_printing_extended
Add array trace printing to incremental SMT2 decision procedure
2 parents 312c433 + 0f7b21c commit 117fd9f

16 files changed

+503
-199
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
int main()
2+
{
3+
int example_array[1025];
4+
unsigned int index;
5+
__CPROVER_assume(index < 1025);
6+
__CPROVER_assert(example_array[index] != 42, "Array condition");
7+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
CORE
2+
array_read.c
3+
--trace
4+
Passing problem to incremental SMT2 solving
5+
\[main\.assertion\.1\] line \d+ Array condition: FAILURE
6+
^Trace for main\.assertion\.1
7+
example_array=\{ (\d+, )*42
8+
^EXIT=10$
9+
^SIGNAL=0$
10+
--
11+
--
12+
Test of reading a value at a non-deterministic index of an array.
13+
Similar to the test in ../arrays/array_read.c, but we want to assert
14+
the value of the array that comes back in the trace to make sure we're
15+
observing the correct values.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
int main()
2+
{
3+
int example_array[1025];
4+
unsigned int index;
5+
__CPROVER_assume(index < 1025);
6+
example_array[index] = 42;
7+
__CPROVER_assert(example_array[index] == 42, "Array condition");
8+
__CPROVER_assert(example_array[index] != 42, "Array condition");
9+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
array_write.c
3+
--trace
4+
Passing problem to incremental SMT2 solving
5+
^Trace for main\.assertion\.2
6+
example_array\[\d{1,4}ll?\]=42
7+
^EXIT=10$
8+
^SIGNAL=0$
9+
--
10+
--
11+
Test of writing a value at a non-deterministic index of an array, then asserting
12+
the value we expect.
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
// Author: Diffblue Ltd.
2+
3+
#ifndef CPROVER_SOLVERS_SMT2_INCREMENTAL_RESPONSE_OR_ERROR_H
4+
#define CPROVER_SOLVERS_SMT2_INCREMENTAL_RESPONSE_OR_ERROR_H
5+
6+
#include <util/invariant.h>
7+
#include <util/optional.h>
8+
9+
#include <string>
10+
#include <vector>
11+
12+
/// Holds either a valid parsed response or response sub-tree of type \tparam
13+
/// smtt or a collection of message strings explaining why the given input was
14+
/// not valid.
15+
template <class smtt>
16+
class response_or_errort final
17+
{
18+
public:
19+
explicit response_or_errort(smtt smt) : smt{std::move(smt)}
20+
{
21+
}
22+
23+
explicit response_or_errort(std::string message)
24+
: messages{std::move(message)}
25+
{
26+
}
27+
28+
explicit response_or_errort(std::vector<std::string> messages)
29+
: messages{std::move(messages)}
30+
{
31+
}
32+
33+
/// \brief Gets the smt response if the response is valid, or returns nullptr
34+
/// otherwise.
35+
const smtt *get_if_valid() const
36+
{
37+
INVARIANT(
38+
smt.has_value() == messages.empty(),
39+
"The response_or_errort class must be in the valid state or error state, "
40+
"exclusively.");
41+
return smt.has_value() ? &smt.value() : nullptr;
42+
}
43+
44+
/// \brief Gets the error messages if the response is invalid, or returns
45+
/// nullptr otherwise.
46+
const std::vector<std::string> *get_if_error() const
47+
{
48+
INVARIANT(
49+
smt.has_value() == messages.empty(),
50+
"The response_or_errort class must be in the valid state or error state, "
51+
"exclusively.");
52+
return smt.has_value() ? nullptr : &messages;
53+
}
54+
55+
private:
56+
// The below two fields could be a single `std::variant` field, if there was
57+
// an implementation of it available in the cbmc repository. However at the
58+
// time of writing we are targeting C++11, `std::variant` was introduced in
59+
// C++17 and we have no backported version.
60+
optionalt<smtt> smt;
61+
std::vector<std::string> messages;
62+
};
63+
64+
#endif // CPROVER_SOLVERS_SMT2_INCREMENTAL_RESPONSE_OR_ERROR_H

src/solvers/smt2_incremental/smt2_incremental_decision_procedure.cpp

Lines changed: 115 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,13 @@
2727
/// return a success status followed by the actual response of interest.
2828
static smt_responset get_response_to_command(
2929
smt_base_solver_processt &solver_process,
30-
const smt_commandt &command)
30+
const smt_commandt &command,
31+
const std::unordered_map<irep_idt, smt_identifier_termt> &identifier_table)
3132
{
3233
solver_process.send(command);
33-
auto response = solver_process.receive_response();
34+
auto response = solver_process.receive_response(identifier_table);
3435
if(response.cast<smt_success_responset>())
35-
return solver_process.receive_response();
36+
return solver_process.receive_response(identifier_table);
3637
else
3738
return response;
3839
}
@@ -84,6 +85,7 @@ void smt2_incremental_decision_proceduret::initialize_array_elements(
8485
const array_exprt &array,
8586
const smt_identifier_termt &array_identifier)
8687
{
88+
identifier_table.emplace(array_identifier.identifier(), array_identifier);
8789
const std::vector<exprt> &elements = array.operands();
8890
const typet &index_type = array.type().index_type();
8991
for(std::size_t i = 0; i < elements.size(); ++i)
@@ -135,13 +137,15 @@ void send_function_definition(
135137
const irep_idt &symbol_identifier,
136138
const std::unique_ptr<smt_base_solver_processt> &solver_process,
137139
std::unordered_map<exprt, smt_identifier_termt, irep_hash>
138-
&expression_identifiers)
140+
&expression_identifiers,
141+
std::unordered_map<irep_idt, smt_identifier_termt> &identifier_table)
139142
{
140143
const smt_declare_function_commandt function{
141144
smt_identifier_termt(
142145
symbol_identifier, convert_type_to_smt_sort(expr.type())),
143146
{}};
144147
expression_identifiers.emplace(expr, function.identifier());
148+
identifier_table.emplace(symbol_identifier, function.identifier());
145149
solver_process->send(function);
146150
}
147151

@@ -183,7 +187,8 @@ void smt2_incremental_decision_proceduret::define_dependent_functions(
183187
*symbol_expr,
184188
symbol_expr->get_identifier(),
185189
solver_process,
186-
expression_identifiers);
190+
expression_identifiers,
191+
identifier_table);
187192
}
188193
else
189194
{
@@ -192,6 +197,7 @@ void smt2_incremental_decision_proceduret::define_dependent_functions(
192197
const smt_define_function_commandt function{
193198
symbol->name, {}, convert_expr_to_smt(symbol->value)};
194199
expression_identifiers.emplace(*symbol_expr, function.identifier());
200+
identifier_table.emplace(identifier, function.identifier());
195201
solver_process->send(function);
196202
}
197203
}
@@ -210,7 +216,8 @@ void smt2_incremental_decision_proceduret::define_dependent_functions(
210216
*nondet_symbol,
211217
nondet_symbol->get_identifier(),
212218
solver_process,
213-
expression_identifiers);
219+
expression_identifiers,
220+
identifier_table);
214221
}
215222
to_be_defined.pop();
216223
}
@@ -263,12 +270,36 @@ void smt2_incremental_decision_proceduret::ensure_handle_for_expr_defined(
263270
smt_define_function_commandt function{
264271
"B" + std::to_string(handle_sequence()), {}, convert_expr_to_smt(expr)};
265272
expression_handle_identifiers.emplace(expr, function.identifier());
273+
identifier_table.emplace(
274+
function.identifier().identifier(), function.identifier());
266275
solver_process->send(function);
267276
}
268277

278+
void smt2_incremental_decision_proceduret::define_index_identifiers(
279+
const exprt &expr)
280+
{
281+
expr.visit_pre([&](const exprt &expr_node) {
282+
if(!can_cast_type<array_typet>(expr_node.type()))
283+
return;
284+
if(const auto with_expr = expr_try_dynamic_cast<with_exprt>(expr_node))
285+
{
286+
const auto index_expr = with_expr->where();
287+
const auto index_term = convert_expr_to_smt(index_expr);
288+
const auto index_identifier = "index_" + std::to_string(index_sequence());
289+
const auto index_definition =
290+
smt_define_function_commandt{index_identifier, {}, index_term};
291+
expression_identifiers.emplace(index_expr, index_definition.identifier());
292+
identifier_table.emplace(index_identifier, index_definition.identifier());
293+
solver_process->send(
294+
smt_define_function_commandt{index_identifier, {}, index_term});
295+
}
296+
});
297+
}
298+
269299
smt_termt
270300
smt2_incremental_decision_proceduret::convert_expr_to_smt(const exprt &expr)
271301
{
302+
define_index_identifiers(expr);
272303
const exprt substituted =
273304
substitute_identifiers(expr, expression_identifiers);
274305
track_expression_objects(substituted, ns, object_map);
@@ -310,13 +341,82 @@ static optionalt<smt_termt> get_identifier(
310341
return {};
311342
}
312343

344+
array_exprt smt2_incremental_decision_proceduret::get_expr(
345+
const smt_termt &array,
346+
const array_typet &type) const
347+
{
348+
INVARIANT(
349+
type.is_complete(), "Array size is required for getting array values.");
350+
const auto size = numeric_cast<std::size_t>(get(type.size()));
351+
INVARIANT(
352+
size,
353+
"Size of array must be convertible to std::size_t for getting array value");
354+
std::vector<exprt> elements;
355+
const auto index_type = type.index_type();
356+
elements.reserve(*size);
357+
for(std::size_t index = 0; index < size; ++index)
358+
{
359+
elements.push_back(get_expr(
360+
smt_array_theoryt::select(
361+
array,
362+
::convert_expr_to_smt(
363+
from_integer(index, index_type),
364+
object_map,
365+
pointer_sizes_map,
366+
object_size_function.make_application)),
367+
type.element_type()));
368+
}
369+
return array_exprt{elements, type};
370+
}
371+
372+
exprt smt2_incremental_decision_proceduret::get_expr(
373+
const smt_termt &descriptor,
374+
const typet &type) const
375+
{
376+
const smt_get_value_commandt get_value_command{descriptor};
377+
const smt_responset response = get_response_to_command(
378+
*solver_process, get_value_command, identifier_table);
379+
const auto get_value_response = response.cast<smt_get_value_responset>();
380+
if(!get_value_response)
381+
{
382+
throw analysis_exceptiont{
383+
"Expected get-value response from solver, but received - " +
384+
response.pretty()};
385+
}
386+
if(get_value_response->pairs().size() > 1)
387+
{
388+
throw analysis_exceptiont{
389+
"Expected single valuation pair in get-value response from solver, but "
390+
"received multiple pairs - " +
391+
response.pretty()};
392+
}
393+
return construct_value_expr_from_smt(
394+
get_value_response->pairs()[0].get().value(), type);
395+
}
396+
313397
exprt smt2_incremental_decision_proceduret::get(const exprt &expr) const
314398
{
315399
log.conditional_output(log.debug(), [&](messaget::mstreamt &debug) {
316400
debug << "`get` - \n " + expr.pretty(2, 0) << messaget::eom;
317401
});
318-
optionalt<smt_termt> descriptor =
319-
get_identifier(expr, expression_handle_identifiers, expression_identifiers);
402+
auto descriptor = [&]() -> optionalt<smt_termt> {
403+
if(const auto index_expr = expr_try_dynamic_cast<index_exprt>(expr))
404+
{
405+
const auto array = get_identifier(
406+
index_expr->array(),
407+
expression_handle_identifiers,
408+
expression_identifiers);
409+
const auto index = get_identifier(
410+
index_expr->index(),
411+
expression_handle_identifiers,
412+
expression_identifiers);
413+
if(!array || !index)
414+
return {};
415+
return smt_array_theoryt::select(*array, *index);
416+
}
417+
return get_identifier(
418+
expr, expression_handle_identifiers, expression_identifiers);
419+
}();
320420
if(!descriptor)
321421
{
322422
if(gather_dependent_expressions(expr).empty())
@@ -349,25 +449,13 @@ exprt smt2_incremental_decision_proceduret::get(const exprt &expr) const
349449
return expr;
350450
}
351451
}
352-
const smt_get_value_commandt get_value_command{*descriptor};
353-
const smt_responset response =
354-
get_response_to_command(*solver_process, get_value_command);
355-
const auto get_value_response = response.cast<smt_get_value_responset>();
356-
if(!get_value_response)
357-
{
358-
throw analysis_exceptiont{
359-
"Expected get-value response from solver, but received - " +
360-
response.pretty()};
361-
}
362-
if(get_value_response->pairs().size() > 1)
452+
if(const auto array_type = type_try_dynamic_cast<array_typet>(expr.type()))
363453
{
364-
throw analysis_exceptiont{
365-
"Expected single valuation pair in get-value response from solver, but "
366-
"received multiple pairs - " +
367-
response.pretty()};
454+
if(array_type->is_incomplete())
455+
return expr;
456+
return get_expr(*descriptor, *array_type);
368457
}
369-
return construct_value_expr_from_smt(
370-
get_value_response->pairs()[0].get().value(), expr.type());
458+
return get_expr(*descriptor, expr.type());
371459
}
372460

373461
void smt2_incremental_decision_proceduret::print_assignment(
@@ -464,8 +552,8 @@ decision_proceduret::resultt smt2_incremental_decision_proceduret::dec_solve()
464552
{
465553
++number_of_solver_calls;
466554
define_object_sizes();
467-
const smt_responset result =
468-
get_response_to_command(*solver_process, smt_check_sat_commandt{});
555+
const smt_responset result = get_response_to_command(
556+
*solver_process, smt_check_sat_commandt{}, identifier_table);
469557
if(const auto check_sat_response = result.cast<smt_check_sat_responset>())
470558
{
471559
if(check_sat_response->kind().cast<smt_unknown_responset>())

src/solvers/smt2_incremental/smt2_incremental_decision_procedure.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ class smt2_incremental_decision_proceduret final
5252
void push() override;
5353
void pop() override;
5454

55+
/// Gets the value of \p descriptor from the solver and returns the solver
56+
/// response expressed as an exprt of type \p type. This is an implementation
57+
/// detail of the `get(exprt)` member function.
58+
exprt get_expr(const smt_termt &descriptor, const typet &type) const;
59+
array_exprt get_expr(const smt_termt &array, const array_typet &type) const;
60+
5561
protected:
5662
// Implementation of protected decision_proceduret member function.
5763
resultt dec_solve() override;
@@ -85,6 +91,7 @@ class smt2_incremental_decision_proceduret final
8591
/// \brief Add objects in \p expr to object_map if needed and convert to smt.
8692
/// \note This function is non-const because it mutates the object_map.
8793
smt_termt convert_expr_to_smt(const exprt &expr);
94+
void define_index_identifiers(const exprt &expr);
8895
/// Sends the solver the definitions of the object sizes.
8996
void define_object_sizes();
9097

@@ -112,7 +119,7 @@ class smt2_incremental_decision_proceduret final
112119
{
113120
return next_id++;
114121
}
115-
} handle_sequence, array_sequence;
122+
} handle_sequence, array_sequence, index_sequence;
116123
/// When the `handle(exprt)` member function is called, the decision procedure
117124
/// commands the SMT solver to define a new function corresponding to the
118125
/// given expression. The mapping of the expressions to the function
@@ -131,6 +138,7 @@ class smt2_incremental_decision_proceduret final
131138
/// array expressions when support for them is implemented.
132139
std::unordered_map<exprt, smt_identifier_termt, irep_hash>
133140
expression_identifiers;
141+
std::unordered_map<irep_idt, smt_identifier_termt> identifier_table;
134142
/// This map is used to track object related state. See documentation in
135143
/// object_tracking.h for details.
136144
smt_object_mapt object_map;

0 commit comments

Comments
 (0)