Skip to content

Commit 208ca7a

Browse files
Merge pull request #6573 from thomasspriggs/tas/smt2_incremental_solving
Add implementation of `smt2_incremental_decision_proceduret::get`
2 parents 181ab42 + a3f6bd4 commit 208ca7a

File tree

6 files changed

+213
-8
lines changed

6 files changed

+213
-8
lines changed

regression/cbmc-incr-smt2/nondeterministic-int-assert/control_flow.desc

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,11 @@ Sending command to SMT2 solver - \(define-fun |B4| \(\) Bool \(|not| false\)\)
2020
Sending command to SMT2 solver - \(assert |B4|\)
2121
Sending command to SMT2 solver - \(check-sat\)
2222
Solver response - sat
23-
^EXIT=(0|127|134|137)$
23+
^EXIT=10$
2424
^SIGNAL=0$
2525
--
2626
type: pointer
2727
--
2828
Test that running cbmc with the `--incremental-smt2-solver` argument can be used
2929
to send a valid SMT2 formula to a sub-process solver for an example input file
30-
which include control flow constructs. Note that at the time of adding this
31-
test, an invariant violation is expected due to the unimplemented response
32-
parsing.
30+
which include control flow constructs.

regression/cbmc-incr-smt2/nondeterministic-int-assert/incremental_solver_called.desc

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,13 @@ Sending command to SMT2 solver - \(define-fun |B2| \(\) Bool \(|not| false\)\)
1212
Sending command to SMT2 solver - \(assert |B2|\)
1313
Sending command to SMT2 solver - \(check-sat\)
1414
Solver response - sat
15-
^EXIT=(0|127|134|137)$
15+
^EXIT=10$
1616
^SIGNAL=0$
1717
--
1818
type: pointer
1919
--
2020
Test that running cbmc with the `--incremental-smt2-solver` argument causes the
21-
incremental smt2 solving to be used. Note that at the time of adding this test,
22-
an invariant violation is expected due to the unimplemented response parsing.
21+
incremental smt2 solving to be used.
2322

2423
The sliced formula is expected to use only the implemented subset of exprts.
2524
This is implementation is sufficient to send this example to the solver and
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
int main()
3+
{
4+
int x, y;
5+
if(x != 0)
6+
__CPROVER_assert(y != 4, "Assert of inequality to 4.");
7+
else
8+
__CPROVER_assert(y != 2, "Assert of inequality to 2.");
9+
int z = y;
10+
return 0;
11+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
CORE winbug
2+
trace.c
3+
--incremental-smt2-solver "z3 --smt2 -in" --trace
4+
Passing problem to incremental SMT2 solving via "z3 --smt2 -in"
5+
Assert of inequality to 4\.: FAILURE
6+
Assert of inequality to 2\.: FAILURE
7+
y=4
8+
y=2
9+
VERIFICATION FAILED
10+
^EXIT=10$
11+
^SIGNAL=0$
12+
--
13+
type: pointer
14+
--
15+
Test that running cbmc with the `--incremental-smt2-solver` argument can be used
16+
to send a valid SMT2 formula to a sub-process solver for an example input file
17+
which include control flow constructs.

src/solvers/smt2_incremental/smt2_incremental_decision_procedure.cpp

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "smt2_incremental_decision_procedure.h"
44

5+
#include <solvers/smt2_incremental/construct_value_expr_from_smt.h>
56
#include <solvers/smt2_incremental/convert_expr_to_smt.h>
67
#include <solvers/smt2_incremental/smt_commands.h>
78
#include <solvers/smt2_incremental/smt_core_theory.h>
@@ -163,9 +164,70 @@ exprt smt2_incremental_decision_proceduret::handle(const exprt &expr)
163164
return expr;
164165
}
165166

