Skip to content

Commit a043228

Browse files
Merge pull request #7926 from thomasspriggs/tas/smt_union_exprt
Add `union_exprt` encoding support in incremental smt2 decision procedure
2 parents 99c5402 + c78a2f6 commit a043228

File tree

11 files changed

+239
-4
lines changed

11 files changed

+239
-4
lines changed

src/solvers/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,7 @@ SRC = $(BOOLEFORCE_SRC) \
211211
smt2_incremental/smt2_incremental_decision_procedure.cpp \
212212
smt2_incremental/encoding/struct_encoding.cpp \
213213
smt2_incremental/encoding/enum_encoding.cpp \
214+
smt2_incremental/encoding/nondet_padding.cpp \
214215
smt2_incremental/theories/smt_array_theory.cpp \
215216
smt2_incremental/theories/smt_bit_vector_theory.cpp \
216217
smt2_incremental/theories/smt_core_theory.cpp \
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
util
1+
solvers/smt2_incremental/encoding
2+
util
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Author: Diffblue Ltd.
2+
3+
#include "nondet_padding.h"
4+
5+
const irep_idt nondet_padding_exprt::ID_nondet_padding = "nondet_padding";
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Author: Diffblue Ltd.
2+
3+
/// \file
4+
/// Expressions for use in incremental SMT2 decision procedure
5+
6+
#ifndef CPROVER_SOLVERS_SMT2_INCREMENTAL_ENCODING_NONDET_PADDING_H
7+
#define CPROVER_SOLVERS_SMT2_INCREMENTAL_ENCODING_NONDET_PADDING_H
8+
9+
#include <util/bitvector_types.h>
10+
#include <util/expr.h>
11+
#include <util/invariant.h>
12+
13+
class nondet_padding_exprt;
14+
void validate_expr(const nondet_padding_exprt &padding);
15+
16+
/// This expression serves as a placeholder for the bits which have non
17+
/// deterministic value in a larger bit vector. It is inserted in contexts where
18+
/// a subset of the bits are assigned to an expression and the remainder are
19+
/// left unspecified.
20+
class nondet_padding_exprt : public expr_protectedt
21+
{
22+
public:
23+
static const irep_idt ID_nondet_padding;
24+
25+
explicit nondet_padding_exprt(typet type)
26+
: expr_protectedt{ID_nondet_padding, std::move(type)}
27+
{
28+
validate_expr(*this);
29+
}
30+
};
31+
32+
template <>
33+
inline bool can_cast_expr<nondet_padding_exprt>(const exprt &base)
34+
{
35+
return base.id() == nondet_padding_exprt::ID_nondet_padding;
36+
}
37+
38+
inline void validate_expr(const nondet_padding_exprt &padding)
39+
{
40+
INVARIANT(
41+
can_cast_type<bv_typet>(padding.type()),
42+
"Nondet padding is expected to pad a bit vector type.");
43+
}
44+
45+
#endif // CPROVER_SOLVERS_SMT2_INCREMENTAL_ENCODING_NONDET_PADDING_H

src/solvers/smt2_incremental/encoding/struct_encoding.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <util/simplify_expr.h>
1313

1414
#include <solvers/flattening/boolbv_width.h>
15+
#include <solvers/smt2_incremental/encoding/nondet_padding.h>
1516

1617
#include <algorithm>
1718
#include <numeric>
@@ -136,6 +137,22 @@ static exprt encode(const struct_exprt &struct_expr)
136137
return concatenation_exprt{struct_expr.operands(), struct_expr.type()};
137138
}
138139

