Skip to content

Commit 5d80835

Browse files
author
Enrico Steffinlongo
authored
Merge pull request #7755 from esteffin/esteffin/incr-smt2-c-enum-tag
Incremental SMT2 support for enum values
2 parents 95f5db1 + b0087bd commit 5d80835

26 files changed

+398
-30
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#include <assert.h>
2+
3+
enum E
4+
{
5+
V0 = 0,
6+
V1 = 1,
7+
V2 = 2
8+
};
9+
10+
int main()
11+
{
12+
unsigned i;
13+
__CPROVER_assume(i < 3);
14+
enum E e[3];
15+
e[0] = V0;
16+
e[1] = V1;
17+
e[2] = V2;
18+
__CPROVER_assert(e[i] > 0, "Array at index 0 is V0, so this should fail");
19+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
CORE
2+
enum_in_array.c
3+
--trace
4+
Passing problem to incremental SMT2 solving
5+
line 18 Array at index 0 is V0, so this should fail: FAILURE
6+
i=0u \(00000000 00000000 00000000 00000000\)
7+
e\[0l+\]=/\*enum\*/V0 \(00000000 00000000 00000000 00000000\)
8+
e\[1l+\]=/\*enum\*/V1 \(00000000 00000000 00000000 00000001\)
9+
e\[2l+\]=/\*enum\*/V2 \(00000000 00000000 00000000 00000010\)
10+
^EXIT=10$
11+
^SIGNAL=0$
12+
--
13+
--
14+
Test that we support enum values and traces for an array of enums. At the time of adding the
15+
regression test, this exercised the conversion code specific to c_enum_tag_typet.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#include <assert.h>
2+
3+
enum E
4+
{
5+
V0 = 0,
6+
V1 = 1,
7+
V2 = 2
8+
};
9+
10+
struct S
11+
{
12+
enum E a;
13+
int b;
14+
};
15+
16+
int main()
17+
{
18+
struct S s;
19+
s.a = V1;
20+
__CPROVER_assume(__CPROVER_enum_is_in_range(s.a));
21+
__CPROVER_assert(s.a > 10, "Struct field s.a is V1, so this should fail");
22+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
enum_in_struct.c
3+
--trace
4+
Passing problem to incremental SMT2 solving
5+
line 21 Struct field s\.a is V1, so this should fail: FAILURE
6+
s\.a=/\*enum\*/V1 \(00000000 00000000 00000000 00000001\)
7+
^EXIT=10$
8+
^SIGNAL=0$
9+
--
10+
--
11+
Test that we support enum values and traces for an struct with enums fields. At the time of adding
12+
the regression test, this exercised the conversion code specific to c_enum_tag_typet.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#include <assert.h>
2+
3+
enum E
4+
{
5+
V0 = 0,
6+
V1 = 1,
7+
V2 = 2
8+
};
9+
10+
int main()
11+
{
12+
enum E e;
13+
e = V1;
14+
__CPROVER_assume(__CPROVER_enum_is_in_range(e));
15+
__CPROVER_assert(e > 10, "Variable e is V1, so this should fail");
16+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CORE
2+
simple.c
3+
--trace
4+
Passing problem to incremental SMT2 solving
5+
line 15 Variable e is V1, so this should fail: FAILURE
6+
e=/\*enum\*/V0 \(00000000 00000000 00000000 00000000\)
7+
e=/\*enum\*/V1 \(00000000 00000000 00000000 00000001\)
8+
^EXIT=10$
9+
^SIGNAL=0$
10+
--
11+
--
12+
Test that we support enum values and traces for a trivial example. At the
13+
time of adding the regression test, this exercised the conversion code specific
14+
to c_enum_tag_typet.

regression/cbmc/enum-trace1/test_json.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
CORE
1+
CORE new-smt-backend
22
main.c
33
--json-ui --function test --trace
44
activate-multi-line-match

regression/cbmc/enum-trace1/test_xml.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
CORE
1+
CORE new-smt-backend
22
main.c
33
--xml-ui --function test --trace
44
activate-multi-line-match

regression/cbmc/enum3/test.desc

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

44
^EXIT=0$

regression/cbmc/enum_is_in_range/enum_test4.desc

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

44
^EXIT=10$

regression/cbmc/enum_is_in_range/enum_test8.desc

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

44
^EXIT=10$

src/solvers/Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,8 @@ 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 \
213+
smt2_incremental/encoding/struct_encoding.cpp \
214+
smt2_incremental/encoding/enum_encoding.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 \

src/solvers/smt2_incremental/construct_value_expr_from_smt.cpp

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

33
#include <util/arith_tools.h>
4+
#include <util/c_types.h>
5+
#include <util/namespace.h>
46
#include <util/pointer_expr.h>
57
#include <util/std_expr.h>
68
#include <util/std_types.h>
@@ -13,10 +15,13 @@ class value_expr_from_smt_factoryt : public smt_term_const_downcast_visitort
1315
{
1416
private:
1517
const typet &type_to_construct;
18+
const namespacet &ns;
1619
optionalt<exprt> result;
1720

18-
explicit value_expr_from_smt_factoryt(const typet &type_to_construct)
19-
: type_to_construct{type_to_construct}, result{}
21+
explicit value_expr_from_smt_factoryt(
22+
const typet &type_to_construct,
23+
const namespacet &ns)
24+
: type_to_construct{type_to_construct}, ns{ns}, result{}
2025
{
2126
}
2227

@@ -70,6 +75,20 @@ class value_expr_from_smt_factoryt : public smt_term_const_downcast_visitort
7075
result = from_integer(bit_vector_constant.value(), type_to_construct);
7176
return;
7277
}
78+
if(
79+
const auto c_enum_tag_type =
80+
type_try_dynamic_cast<c_enum_tag_typet>(type_to_construct))
81+
{
82+
const c_enum_typet &real_type = ns.follow_tag(*c_enum_tag_type);
83+
INVARIANT(
84+
to_bitvector_type(real_type.underlying_type()).get_width() ==
85+
sort_width,
86+
"Width of smt bit vector term must match the width of bit vector "
87+
"underlying type of the original c_enum type.");
88+
result = from_integer(bit_vector_constant.value(), real_type);
89+
result->type() = type_to_construct;
90+
return;
91+
}
7392

7493
INVARIANT(
7594
false,
@@ -102,9 +121,12 @@ class value_expr_from_smt_factoryt : public smt_term_const_downcast_visitort
102121
/// \brief This function is complete the external interface to this class. All
103122
/// construction of this class and construction of expressions should be
104123
/// carried out via this function.
105-
static exprt make(const smt_termt &value_term, const typet &type_to_construct)
124+
static exprt make(
125+
const smt_termt &value_term,
126+
const typet &type_to_construct,
127+
const namespacet &ns)
106128
{
107-
value_expr_from_smt_factoryt factory{type_to_construct};
129+
value_expr_from_smt_factoryt factory{type_to_construct, ns};
108130
value_term.accept(factory);
109131
INVARIANT(factory.result.has_value(), "Factory must result in expr value.");
110132
return *factory.result;
@@ -113,7 +135,8 @@ class value_expr_from_smt_factoryt : public smt_term_const_downcast_visitort
113135

114136
exprt construct_value_expr_from_smt(
115137
const smt_termt &value_term,
116-
const typet &type_to_construct)
138+
const typet &type_to_construct,
139+
const namespacet &ns)
117140
{
118-
return value_expr_from_smt_factoryt::make(value_term, type_to_construct);
141+
return value_expr_from_smt_factoryt::make(value_term, type_to_construct, ns);
119142
}

src/solvers/smt2_incremental/construct_value_expr_from_smt.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ class typet;
1818
/// \param type_to_construct
1919
/// The type which the constructed expr returned is expected to have. This
2020
/// type must be compatible with the sort of \p value_term.
21+
/// \param ns
22+
/// The namespace to resolve `c_enum_tag_type` to reconstruct the correct
23+
/// type of enum values in the trace.
2124
/// \note The type is required separately in order to carry out this conversion,
2225
/// because the smt value term does not contain all the required information.
2326
/// For example an 8 bit, bit vector with a value of 255 could be used to
@@ -26,6 +29,7 @@ class typet;
2629
/// using the type.
2730
exprt construct_value_expr_from_smt(
2831
const smt_termt &value_term,
29-
const typet &type_to_construct);
32+
const typet &type_to_construct,
33+
const namespacet &ns);
3034

3135
#endif // CPROVER_SOLVERS_SMT2_INCREMENTAL_CONSTRUCT_VALUE_EXPR_FROM_SMT_H
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Author: Diffblue Ltd.
2+
3+
#include "enum_encoding.h"
4+
5+
#include <util/c_types.h>
6+
#include <util/expr_cast.h>
7+
#include <util/namespace.h>
8+
9+
#include <queue>
10+
11+
// Internal function to lower inner types of compound types (at the moment only
12+
// arrays)
13+
static typet encode(typet type, const namespacet &ns)
14+
{
15+
std::queue<typet *> work_queue;
16+
work_queue.push(&type);
17+
while(!work_queue.empty())
18+
{
19+
typet &current = *work_queue.front();
20+
work_queue.pop();
21+
if(const auto c_enum_tag = type_try_dynamic_cast<c_enum_tag_typet>(current))
22+
{
23+
// Replace the type of the c_enum_tag with the underlying type of the enum
24+
// it points to
25+
current = ns.follow_tag(*c_enum_tag).underlying_type();
26+
}
27+
if(const auto array = type_try_dynamic_cast<array_typet>(current))
28+
{
29+
// Add the type of the array's elements to the queue to be explored
30+
work_queue.push(&array->element_type());
31+
}
32+
}
33+
return type;
34+
}
35+
36+
// Worklist algorithm to lower enum types of expr and its sub-expressions.
37+
exprt lower_enum(exprt expr, const namespacet &ns)
38+
{
39+
std::queue<exprt *> work_queue;
40+
work_queue.push(&expr);
41+
while(!work_queue.empty())
42+
{
43+
exprt &current = *work_queue.front();
44+
work_queue.pop();
45+
46+
// Replace the type of the expression node with the encoded one
47+
current.type() = encode(current.type(), ns);
48+
49+
for(auto &operand : current.operands())
50+
work_queue.push(&operand);
51+
}
52+
return expr;
53+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Author: Diffblue Ltd.
2+
3+
#ifndef CPROVER_SOLVERS_SMT2_INCREMENTAL_ENCODING_ENUM_ENCODING_H
4+
#define CPROVER_SOLVERS_SMT2_INCREMENTAL_ENCODING_ENUM_ENCODING_H
5+
6+
#include <util/expr.h>
7+
8+
/// Function to lower `expr` and its sub-expressions containing enum types.
9+
/// Specifically it replaces the node `c_enum_tag_typet` type with the
10+
/// underlying type of the enum the tag points to.
11+
/// @param expr the expression to lower.
12+
/// @param ns the namespace where to lookup `c_enum_tag_type`s.
13+
/// @return the lowered expression.
14+
exprt lower_enum(exprt expr, const namespacet &ns);
15+
16+
#endif // CPROVER_SOLVERS_SMT2_INCREMENTAL_ENCODING_ENUM_ENCODING_H
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
util

src/solvers/smt2_incremental/smt2_incremental_decision_procedure.cpp

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#include <solvers/smt2_incremental/ast/smt_terms.h>
1717
#include <solvers/smt2_incremental/construct_value_expr_from_smt.h>
1818
#include <solvers/smt2_incremental/convert_expr_to_smt.h>
19+
#include <solvers/smt2_incremental/encoding/enum_encoding.h>
1920
#include <solvers/smt2_incremental/smt_solver_process.h>
2021
#include <solvers/smt2_incremental/theories/smt_array_theory.h>
2122
#include <solvers/smt2_incremental/theories/smt_core_theory.h>
@@ -347,12 +348,15 @@ static optionalt<smt_termt> get_identifier(
347348
const std::unordered_map<exprt, smt_identifier_termt, irep_hash>
348349
&expression_handle_identifiers,
349350
const std::unordered_map<exprt, smt_identifier_termt, irep_hash>
350-
&expression_identifiers)
351+
&expression_identifiers,
352+
const namespacet &ns)
351353
{
352-
const auto handle_find_result = expression_handle_identifiers.find(expr);
354+
const exprt lowered_expr = lower_enum(expr, ns);
355+
const auto handle_find_result =
356+
expression_handle_identifiers.find(lowered_expr);
353357
if(handle_find_result != expression_handle_identifiers.cend())
354358
return handle_find_result->second;
355-
const auto expr_find_result = expression_identifiers.find(expr);
359+
const auto expr_find_result = expression_identifiers.find(lowered_expr);
356360
if(expr_find_result != expression_identifiers.cend())
357361
return expr_find_result->second;
358362
return {};
@@ -409,7 +413,7 @@ exprt smt2_incremental_decision_proceduret::get_expr(
409413
response.pretty()};
410414
}
411415
return construct_value_expr_from_smt(
412-
get_value_response->pairs()[0].get().value(), type);
416+
get_value_response->pairs()[0].get().value(), type, ns);
413417
}
414418

415419
// This is a fall back which builds resulting expression based on getting the
@@ -446,18 +450,20 @@ exprt smt2_incremental_decision_proceduret::get(const exprt &expr) const
446450
const auto array = get_identifier(
447451
index_expr->array(),
448452
expression_handle_identifiers,
449-
expression_identifiers);
453+
expression_identifiers,
454+
ns);
450455
const auto index = get_identifier(
451456
index_expr->index(),
452457
expression_handle_identifiers,
453-
expression_identifiers);
458+
expression_identifiers,
459+
ns);
454460
if(!array || !index)
455461
return {};
456462
return smt_array_theoryt::select(*array, *index);
457463
}
458464
if(
459465
auto identifier_descriptor = get_identifier(
460-
expr, expression_handle_identifiers, expression_identifiers))
466+
expr, expression_handle_identifiers, expression_identifiers, ns))
461467
{
462468
return identifier_descriptor;
463469
}
@@ -590,8 +596,8 @@ void smt2_incremental_decision_proceduret::define_object_properties()
590596

591597
exprt smt2_incremental_decision_proceduret::lower(exprt expression)
592598
{
593-
const exprt lowered =
594-
struct_encoding.encode(lower_byte_operators(expression, ns));
599+
const exprt lowered = struct_encoding.encode(
600+
lower_enum(lower_byte_operators(expression, ns), ns));
595601
log.conditional_output(log.debug(), [&](messaget::mstreamt &debug) {
596602
if(lowered != expression)
597603
debug << "lowered to -\n " << lowered.pretty(2, 0) << messaget::eom;

src/solvers/smt2_incremental/smt2_incremental_decision_procedure.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010
#include <util/std_expr.h>
1111

1212
#include <solvers/smt2_incremental/ast/smt_terms.h>
13+
#include <solvers/smt2_incremental/encoding/struct_encoding.h>
1314
#include <solvers/smt2_incremental/object_tracking.h>
1415
#include <solvers/smt2_incremental/smt_is_dynamic_object.h>
1516
#include <solvers/smt2_incremental/smt_object_size.h>
16-
#include <solvers/smt2_incremental/struct_encoding.h>
1717
#include <solvers/smt2_incremental/type_size_mapping.h>
1818
#include <solvers/stack_decision_procedure.h>
1919

0 commit comments

Comments
 (0)