Skip to content

Commit 453eb65

Browse files
Merge pull request #7740 from thomasspriggs/tas/smt2_structs
Add initial struct support for incremental SMT2 decision procedure
2 parents b155fb0 + a06d62f commit 453eb65

File tree

9 files changed

+264
-5
lines changed

9 files changed

+264
-5
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#include <assert.h>
2+
#include <stdint.h>
3+
4+
struct my_struct_type
5+
{
6+
int16_t a;
7+
int16_t b;
8+
};
9+
10+
int main()
11+
{
12+
int16_t nondet;
13+
struct my_struct_type my_struct;
14+
if(nondet == 3)
15+
my_struct.a = 10;
16+
else
17+
my_struct.a = 12;
18+
struct my_struct_type struct_copy;
19+
struct_copy = my_struct;
20+
assert(my_struct.b != 30 || struct_copy.a != 10);
21+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
CORE
2+
simple.c
3+
--trace
4+
Passing problem to incremental SMT2 solving
5+
line 20 assertion my_struct.b != 30 || struct_copy.a != 10: FAILURE
6+
nondet=3
7+
struct_copy.a=10
8+
struct_copy.b=30
9+
^EXIT=10$
10+
^SIGNAL=0$
11+
--
12+
--
13+
Test that we support struct values and traces for a trivial example. At the
14+
time of adding the regression test, this exercised the conversion code specific
15+
to struct_tag_typet and struct_exprt. Field sensitivity is expected to eliminate
16+
many of the struct specific expressions before they can reach the decision
17+
procedure with this simple example.

src/solvers/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ SRC = $(BOOLEFORCE_SRC) \
210210
smt2_incremental/smt_solver_process.cpp \
211211
smt2_incremental/smt_to_smt2_string.cpp \
212212
smt2_incremental/smt2_incremental_decision_procedure.cpp \
213+
smt2_incremental/struct_encoding.cpp \
213214
smt2_incremental/theories/smt_array_theory.cpp \
214215
smt2_incremental/theories/smt_bit_vector_theory.cpp \
215216
smt2_incremental/theories/smt_core_theory.cpp \

src/solvers/smt2_incremental/smt2_incremental_decision_procedure.cpp

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,8 @@ smt2_incremental_decision_proceduret::smt2_incremental_decision_proceduret(
249249
number_of_solver_calls{0},
250250
solver_process(std::move(_solver_process)),
251251
log{message_handler},
252-
object_map{initial_smt_object_map()}
252+
object_map{initial_smt_object_map()},
253+
struct_encoding{_ns}
253254
{
254255
solver_process->send(
255256
smt_set_option_commandt{smt_option_produce_modelst{true}});
@@ -268,7 +269,7 @@ void smt2_incremental_decision_proceduret::ensure_handle_for_expr_defined(
268269
return;
269270
}
270271

271-
const exprt lowered_expr = lower_byte_operators(in_expr, ns);
272+
const exprt lowered_expr = lower(in_expr);
272273

273274
define_dependent_functions(lowered_expr);
274275
smt_define_function_commandt function{
@@ -514,12 +515,12 @@ void smt2_incremental_decision_proceduret::set_to(
514515
const exprt &in_expr,
515516
bool value)
516517
{
517-
const exprt lowered_expr = lower_byte_operators(in_expr, ns);
518-
PRECONDITION(can_cast_type<bool_typet>(lowered_expr.type()));
519518
log.conditional_output(log.debug(), [&](messaget::mstreamt &debug) {
520519
debug << "`set_to` (" << std::string{value ? "true" : "false"} << ") -\n "
521-
<< lowered_expr.pretty(2, 0) << messaget::eom;
520+
<< in_expr.pretty(2, 0) << messaget::eom;
522521
});
522+
const exprt lowered_expr = lower(in_expr);
523+
PRECONDITION(can_cast_type<bool_typet>(lowered_expr.type()));
523524

524525
define_dependent_functions(lowered_expr);
525526
auto converted_term = [&]() -> smt_termt {
@@ -587,6 +588,17 @@ void smt2_incremental_decision_proceduret::define_object_properties()
587588
}
588589
}
589590

591+
exprt smt2_incremental_decision_proceduret::lower(exprt expression)
592+
{
593+
const exprt lowered =
594+
struct_encoding.encode(lower_byte_operators(expression, ns));
595+
log.conditional_output(log.debug(), [&](messaget::mstreamt &debug) {
596+
if(lowered != expression)
597+
debug << "lowered to -\n " << lowered.pretty(2, 0) << messaget::eom;
598+
});
599+
return lowered;
600+
}
601+
590602
decision_proceduret::resultt smt2_incremental_decision_proceduret::dec_solve()
591603
{
592604
++number_of_solver_calls;

src/solvers/smt2_incremental/smt2_incremental_decision_procedure.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <solvers/smt2_incremental/object_tracking.h>
1414
#include <solvers/smt2_incremental/smt_is_dynamic_object.h>
1515
#include <solvers/smt2_incremental/smt_object_size.h>
16+
#include <solvers/smt2_incremental/struct_encoding.h>
1617
#include <solvers/smt2_incremental/type_size_mapping.h>
1718
#include <solvers/stack_decision_procedure.h>
1819

@@ -93,6 +94,10 @@ class smt2_incremental_decision_proceduret final
9394
/// Sends the solver the definitions of the object sizes and dynamic memory
9495
/// statuses.
9596
void define_object_properties();
97+
/// Performs a combination of transformations which reduces the set of
98+
/// possible expression forms by expressing these in terms of the remaining
99+
/// language features.
100+
exprt lower(exprt expression);
96101

97102
/// Namespace for looking up the expressions which symbol_exprts relate to.
98103
/// This includes the symbols defined outside of the decision procedure but
@@ -165,6 +170,7 @@ class smt2_incremental_decision_proceduret final
165170
smt_is_dynamic_objectt is_dynamic_object_function;
166171
/// Precalculated type sizes used for pointer arithmetic.
167172
type_size_mapt pointer_sizes_map;
173+
struct_encodingt struct_encoding;
168174
};
169175

170176
#endif // CPROVER_SOLVERS_SMT2_INCREMENTAL_SMT2_INCREMENTAL_DECISION_PROCEDURE_H
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Author: Diffblue Ltd.
2+
3+
#include "struct_encoding.h"
4+
5+
#include <util/bitvector_expr.h>
6+
#include <util/bitvector_types.h>
7+
#include <util/make_unique.h>
8+
9+
#include <solvers/flattening/boolbv_width.h>
10+
11+
#include <queue>
12+
13+
struct_encodingt::struct_encodingt(const namespacet &ns)
14+
: boolbv_width{util_make_unique<boolbv_widtht>(ns)}
15+
{
16+
}
17+
18+
struct_encodingt::struct_encodingt(const struct_encodingt &other)
19+
: boolbv_width{util_make_unique<boolbv_widtht>(*other.boolbv_width)}
20+
{
21+
}
22+
23+
struct_encodingt::~struct_encodingt() = default;
24+
25+
typet struct_encodingt::encode(typet type) const
26+
{
27+
std::queue<typet *> work_queue;
28+
work_queue.push(&type);
29+
while(!work_queue.empty())
30+
{
31+
typet &current = *work_queue.front();
32+
work_queue.pop();
33+
if(const auto struct_tag = type_try_dynamic_cast<struct_tag_typet>(current))
34+
{
35+
current = bv_typet{(*boolbv_width)(*struct_tag)};
36+
}
37+
if(const auto array = type_try_dynamic_cast<array_typet>(current))
38+
{
39+
work_queue.push(&array->element_type());
40+
}
41+
}
42+
return type;
43+
}
44+
45+
static exprt encode(const struct_exprt &struct_expr)
46+
{
47+
return concatenation_exprt{struct_expr.operands(), struct_expr.type()};
48+
}
49+
50+
exprt struct_encodingt::encode(exprt expr) const
51+
{
52+
std::queue<exprt *> work_queue;
53+
work_queue.push(&expr);
54+
while(!work_queue.empty())
55+
{
56+
exprt &current = *work_queue.front();
57+
work_queue.pop();
58+
current.type() = encode(current.type());
59+
if(const auto struct_expr = expr_try_dynamic_cast<struct_exprt>(current))
60+
current = ::encode(*struct_expr);
61+
for(auto &operand : current.operands())
62+
work_queue.push(&operand);
63+
}
64+
return expr;
65+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Author: Diffblue Ltd.
2+
3+
#ifndef CPROVER_SOLVERS_SMT2_INCREMENTAL_STRUCT_ENCODING_H
4+
#define CPROVER_SOLVERS_SMT2_INCREMENTAL_STRUCT_ENCODING_H
5+
6+
#include <util/expr.h> // For passing exprt by value. // IWYU pragma: keep
7+
#include <util/type.h> // For passing `typet` by value. // IWYU pragma: keep
8+
9+
#include <memory>
10+
11+
class namespacet;
12+
class boolbv_widtht;
13+
14+
/// Encodes struct types/values into non-struct expressions/types.
15+
class struct_encodingt final
16+
{
17+
public:
18+
explicit struct_encodingt(const namespacet &ns);
19+
struct_encodingt(const struct_encodingt &other);
20+
~struct_encodingt();
21+
22+
typet encode(typet type) const;
23+
exprt encode(exprt expr) const;
24+
25+
private:
26+
std::unique_ptr<boolbv_widtht> boolbv_width;
27+
};
28+
29+
#endif // CPROVER_SOLVERS_SMT2_INCREMENTAL_STRUCT_ENCODING_H

unit/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ SRC += analyses/ai/ai.cpp \
114114
solvers/smt2_incremental/smt_object_size.cpp \
115115
solvers/smt2_incremental/smt_response_validation.cpp \
116116
solvers/smt2_incremental/smt_to_smt2_string.cpp \
117+
solvers/smt2_incremental/struct_encoding.cpp \
117118
solvers/smt2_incremental/theories/smt_array_theory.cpp \
118119
solvers/smt2_incremental/theories/smt_bit_vector_theory.cpp \
119120
solvers/smt2_incremental/theories/smt_core_theory.cpp \
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
// Author: Diffblue Ltd.
2+
3+
#include <util/arith_tools.h>
4+
#include <util/bitvector_expr.h>
5+
#include <util/bitvector_types.h>
6+
#include <util/namespace.h>
7+
#include <util/symbol_table.h>
8+
9+
#include <solvers/smt2_incremental/struct_encoding.h>
10+
#include <testing-utils/use_catch.h>
11+
12+
struct struct_encoding_test_environmentt
13+
{
14+
symbol_tablet symbol_table;
15+
namespacet ns{symbol_table};
16+
struct_encodingt struct_encoding{ns};
17+
18+
static struct_encoding_test_environmentt make()
19+
{
20+
return {};
21+
}
22+
23+
private:
24+
struct_encoding_test_environmentt() = default;
25+
};
26+
27+
TEST_CASE(
28+
"struct encoding of non-stuct type is a no-op",
29+
"[core][smt2_incremental]")
30+
{
31+
auto test = struct_encoding_test_environmentt::make();
32+
typet input = signedbv_typet{8};
33+
REQUIRE(test.struct_encoding.encode(input) == input);
34+
}
35+
36+
TEST_CASE("struct encoding of types", "[core][smt2_incremental]")
37+
{
38+
const struct_union_typet::componentst components{
39+
{"foo", unsignedbv_typet{8}}, {"bar", signedbv_typet{16}}};
40+
struct_typet struct_type{components};
41+
type_symbolt type_symbol{"my_structt", struct_type, ID_C};
42+
auto test = struct_encoding_test_environmentt::make();
43+
test.symbol_table.insert(type_symbol);
44+
struct_tag_typet struct_tag{type_symbol.name};
45+
SECTION("direct struct_tag_type encoding")
46+
{
47+
REQUIRE(test.struct_encoding.encode(struct_tag) == bv_typet{24});
48+
}
49+
SECTION("array of structs encoding")
50+
{
51+
const auto index_type = signedbv_typet{32};
52+
const auto array_size = from_integer(5, index_type);
53+
array_typet array_of_struct{struct_tag, array_size};
54+
array_typet expected_encoded_array{bv_typet{24}, array_size};
55+
REQUIRE(
56+
test.struct_encoding.encode(array_of_struct) == expected_encoded_array);
57+
}
58+
SECTION("array of array of structs encoding")
59+
{
60+
const auto index_type = signedbv_typet{32};
61+
const auto array_size_inner = from_integer(4, index_type);
62+
const auto array_size_outer = from_integer(2, index_type);
63+
array_typet array_of_struct{struct_tag, array_size_inner};
64+
array_typet array_of_array_of_struct{array_of_struct, array_size_outer};
65+
array_typet expected_encoded_array{
66+
array_typet{bv_typet{24}, array_size_inner}, array_size_outer};
67+
REQUIRE(
68+
test.struct_encoding.encode(array_of_array_of_struct) ==
69+
expected_encoded_array);
70+
}
71+
}
72+
73+
TEST_CASE("struct encoding of expressions", "[core][smt2_incremental]")
74+
{
75+
const struct_union_typet::componentst component_types{
76+
{"green", signedbv_typet{32}}, {"eggs", unsignedbv_typet{16}}};
77+
const struct_typet struct_type{component_types};
78+
const type_symbolt type_symbol{"my_structt", struct_type, ID_C};
79+
auto test = struct_encoding_test_environmentt::make();
80+
test.symbol_table.insert(type_symbol);
81+
const struct_tag_typet struct_tag{type_symbol.name};
82+
const symbolt struct_value_symbol{"my_struct", struct_tag, ID_C};
83+
test.symbol_table.insert(struct_value_symbol);
84+
const auto symbol_expr = struct_value_symbol.symbol_expr();
85+
const auto symbol_expr_as_bv = symbol_exprt{"my_struct", bv_typet{48}};
86+
SECTION("struct typed symbol expression")
87+
{
88+
REQUIRE(test.struct_encoding.encode(symbol_expr) == symbol_expr_as_bv);
89+
}
90+
SECTION("struct equality expression")
91+
{
92+
const auto struct_equal = equal_exprt{symbol_expr, symbol_expr};
93+
const auto bv_equal = equal_exprt{symbol_expr_as_bv, symbol_expr_as_bv};
94+
REQUIRE(test.struct_encoding.encode(struct_equal) == bv_equal);
95+
}
96+
SECTION("expression for a struct from list of components")
97+
{
98+
const symbolt green_ham{"ham", signedbv_typet{32}, ID_C};
99+
test.symbol_table.insert(green_ham);
100+
const auto forty_two = from_integer(42, unsignedbv_typet{16});
101+
const exprt::operandst components{green_ham.symbol_expr(), forty_two};
102+
const struct_exprt struct_expr{components, struct_tag};
103+
const concatenation_exprt expected_result{
104+
{green_ham.symbol_expr(), forty_two}, bv_typet{48}};
105+
REQUIRE(test.struct_encoding.encode(struct_expr) == expected_result);
106+
}
107+
}

0 commit comments

Comments
 (0)