Skip to content

Commit eaa2cdd

Browse files
authoredNov 1, 2023
Merge pull request #7990 from thomasspriggs/tas/smt_union_traces
Add support for union values in traces for incremental smt2 decision procedure
2 parents 879e86a + 8d8dfa9 commit eaa2cdd

File tree

8 files changed

+135
-2
lines changed

8 files changed

+135
-2
lines changed
 
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#include <assert.h>
2+
#include <stdint.h>
3+
4+
union my_uniont
5+
{
6+
uint16_t a;
7+
uint8_t b;
8+
};
9+
10+
int main()
11+
{
12+
union my_uniont my_union = {.b = 5};
13+
assert(my_union.a == 5);
14+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
padded.c
3+
--trace
4+
Passing problem to incremental SMT2 solving
5+
\[main\.assertion\.1\] line 13 assertion my_union\.a \=\= 5\: FAILURE
6+
my_union\=\{ \.a\=\d+ \} \(\d{8} 00000101\)
7+
^EXIT=10$
8+
^SIGNAL=0$
9+
--
10+
--
11+
Test that we support union values and traces for a example where the trace
12+
should include non deterministic padding.

‎src/solvers/smt2_incremental/encoding/struct_encoding.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,3 +277,30 @@ exprt struct_encodingt::decode(
277277
}
278278
return simplify_expr(struct_exprt{encoded_fields, original_type}, ns);
279279
}
280+
281+
exprt struct_encodingt::decode(
282+
const exprt &encoded,
283+
const union_tag_typet &original_type) const
284+
{
285+
INVARIANT(
286+
can_cast_type<bv_typet>(encoded.type()),
287+
"Unions are expected to be encoded into bit vectors.");
288+
const union_typet definition = ns.get().follow_tag(original_type);
289+
const auto &components = definition.components();
290+
if(components.empty())
291+
return empty_union_exprt{original_type};
292+
// A union expression is built here using the first component in the
293+
// declaration of the union. There may be better alternatives but this matches
294+
// the SAT backend. See the case for `type.id() == ID_union` in
295+
// `boolbvt::bv_get_rec`.
296+
const auto &component_for_definition = components[0];
297+
return simplify_expr(
298+
union_exprt{
299+
component_for_definition.get_name(),
300+
typecast_exprt{
301+
encode(member_exprt{
302+
typecast_exprt{encoded, original_type}, component_for_definition}),
303+
component_for_definition.type()},
304+
original_type},
305+
ns);
306+
}

‎src/solvers/smt2_incremental/encoding/struct_encoding.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ class namespacet;
1212
class boolbv_widtht;
1313
class member_exprt;
1414
class struct_tag_typet;
15+
class union_tag_typet;
1516

1617
/// Encodes struct types/values into non-struct expressions/types.
1718
class struct_encodingt final
@@ -27,6 +28,8 @@ class struct_encodingt final
2728
/// from the bit vector \p encoded expression.
2829
exprt
2930
decode(const exprt &encoded, const struct_tag_typet &original_type) const;
31+
exprt
32+
decode(const exprt &encoded, const union_tag_typet &original_type) const;
3033

3134
private:
3235
std::unique_ptr<boolbv_widtht> boolbv_width;

‎src/solvers/smt2_incremental/smt2_incremental_decision_procedure.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
#include <util/arith_tools.h>
66
#include <util/byte_operators.h>
7+
#include <util/c_types.h>
78
#include <util/namespace.h>
89
#include <util/nodiscard.h>
910
#include <util/range.h>
@@ -478,6 +479,17 @@ optionalt<exprt> smt2_incremental_decision_proceduret::get_expr(
478479
return {struct_encoding.decode(*encoded_result, type)};
479480
}
480481

482+
optionalt<exprt> smt2_incremental_decision_proceduret::get_expr(
483+
const smt_termt &union_term,
484+
const union_tag_typet &type) const
485+
{
486+
const auto encoded_result =
487+
get_expr(union_term, struct_encoding.encode(type));
488+
if(!encoded_result)
489+
return {};
490+
return {struct_encoding.decode(*encoded_result, type)};
491+
}
492+
481493
optionalt<exprt> smt2_incremental_decision_proceduret::get_expr(
482494
const smt_termt &descriptor,
483495
const typet &type) const
@@ -492,6 +504,10 @@ optionalt<exprt> smt2_incremental_decision_proceduret::get_expr(
492504
{
493505
return get_expr(descriptor, *struct_type);
494506
}
507+
if(const auto union_type = type_try_dynamic_cast<union_tag_typet>(type))
508+
{
509+
return get_expr(descriptor, *union_type);
510+
}
495511
const smt_get_value_commandt get_value_command{descriptor};
496512
const smt_responset response = get_response_to_command(
497513
*solver_process, get_value_command, identifier_table);

‎src/solvers/smt2_incremental/smt2_incremental_decision_procedure.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
class namespacet;
2424
class smt_base_solver_processt; // IWYU pragma: keep
2525
class string_constantt;
26+
class union_tag_typet;
2627

2728
class smt2_incremental_decision_proceduret final
2829
: public stack_decision_proceduret
@@ -60,6 +61,8 @@ class smt2_incremental_decision_proceduret final
6061
optionalt<exprt>
6162
get_expr(const smt_termt &struct_term, const struct_tag_typet &type) const;
6263
optionalt<exprt>
64+
get_expr(const smt_termt &union_term, const union_tag_typet &type) const;
65+
optionalt<exprt>
6366
get_expr(const smt_termt &array, const array_typet &type) const;
6467

6568
protected:

‎unit/solvers/smt2_incremental/encoding/struct_encoding.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,50 @@ TEST_CASE("decoding into struct expressions.", "[core][smt2_incremental]")
418418
REQUIRE(test.struct_encoding.decode(encoded, struct_tag) == expected);
419419
}
420420