167+
static optionalt<smt_termt> get_identifier(
168+
const exprt &expr,
169+
const std::unordered_map<exprt, smt_identifier_termt, irep_hash>
170+
&expression_handle_identifiers,
171+
const std::unordered_map<exprt, smt_identifier_termt, irep_hash>
172+
&expression_identifiers)
173+
{
174+
const auto handle_find_result = expression_handle_identifiers.find(expr);
175+
if(handle_find_result != expression_handle_identifiers.cend())
176+
return handle_find_result->second;
177+
const auto expr_find_result = expression_identifiers.find(expr);
178+
if(expr_find_result != expression_identifiers.cend())
179+
return expr_find_result->second;
180+
return {};
181+
}
182+
166183
exprt smt2_incremental_decision_proceduret::get(const exprt &expr) const
167184
{
168-
UNIMPLEMENTED_FEATURE("`get` of:\n " + expr.pretty(2, 0));
185+
log.debug() << "`get` - \n " + expr.pretty(2, 0) << messaget::eom;
186+
optionalt<smt_termt> descriptor =
187+
get_identifier(expr, expression_handle_identifiers, expression_identifiers);
188+
if(!descriptor)
189+
{
190+
if(gather_dependent_expressions(expr).empty())
191+
{
192+
descriptor = convert_expr_to_smt(expr);
193+
}
194+
else
195+
{
196+
const auto symbol_expr = expr_try_dynamic_cast<symbol_exprt>(expr);
197+
INVARIANT(
198+
symbol_expr, "Unhandled expressions are expected to be symbols");
199+
// Note this case is currently expected to be encountered during trace
200+
// generation for -
201+
// * Steps which were removed via --slice-formula.
202+
// * Getting concurrency clock values.
203+
// The below implementation which returns the given expression was chosen
204+
// based on the implementation of `smt2_convt::get` in the non-incremental
205+
// smt2 decision procedure.
206+
log.warning()
207+
<< "`get` attempted for unknown symbol, with identifier - \n"
208+
<< symbol_expr->get_identifier() << messaget::eom;
209+
return expr;
210+
}
211+
}
212+
const smt_get_value_commandt get_value_command{*descriptor};
213+
const smt_responset response =
214+
get_response_to_command(*solver_process, get_value_command);
215+
const auto get_value_response = response.cast<smt_get_value_responset>();
216+
if(!get_value_response)
217+
{
218+
throw analysis_exceptiont{
219+
"Expected get-value response from solver, but received - " +
220+
response.pretty()};
221+
}
222+
if(get_value_response->pairs().size() > 1)
223+
{
224+
throw analysis_exceptiont{
225+
"Expected single valuation pair in get-value response from solver, but "
226+
"received multiple pairs - " +
227+
response.pretty()};
228+
}
229+
return construct_value_expr_from_smt(
230+
get_value_response->pairs()[0].get().value(), expr.type());
169231
}
170232

171233
void smt2_incremental_decision_proceduret::print_assignment(

unit/solvers/smt2_incremental/smt2_incremental_decision_procedure.cpp

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include <solvers/smt2_incremental/smt_solver_process.h>
1010
#include <solvers/smt2_incremental/smt_sorts.h>
1111
#include <solvers/smt2_incremental/smt_terms.h>
12+
#include <testing-utils/invariant.h>
1213
#include <util/arith_tools.h>
1314
#include <util/bitvector_types.h>
1415
#include <util/exception_utils.h>
@@ -85,6 +86,14 @@ class smt_mock_solver_processt : public smt_base_solver_processt
8586
~smt_mock_solver_processt() override = default;
8687
};
8788

