Skip to content

Commit bcf6f4f

Browse files
Merge pull request #7839 from thomasspriggs/tas/smt_empty_structs
Add empty struct support to incremental SMT decision procedure
2 parents a82e456 + ee6337f commit bcf6f4f

File tree

11 files changed

+114
-15
lines changed

11 files changed

+114
-15
lines changed

regression/cbmc/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ add_test_pl_profile(
3434
add_test_pl_profile(
3535
"cbmc-new-smt-backend"
3636
"$<TARGET_FILE:cbmc> --incremental-smt2-solver 'z3 --smt2 -in'"
37-
"-I;new-smt-backend;-s;new-smt-backend"
37+
"${gcc_only_string}-I;new-smt-backend;-s;new-smt-backend"
3838
"CORE"
3939
)
4040

regression/cbmc/Empty_struct1/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
CORE gcc-only
1+
CORE gcc-only new-smt-backend
22
main.c
33

44
^EXIT=0$

regression/cbmc/Empty_struct2/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
CORE gcc-only
1+
CORE gcc-only new-smt-backend
22
main.c
33

44
^EXIT=0$

regression/cbmc/Empty_struct3/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
CORE gcc-only
1+
CORE gcc-only new-smt-backend
22
main.c
33
--json-ui
44
VERIFICATION FAILED

regression/cbmc/empty_compound_type1/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
CORE gcc-only
1+
CORE gcc-only new-smt-backend
22
main.c
33

44
^EXIT=0$

regression/cbmc/empty_compound_type2/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
CORE gcc-only
1+
CORE gcc-only new-smt-backend
22
main.c
33
--pointer-check
44
^EXIT=10$

regression/cbmc/empty_compound_type3/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
CORE gcc-only
1+
CORE gcc-only new-smt-backend
22
main.c
33

44
^EXIT=10$

src/solvers/smt2_incremental/encoding/struct_encoding.cpp

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include "struct_encoding.h"
44

5+
#include <util/arith_tools.h>
56
#include <util/bitvector_expr.h>
67
#include <util/bitvector_types.h>
78
#include <util/make_unique.h>
@@ -38,9 +39,14 @@ typet struct_encodingt::encode(typet type) const
3839
work_queue.pop();
3940
if(const auto struct_tag = type_try_dynamic_cast<struct_tag_typet>(current))
4041
{
41-
const auto width = (*boolbv_width)(*struct_tag);
42+
auto width = (*boolbv_width)(*struct_tag);
43+
// The bit vector theory of SMT disallows zero bit length length bit
44+
// vectors. C++ gives a minimum size for a struct (even an empty struct)
45+
// as being one byte; in order to ensure that structs have unique memory
46+
// locations. Therefore encoding empty structs as having 8 bits / 1 byte
47+
// is a reasonable solution in this case.
4248
if(width == 0)
43-
UNIMPLEMENTED_FEATURE("support for zero bit width structs.");
49+
width = 8;
4450
current = bv_typet{width};
4551
}
4652
if(const auto array = type_try_dynamic_cast<array_typet>(current))
@@ -102,11 +108,16 @@ static exprt encode(const with_exprt &with, const namespacet &ns)
102108
return struct_exprt{components, tag_type};
103109
}
104110

111+
/// Non-empty structs are flattened into a large bit vector using concatenation
112+
/// to express all the member operands of \p struct_expr. Empty structs are
113+
/// encoded as a zero byte. This has useful properties such as -
114+
/// * A zero byte is valid SMT, unlike zero length bit vectors.
115+
/// * Any two zero byte instances are always equal. This property would not
116+
/// be true of two instances of a non-det byte for instance.
105117
static exprt encode(const struct_exprt &struct_expr)
106118
{
107-
INVARIANT(
108-
!struct_expr.operands().empty(),
109-
"empty structs may not be encoded into SMT terms.");
119+
if(struct_expr.operands().empty())
120+
return from_integer(0, bv_typet{8});
110121
if(struct_expr.operands().size() == 1)
111122
return struct_expr.operands().front();
112123
return concatenation_exprt{struct_expr.operands(), struct_expr.type()};

src/solvers/smt2_incremental/smt2_incremental_decision_procedure.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -497,14 +497,15 @@ exprt smt2_incremental_decision_proceduret::get(const exprt &expr) const
497497
{
498498
return identifier_descriptor;
499499
}
500-
if(gather_dependent_expressions(expr).empty())
500+
const exprt lowered = lower(expr);
501+
if(gather_dependent_expressions(lowered).empty())
501502
{
502503
INVARIANT(
503-
objects_are_already_tracked(expr, object_map),
504+
objects_are_already_tracked(lowered, object_map),
504505
"Objects in expressions being read should already be tracked from "
505506
"point of being set/handled.");
506507
return ::convert_expr_to_smt(
507-
expr,
508+
lowered,
508509
object_map,
509510
pointer_sizes_map,
510511
object_size_function.make_application,

unit/solvers/smt2_incremental/encoding/struct_encoding.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,34 @@ TEST_CASE("decoding into struct expressions.", "[core][smt2_incremental]")
341341
REQUIRE(test.struct_encoding.decode(encoded, struct_tag) == expected);
342342
}
343343

344+
TEST_CASE("encoding of struct with no members", "[core][smt2_incremental]")
345+
{
346+
auto test = struct_encoding_test_environmentt::make();
347+
const struct_union_typet::componentst component_types{};
348+
const type_symbolt type_symbol{"emptyt", struct_typet{component_types}, ID_C};
349+
test.symbol_table.insert(type_symbol);
350+
const struct_tag_typet type_reference{type_symbol.name};
351+
const auto expected_type = bv_typet{8};
352+
const auto expected_zero_byte = from_integer(0, expected_type);
353+
SECTION("Type of empty struct")
354+
{
355+
REQUIRE(test.struct_encoding.encode(type_reference) == expected_type);
356+
}
357+
SECTION("Empty struct typed symbol expression")
358+
{
359+
const symbolt empty_value_symbol{"foo", type_reference, ID_C};
360+
test.symbol_table.insert(empty_value_symbol);
361+
const auto symbol_expr = empty_value_symbol.symbol_expr();
362+
const exprt expected_symbol = symbol_exprt{"foo", expected_type};
363+
REQUIRE(test.struct_encoding.encode(symbol_expr) == expected_symbol);
364+
}
365+
SECTION("Struct expression")
366+
{
367+
const struct_exprt empty{{}, type_reference};
368+
REQUIRE(test.struct_encoding.encode(empty) == expected_zero_byte);
369+
}
370+
}
371+
344372
TEST_CASE(
345373
"encoding of single member struct expressions",
346374
"[core][smt2_incremental]")

0 commit comments

Comments
 (0)