140+
static exprt
141+
encode(const union_exprt &union_expr, const boolbv_widtht &bit_width)
142+
{
143+
const std::size_t union_width = bit_width(union_expr.type());
144+
const exprt &component = union_expr.op();
145+
const std::size_t component_width = bit_width(union_expr.op().type());
146+
if(union_width == component_width)
147+
return typecast_exprt(component, bv_typet{union_width});
148+
INVARIANT(
149+
union_width >= component_width,
150+
"Union must be at least as wide as its component.");
151+
return concatenation_exprt{
152+
{nondet_padding_exprt{bv_typet{union_width - component_width}}, component},
153+
bv_typet{union_width}};
154+
}
155+
139156
static std::size_t count_trailing_bit_width(
140157
const struct_typet &type,
141158
const irep_idt name,
@@ -195,6 +212,8 @@ exprt struct_encodingt::encode(exprt expr) const
195212
current.type() = encode(current.type());
196213
if(const auto struct_expr = expr_try_dynamic_cast<struct_exprt>(current))
197214
current = ::encode(*struct_expr);
215+
if(const auto union_expr = expr_try_dynamic_cast<union_exprt>(current))
216+
current = ::encode(*union_expr, *boolbv_width);
198217
if(const auto member_expr = expr_try_dynamic_cast<member_exprt>(current))
199218
current = encode_member(*member_expr);
200219
for(auto &operand : current.operands())

src/solvers/smt2_incremental/smt2_incremental_decision_procedure.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <solvers/smt2_incremental/construct_value_expr_from_smt.h>
1919
#include <solvers/smt2_incremental/convert_expr_to_smt.h>
2020
#include <solvers/smt2_incremental/encoding/enum_encoding.h>
21+
#include <solvers/smt2_incremental/encoding/nondet_padding.h>
2122
#include <solvers/smt2_incremental/smt_solver_process.h>
2223
#include <solvers/smt2_incremental/theories/smt_array_theory.h>
2324
#include <solvers/smt2_incremental/theories/smt_core_theory.h>
@@ -364,12 +365,28 @@ void smt2_incremental_decision_proceduret::define_index_identifiers(
364365
});
365366
}
366367

368+
exprt smt2_incremental_decision_proceduret::substitute_defined_padding(
369+
exprt root_expr)
370+
{
371+
root_expr.visit_pre([&](exprt &node) {
372+
if(const auto pad = expr_try_dynamic_cast<nondet_padding_exprt>(node))
373+
{
374+
const auto instance = "padding_" + std::to_string(padding_sequence());
375+
const auto term =
376+
smt_identifier_termt{instance, convert_type_to_smt_sort(pad->type())};
377+
solver_process->send(smt_declare_function_commandt{term, {}});
378+
node = symbol_exprt{instance, node.type()};
379+
}
380+
});
381+
return root_expr;
382+
}
383+
367384
smt_termt
368385
smt2_incremental_decision_proceduret::convert_expr_to_smt(const exprt &expr)
369386
{
370387
define_index_identifiers(expr);
371-
const exprt substituted =
372-
substitute_identifiers(expr, expression_identifiers);
388+
const exprt substituted = substitute_defined_padding(
389+
substitute_identifiers(expr, expression_identifiers));
373390
track_expression_objects(substituted, ns, object_map);
374391
associate_pointer_sizes(
375392
substituted,

src/solvers/smt2_incremental/smt2_incremental_decision_procedure.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,11 @@ class smt2_incremental_decision_proceduret final
102102
/// \note This function is non-const because it mutates the object_map.
103103
smt_termt convert_expr_to_smt(const exprt &expr);
104104
void define_index_identifiers(const exprt &expr);
105+
/// In the case where lowering passes insert instances of the anonymous
106+
/// `nondet_padding_exprt`, these need functions declaring for each instance.
107+
/// These instances are then substituted for the function identifier in order
108+
/// to free the solver to choose a non-det value.
109+
exprt substitute_defined_padding(exprt expr);
105110
/// Sends the solver the definitions of the object sizes and dynamic memory
106111
/// statuses.
107112
void define_object_properties();
@@ -135,7 +140,7 @@ class smt2_incremental_decision_proceduret final
135140
{
136141
return next_id++;
137142
}
138-
} handle_sequence, array_sequence, index_sequence;
143+
} handle_sequence, array_sequence, index_sequence, padding_sequence;
139144
/// When the `handle(exprt)` member function is called, the decision procedure
140145
/// commands the SMT solver to define a new function corresponding to the
141146
/// given expression. The mapping of the expressions to the function