89+
/// \brief Data structures and their initialisation shared between tests.
90+
/// \details
91+
/// Instantiates a `smt2_incremental_decision_proceduret` using a mock of the
92+
/// solver process to direct communication with the solver to collections of
93+
/// `sent_commands` and `mock_responses`. The `mock_respones` must be
94+
/// populated by the test, before the decision procedure expects them. The
95+
/// `sent_commands` should be checked by the test after the decision procedure
96+
/// has sent them.
8897
struct decision_procedure_test_environmentt final
8998
{
9099
void send(const smt_commandt &smt_command);
@@ -378,3 +387,112 @@ TEST_CASE(
378387
"SMT solver does not support given command."});
379388
}
380389
}
390+
391+
TEST_CASE(
392+
"smt2_incremental_decision_proceduret getting values back from solver.",
393+
"[core][smt2_incremental]")
394+
{
395+
decision_procedure_test_environmentt test{};
396+
const symbolt foo = make_test_symbol("foo", signedbv_typet{16});
397+
const smt_identifier_termt foo_term{"foo", smt_bit_vector_sortt{16}};
398+
const exprt expr_42 = from_integer({42}, signedbv_typet{16});
399+
const smt_bit_vector_constant_termt term_42{42, 16};
400+
SECTION("Set \"foo\" identifier and solve.")
401+
{
402+
test.sent_commands.clear();
403+
const exprt equal_42 = equal_exprt{foo.symbol_expr(), expr_42};
404+
test.procedure.set_to(equal_42, true);
405+
test.mock_responses.push_back(smt_check_sat_responset{smt_sat_responset{}});
406+
test.procedure();
407+
REQUIRE(
408+
test.sent_commands ==
409+
std::vector<smt_commandt>{
410+
smt_declare_function_commandt{foo_term, {}},
411+
smt_assert_commandt{smt_core_theoryt::equal(foo_term, term_42)},
412+
smt_check_sat_commandt{}});
413+
SECTION("Get \"foo\" value back")
414+
{
415+
test.sent_commands.clear();
416+
test.mock_responses.push_back(
417+
smt_get_value_responset{{{foo_term, term_42}}});
418+
REQUIRE(test.procedure.get(foo.symbol_expr()) == expr_42);
419+
REQUIRE(
420+
test.sent_commands ==
421+
std::vector<smt_commandt>{smt_get_value_commandt{foo_term}});
422+
}
423+
SECTION("Get value of non-set symbol")
424+
{
425+
// smt2_incremental_decision_proceduret is used this way when cbmc is
426+
// invoked with the combination of `--trace` and `--slice-formula`.
427+
test.sent_commands.clear();
428+
const exprt bar =
429+
make_test_symbol("bar", signedbv_typet{16}).symbol_expr();
430+
REQUIRE(test.procedure.get(bar) == bar);
431+
REQUIRE(test.sent_commands.empty());
432+
}
433+
SECTION("Get value of type less symbol back")
434+
{
435+
// smt2_incremental_decision_proceduret is used this way as part of
436+
// building the goto trace, to get the partial order concurrency clock
437+
// values.
438+
test.sent_commands.clear();
439+
const symbol_exprt baz = symbol_exprt::typeless("baz");
440+
REQUIRE(test.procedure.get(baz) == baz);
441+
REQUIRE(test.sent_commands.empty());
442+
}
443+
SECTION("Get value of trivially solved expression")
444+
{
445+
test.sent_commands.clear();
446+
const smt_termt not_true_term =
447+
smt_core_theoryt::make_not(smt_bool_literal_termt{true});
448+
test.mock_responses.push_back(smt_get_value_responset{
449+
{{not_true_term, smt_bool_literal_termt{false}}}});
450+
REQUIRE(test.procedure.get(not_exprt{true_exprt{}}) == false_exprt{});
451+
REQUIRE(
452+
test.sent_commands ==
453+
std::vector<smt_commandt>{smt_get_value_commandt{not_true_term}});
454+
}
455+
SECTION("Invariant violated due to expression in unexpected form.")
456+
{
457+
const mult_exprt unexpected{foo.symbol_expr(), from_integer(2, foo.type)};
458+
const cbmc_invariants_should_throwt invariants_throw;
459+
REQUIRE_THROWS_MATCHES(
460+
test.procedure.get(unexpected),
461+
invariant_failedt,
462+
invariant_failure_containing(
463+
"Unhandled expressions are expected to be symbols"));
464+
}
465+
SECTION("Error handling of mismatched response.")
466+
{
467+
test.sent_commands.clear();
468+
const smt_check_sat_responset unexpected{smt_sat_responset{}};
469+
test.mock_responses.push_back(unexpected);
470+
REQUIRE_THROWS_MATCHES(
471+
test.procedure.get(foo.symbol_expr()),
472+
analysis_exceptiont,
473+
analysis_execption_with_messaget{
474+
"Expected get-value response from solver, but received - " +
475+
unexpected.pretty()});
476+
REQUIRE(
477+
test.sent_commands ==
478+
std::vector<smt_commandt>{smt_get_value_commandt{foo_term}});
479+
}
480+
SECTION("Error handling of multiple responses.")
481+
{
482+
test.sent_commands.clear();
483+
const smt_get_value_responset unexpected{
484+
{{foo_term, term_42}, {foo_term, term_42}}};
485+
test.mock_responses.push_back(unexpected);
486+
REQUIRE_THROWS_MATCHES(
487+
test.procedure.get(foo.symbol_expr()),
488+
analysis_exceptiont,
489+
analysis_execption_with_messaget{
490+
"Expected single valuation pair in get-value response from solver, "
491+
"but received multiple pairs - " +
492+
unexpected.pretty()});
493+
REQUIRE(
494+
test.sent_commands ==
495+
std::vector<smt_commandt>{smt_get_value_commandt{foo_term}});
496+
}
497+
}
498+
}

0 commit comments

Comments
 (0)