421+
TEST_CASE("decoding into union expressions.", "[core][smt2_incremental]")
422+
{
423+
auto test = struct_encoding_test_environmentt::make();
424+
SECTION("Single union component")
425+
{
426+
const struct_union_typet::componentst type_components{
427+
{"eggs", unsignedbv_typet{16}}};
428+
const union_typet union_type{type_components};
429+
const type_symbolt type_symbol{"my_uniont", union_type, ID_C};
430+
test.symbol_table.insert(type_symbol);
431+
const union_tag_typet union_tag{type_symbol.name};
432+
const union_exprt expected{
433+
"eggs", from_integer(12, unsignedbv_typet{16}), union_tag};
434+
const exprt encoded = from_integer(12, bv_typet{16});
435+
REQUIRE(test.struct_encoding.decode(encoded, union_tag) == expected);
436+
}
437+
SECTION("Multiple union components")
438+
{
439+
const struct_union_typet::componentst type_components{
440+
{"green", signedbv_typet{32}},
441+
{"eggs", unsignedbv_typet{16}},
442+
{"ham", signedbv_typet{24}}};
443+
const union_typet union_type{type_components};
444+
const type_symbolt type_symbol{"my_uniont", union_type, ID_C};
445+
test.symbol_table.insert(type_symbol);
446+
const union_tag_typet union_tag{type_symbol.name};
447+
const union_exprt expected{
448+
"green", from_integer(-42, signedbv_typet{32}), union_tag};
449+
const exprt encoded = from_integer((~uint32_t{42} + 1), bv_typet{32});
450+
REQUIRE(test.struct_encoding.decode(encoded, union_tag) == expected);
451+
}
452+
SECTION("Empty union")
453+
{
454+
const struct_union_typet::componentst type_components{};
455+
const union_typet union_type{type_components};
456+
const type_symbolt type_symbol{"my_empty_uniont", union_type, ID_C};
457+
test.symbol_table.insert(type_symbol);
458+
const union_tag_typet union_tag{type_symbol.name};
459+
const empty_union_exprt expected{union_tag};
460+
const exprt encoded = from_integer(0, bv_typet{8});
461+
REQUIRE(test.struct_encoding.decode(encoded, union_tag) == expected);
462+
}
463+
}
464+
421465
TEST_CASE("encoding of struct with no members", "[core][smt2_incremental]")
422466
{
423467
auto test = struct_encoding_test_environmentt::make();

‎unit/solvers/smt2_incremental/smt2_incremental_decision_procedure.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -871,14 +871,28 @@ TEST_CASE(
871871
const auto foo_term = smt_identifier_termt{"foo", smt_bit_vector_sortt{32}};
872872
const auto padding_term =
873873
smt_identifier_termt{"padding_0", smt_bit_vector_sortt{24}};
874-
const std::vector<smt_commandt> expected_commands{
874+
const std::vector<smt_commandt> expected_set_commands{
875875
smt_declare_function_commandt{foo_term, {}},
876876
smt_declare_function_commandt{padding_term, {}},
877877
smt_assert_commandt{smt_core_theoryt::equal(
878878
foo_term,
879879
smt_bit_vector_theoryt::concat(
880880
padding_term, smt_bit_vector_constant_termt{12, 8}))}};
881-
REQUIRE(test.sent_commands == expected_commands);
881+
REQUIRE(test.sent_commands == expected_set_commands);
882+
883+
INFO("Ensuring decision procedure in suitable state for getting values.");
884+
test.mock_responses.push_back(smt_check_sat_responset{smt_sat_responset{}});
885+
test.procedure();
886+
887+
INFO("Getting union typed value.");
888+
test.sent_commands.clear();
889+
test.mock_responses.push_back(smt_get_value_responset{
890+
{{foo_term, smt_bit_vector_constant_termt{~uint32_t{255} | 12, 32}}}});
891+
const auto expected_value = union_exprt{"eggs", dozen, union_tag};
892+
REQUIRE(test.procedure.get(symbol_expr) == expected_value);
893+
const std::vector<smt_commandt> expected_get_commands{
894+
smt_get_value_commandt{foo_term}};
895+
REQUIRE(test.sent_commands == expected_get_commands);
882896
}
883897

884898
TEST_CASE(

0 commit comments

Comments
 (0)
Please sign in to comment.