unit/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ SRC += analyses/ai/ai.cpp \
117117
solvers/smt2_incremental/smt_to_smt2_string.cpp \
118118
solvers/smt2_incremental/encoding/struct_encoding.cpp \
119119
solvers/smt2_incremental/encoding/enum_encoding.cpp \
120+
solvers/smt2_incremental/encoding/nondet_padding.cpp \
120121
solvers/smt2_incremental/theories/smt_array_theory.cpp \
121122
solvers/smt2_incremental/theories/smt_bit_vector_theory.cpp \
122123
solvers/smt2_incremental/theories/smt_core_theory.cpp \
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Author: Diffblue Ltd.
2+
3+
#include <util/expr_cast.h>
4+
#include <util/std_expr.h>
5+
6+
#include <solvers/smt2_incremental/encoding/nondet_padding.h>
7+
#include <testing-utils/invariant.h>
8+
#include <testing-utils/use_catch.h>
9+
10+
TEST_CASE("Nondet padding expression", "[core][smt2_incremental]")
11+
{
12+
const bv_typet type{8};
13+
const exprt padding = nondet_padding_exprt{type};
14+
SECTION("Valid usage")
15+
{
16+
REQUIRE(padding.type() == type);
17+
REQUIRE(nullptr != expr_try_dynamic_cast<nondet_padding_exprt>(padding));
18+
}
19+
const auto type_error = invariant_failure_containing(
20+
"Nondet padding is expected to pad a bit vector type.");
21+
SECTION("Failed downcasts")
22+
{
23+
const exprt not_padding = symbol_exprt{"foo", empty_typet{}};
24+
REQUIRE(
25+
nullptr == expr_try_dynamic_cast<nondet_padding_exprt>(not_padding));
26+
const exprt wrong_type = [&]() {
27+
exprt padding = nondet_padding_exprt{type};
28+
padding.type() = empty_typet{};
29+
return padding;
30+
}();
31+
cbmc_invariants_should_throwt invariants_throw;
32+
CHECK_THROWS_MATCHES(
33+
expr_checked_cast<nondet_padding_exprt>(wrong_type),
34+
invariant_failedt,
35+
type_error);
36+
}
37+
SECTION("Construction with incorrect type")
38+
{
39+
cbmc_invariants_should_throwt invariants_throw;
40+
CHECK_THROWS_MATCHES(
41+
nondet_padding_exprt{empty_typet{}}, invariant_failedt, type_error);
42+
}
43+
}

unit/solvers/smt2_incremental/encoding/struct_encoding.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <util/namespace.h>
99
#include <util/symbol_table.h>
1010

11+
#include <solvers/smt2_incremental/encoding/nondet_padding.h>
1112
#include <solvers/smt2_incremental/encoding/struct_encoding.h>
1213
#include <testing-utils/use_catch.h>
1314

@@ -453,3 +454,39 @@ TEST_CASE(
453454
REQUIRE(test.struct_encoding.encode(struct_expr) == dozen);
454455
}
455456
}
457+
458+
TEST_CASE("encoding of union expressions", "[core][smt2_incremental]")
459+
{
460+
const struct_union_typet::componentst components{
461+
{"eggs", unsignedbv_typet{8}}, {"ham", signedbv_typet{32}}};
462+
const type_symbolt type_symbol{"foot", union_typet{components}, ID_C};
463+
auto test = struct_encoding_test_environmentt::make();
464+
test.symbol_table.insert(type_symbol);
465+
const union_tag_typet union_tag{type_symbol.name};
466+
const symbolt union_value_symbol{"foo", union_tag, ID_C};
467+
test.symbol_table.insert(union_value_symbol);
468+
const auto symbol_expr = union_value_symbol.symbol_expr();
469+
const auto symbol_expr_as_bv = symbol_exprt{"foo", bv_typet{32}};
470+
const auto dozen = from_integer(12, unsignedbv_typet{8});
471+
const auto partial_union = union_exprt{"eggs", dozen, union_tag};
472+
const auto partial_union_as_bv = concatenation_exprt{
473+
{nondet_padding_exprt{bv_typet{24}}, dozen}, bv_typet{32}};
474+
SECTION("Union typed symbol expression")
475+
{
476+
REQUIRE(test.struct_encoding.encode(symbol_expr) == symbol_expr_as_bv);
477+
}
478+
SECTION("Expression for a union from a component")
479+
{
480+
REQUIRE(test.struct_encoding.encode(partial_union) == partial_union_as_bv);
481+
const auto one = from_integer(1, unsignedbv_typet{32});
482+
const auto full_union = union_exprt{"ham", one, union_tag};
483+
const auto full_union_as_bv = typecast_exprt{one, bv_typet{32}};
484+
REQUIRE(test.struct_encoding.encode(full_union) == full_union_as_bv);
485+
}
486+
SECTION("Union equality expression")
487+
{
488+
const auto union_equal = equal_exprt{symbol_expr, partial_union};
489+
const auto bv_equal = equal_exprt{symbol_expr_as_bv, partial_union_as_bv};
490+
REQUIRE(test.struct_encoding.encode(union_equal) == bv_equal);
491+
}
492+
}

unit/solvers/smt2_incremental/smt2_incremental_decision_procedure.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Author: Diffblue Ltd.
22

33
#include <util/arith_tools.h>
4+
#include <util/bitvector_expr.h>
45
#include <util/bitvector_types.h>
56
#include <util/config.h>
67
#include <util/exception_utils.h>
@@ -13,6 +14,7 @@
1314
#include <solvers/smt2_incremental/ast/smt_responses.h>
1415
#include <solvers/smt2_incremental/ast/smt_sorts.h>
1516
#include <solvers/smt2_incremental/ast/smt_terms.h>
17+
#include <solvers/smt2_incremental/encoding/nondet_padding.h>
1618
#include <solvers/smt2_incremental/smt2_incremental_decision_procedure.h>
1719
#include <solvers/smt2_incremental/smt_solver_process.h>
1820
#include <solvers/smt2_incremental/theories/smt_array_theory.h>
@@ -268,6 +270,28 @@ TEST_CASE(
268270
smt_identifier_termt{"third_comparison", smt_bool_sortt{}})},
269271
smt_assert_commandt{comparison_conjunction_term}});
270272
}
273+
SECTION("Set with nondet_padding")
274+
{
275+
const exprt to_set = equal_exprt{
276+
concatenation_exprt{
277+
{nondet_padding_exprt{bv_typet{8}}, from_integer(42, bv_typet{8})},
278+
bv_typet{16}},
279+
from_integer(42, bv_typet{16})};
280+
test.sent_commands.clear();
281+
test.procedure.set_to(to_set, true);
282+
// (declare-fun padding_0 () (_ BitVec 8))
283+
// (assert (= (concat padding_0 (_ bv42 8)) (_ bv42 16)))
284+
const auto expected_padding_term =
285+
smt_identifier_termt{"padding_0", smt_bit_vector_sortt{8}};
286+
REQUIRE(
287+
test.sent_commands ==
288+
std::vector<smt_commandt>{
289+
smt_declare_function_commandt{expected_padding_term, {}},
290+
smt_assert_commandt{smt_core_theoryt::equal(
291+
smt_bit_vector_theoryt::concat(
292+
expected_padding_term, smt_bit_vector_constant_termt{42, 8}),
293+
smt_bit_vector_constant_termt{42, 16})}});
294+
}
271295
SECTION("Handle of value-less symbol.")
272296
{
273297
const symbolt foo = make_test_symbol("foo", bool_typet{});
@@ -820,6 +844,43 @@ TEST_CASE(
820844
REQUIRE(test.sent_commands == expected_commands);
821845
}
822846

847+
TEST_CASE(
848+
"smt2_incremental_decision_proceduret union support.",
849+
"[core][smt2_incremental]")
850+
{
851+
auto test = decision_procedure_test_environmentt::make();
852+
// union foot{ int8_t eggs, uint32_t ham } foo;
853+
const struct_union_typet::componentst components{
854+
{"eggs", unsignedbv_typet{8}}, {"ham", signedbv_typet{32}}};
855+
const type_symbolt type_symbol{"foot", union_typet{components}, ID_C};
856+
test.symbol_table.insert(type_symbol);
857+
const union_tag_typet union_tag{type_symbol.name};
858+
const symbolt union_value_symbol{"foo", union_tag, ID_C};
859+
test.symbol_table.insert(union_value_symbol);
860+
// assume(foo == foot{ .eggs = 12 });
861+
const auto symbol_expr = union_value_symbol.symbol_expr();
862+
const auto dozen = from_integer(12, unsignedbv_typet{8});
863+
const auto union_needing_padding = union_exprt{"eggs", dozen, union_tag};
864+
const auto equals = equal_exprt{symbol_expr, union_needing_padding};
865+
test.sent_commands.clear();
866+
test.procedure.set_to(equals, true);
867+
868+
// (declare-fun foo () (_ BitVec 32))
869+
// (declare-fun padding_0 () (_ BitVec 24))
870+
// (assert (= foo (concat padding_0 (_ bv12 8)))) }
871+
const auto foo_term = smt_identifier_termt{"foo", smt_bit_vector_sortt{32}};
872+
const auto padding_term =
873+
smt_identifier_termt{"padding_0", smt_bit_vector_sortt{24}};
874+
const std::vector<smt_commandt> expected_commands{
875+
smt_declare_function_commandt{foo_term, {}},
876+
smt_declare_function_commandt{padding_term, {}},
877+
smt_assert_commandt{smt_core_theoryt::equal(
878+
foo_term,
879+
smt_bit_vector_theoryt::concat(
880+
padding_term, smt_bit_vector_constant_termt{12, 8}))}};
881+
REQUIRE(test.sent_commands == expected_commands);
882+
}
883+
823884
TEST_CASE(
824885
"smt2_incremental_decision_proceduret byte update-extract commands.",
825886
"[core][smt2_incremental]")

0 commit comments

Comments
 (0)