Skip to content

Commit e831f43

Browse files
Merge pull request #6654 from thomasspriggs/tas/smt_bv_extract
Add bit vector extract operation support to incremental SMT2 solving
2 parents d5ff1ba + 8edd58d commit e831f43

15 files changed

+542
-25
lines changed

src/solvers/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ SRC = $(BOOLEFORCE_SRC) \
198198
smt2_incremental/smt_bit_vector_theory.cpp \
199199
smt2_incremental/smt_commands.cpp \
200200
smt2_incremental/smt_core_theory.cpp \
201+
smt2_incremental/smt_index.cpp \
201202
smt2_incremental/smt_logics.cpp \
202203
smt2_incremental/smt_options.cpp \
203204
smt2_incremental/smt_response_validation.cpp \

src/solvers/smt2_incremental/smt_bit_vector_theory.cpp

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

55
#include <util/invariant.h>
66

7+
const char *smt_bit_vector_theoryt::extractt::identifier()
8+
{
9+
return "extract";
10+
}
11+
12+
smt_sortt
13+
smt_bit_vector_theoryt::extractt::return_sort(const smt_termt &operand) const
14+
{
15+
return smt_bit_vector_sortt{i - j + 1};
16+
}
17+
18+
std::vector<smt_indext> smt_bit_vector_theoryt::extractt::indices() const
19+
{
20+
return {smt_numeral_indext{i}, smt_numeral_indext{j}};
21+
}
22+
23+
void smt_bit_vector_theoryt::extractt::validate(const smt_termt &operand) const
24+
{
25+
PRECONDITION(i >= j);
26+
const auto bit_vector_sort = operand.get_sort().cast<smt_bit_vector_sortt>();
27+
PRECONDITION(bit_vector_sort);
28+
PRECONDITION(i < bit_vector_sort->bit_width());
29+
}
30+
31+
smt_function_application_termt::factoryt<smt_bit_vector_theoryt::extractt>
32+
smt_bit_vector_theoryt::extract(std::size_t i, std::size_t j)
33+
{
34+
PRECONDITION(i >= j);
35+
return smt_function_application_termt::factoryt<extractt>(i, j);
36+
}
37+
738
static void validate_bit_vector_operator_arguments(
839
const smt_termt &left,
940
const smt_termt &right)

src/solvers/smt2_incremental/smt_bit_vector_theory.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,18 @@
88
class smt_bit_vector_theoryt
99
{
1010
public:
11+
struct extractt final
12+
{
13+
std::size_t i;
14+
std::size_t j;
15+
static const char *identifier();
16+
smt_sortt return_sort(const smt_termt &operand) const;
17+
std::vector<smt_indext> indices() const;
18+
void validate(const smt_termt &operand) const;
19+
};
20+
static smt_function_application_termt::factoryt<extractt>
21+
extract(std::size_t i, std::size_t j);
22+
1123
// Relational operator class declarations
1224
struct unsigned_less_thant final
1325
{
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 "smt_index.h"
4+
5+
// Define the irep_idts for kinds of index.
6+
const irep_idt ID_smt_numeral_index{"smt_numeral_index"};
7+
const irep_idt ID_smt_symbol_index{"smt_symbol_index"};
8+
9+
bool smt_indext::operator==(const smt_indext &other) const
10+
{
11+
return irept::operator==(other);
12+
}
13+
14+
bool smt_indext::operator!=(const smt_indext &other) const
15+
{
16+
return !(*this == other);
17+
}
18+
19+
template <>
20+
const smt_numeral_indext *smt_indext::cast<smt_numeral_indext>() const &
21+
{
22+
return id() == ID_smt_numeral_index
23+
? static_cast<const smt_numeral_indext *>(this)
24+
: nullptr;
25+
}
26+
27+
template <>
28+
const smt_symbol_indext *smt_indext::cast<smt_symbol_indext>() const &
29+
{
30+
return id() == ID_smt_symbol_index
31+
? static_cast<const smt_symbol_indext *>(this)
32+
: nullptr;
33+
}
34+
35+
void smt_indext::accept(smt_index_const_downcast_visitort &visitor) const
36+
{
37+
if(const auto numeral = this->cast<smt_numeral_indext>())
38+
return visitor.visit(*numeral);
39+
if(const auto symbol = this->cast<smt_symbol_indext>())
40+
return visitor.visit(*symbol);
41+
UNREACHABLE;
42+
}
43+
44+
smt_numeral_indext::smt_numeral_indext(std::size_t value)
45+
: smt_indext{ID_smt_numeral_index}
46+
{
47+
set_size_t(ID_value, value);
48+
}
49+
50+
std::size_t smt_numeral_indext::value() const
51+
{
52+
return get_size_t(ID_value);
53+
}
54+
55+
smt_symbol_indext::smt_symbol_indext(irep_idt identifier)
56+
: smt_indext{ID_smt_symbol_index}
57+
{
58+
PRECONDITION(!identifier.empty());
59+
set(ID_identifier, identifier);
60+
}
61+
62+
irep_idt smt_symbol_indext::identifier() const
63+
{
64+
return get(ID_identifier);
65+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// Author: Diffblue Ltd.
2+
3+
#ifndef CPROVER_SOLVERS_SMT2_INCREMENTAL_SMT_INDEX_H
4+
#define CPROVER_SOLVERS_SMT2_INCREMENTAL_SMT_INDEX_H
5+
6+
#include <util/irep.h>
7+
8+
class smt_index_const_downcast_visitort;
9+
10+
/// \brief
11+
/// For implementation of indexed identifiers. See SMT-LIB Standard Version 2.6
12+
/// section 3.3.
13+
class smt_indext : protected irept
14+
{
15+
public:
16+
// smt_indext does not support the notion of an empty / null state. Use
17+
// optionalt<smt_indext> instead if an empty index is required.
18+
smt_indext() = delete;
19+
20+
using irept::pretty;
21+
22+
bool operator==(const smt_indext &) const;
23+
bool operator!=(const smt_indext &) const;
24+
25+
template <typename sub_classt>
26+
const sub_classt *cast() const &;
27+
28+
void accept(smt_index_const_downcast_visitort &) const;
29+
30+
/// \brief Class for adding the ability to up and down cast smt_indext to and
31+
/// from irept. These casts are required by other irept derived classes in
32+
/// order to store instances of smt_termt inside them.
33+
/// \tparam derivedt The type of class which derives from this class and from
34+
/// irept.
35+
template <typename derivedt>
36+
class storert
37+
{
38+
protected:
39+
storert();
40+
static irept upcast(smt_indext index);
41+
static const smt_indext &downcast(const irept &);
42+
};
43+
44+
protected:
45+
using irept::irept;
46+
};
47+
48+
template <typename derivedt>
49+
smt_indext::storert<derivedt>::storert()
50+
{
51+
static_assert(
52+
std::is_base_of<irept, derivedt>::value &&
53+
std::is_base_of<storert<derivedt>, derivedt>::value,
54+
"Only irept based classes need to upcast smt_sortt to store it.");
55+
}
56+
57+
template <typename derivedt>
58+
irept smt_indext::storert<derivedt>::upcast(smt_indext index)
59+
{
60+
return static_cast<irept &&>(std::move(index));
61+
}
62+
63+
template <typename derivedt>
64+
const smt_indext &smt_indext::storert<derivedt>::downcast(const irept &irep)
65+
{
66+
return static_cast<const smt_indext &>(irep);
67+
}
68+
69+
class smt_numeral_indext : public smt_indext
70+
{
71+
public:
72+
explicit smt_numeral_indext(std::size_t value);
73+
std::size_t value() const;
74+
};
75+
76+
class smt_symbol_indext : public smt_indext
77+
{
78+
public:
79+
explicit smt_symbol_indext(irep_idt identifier);
80+
irep_idt identifier() const;
81+
};
82+
83+
class smt_index_const_downcast_visitort
84+
{
85+
public:
86+
virtual void visit(const smt_numeral_indext &) = 0;
87+
virtual void visit(const smt_symbol_indext &) = 0;
88+
};
89+
90+
#endif // CPROVER_SOLVERS_SMT2_INCREMENTAL_SMT_INDEX_H

src/solvers/smt2_incremental/smt_terms.cpp

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,10 @@ static bool is_valid_smt_identifier(irep_idt identifier)
5656
return std::regex_match(id2string(identifier), valid);
5757
}
5858

59-
smt_identifier_termt::smt_identifier_termt(irep_idt identifier, smt_sortt sort)
59+
smt_identifier_termt::smt_identifier_termt(
60+
irep_idt identifier,
61+
smt_sortt sort,
62+
std::vector<smt_indext> indices)
6063
: smt_termt(ID_smt_identifier_term, std::move(sort))
6164
{
6265
// The below invariant exists as a sanity check that the string being used for
@@ -67,13 +70,27 @@ smt_identifier_termt::smt_identifier_termt(irep_idt identifier, smt_sortt sort)
6770
is_valid_smt_identifier(identifier),
6871
R"(Identifiers may not contain | characters.)");
6972
set(ID_identifier, identifier);
73+
for(auto &index : indices)
74+
{
75+
get_sub().push_back(
76+
smt_indext::storert<smt_identifier_termt>::upcast(std::move(index)));
77+
}
7078
}
7179

7280
irep_idt smt_identifier_termt::identifier() const
7381
{
7482
return get(ID_identifier);
7583
}
7684

85+
std::vector<std::reference_wrapper<const smt_indext>>
86+
smt_identifier_termt::indices() const
87+
{
88+
return make_range(get_sub()).map([](const irept &index) {
89+
return std::cref(
90+
smt_indext::storert<smt_identifier_termt>::downcast(index));
91+
});
92+
}
93+
7794
smt_bit_vector_constant_termt::smt_bit_vector_constant_termt(
7895
const mp_integer &value,
7996
smt_bit_vector_sortt sort)

src/solvers/smt2_incremental/smt_terms.h

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,14 @@
33
#ifndef CPROVER_SOLVERS_SMT2_INCREMENTAL_SMT_TERMS_H
44
#define CPROVER_SOLVERS_SMT2_INCREMENTAL_SMT_TERMS_H
55

6-
#include <solvers/smt2_incremental/smt_sorts.h>
76
#include <util/irep.h>
87

8+
#include <solvers/smt2_incremental/smt_index.h>
9+
#include <solvers/smt2_incremental/smt_sorts.h>
10+
#include <solvers/smt2_incremental/type_traits.h>
11+
912
#include <functional>
13+
#include <utility>
1014

1115
class BigInt;
1216
using mp_integer = BigInt;
@@ -77,7 +81,14 @@ class smt_bool_literal_termt : public smt_termt
7781

7882
/// Stores identifiers in unescaped and unquoted form. Any escaping or quoting
7983
/// required should be performed during printing.
80-
class smt_identifier_termt : public smt_termt
84+
/// \details
85+
/// The SMT-LIB standard Version 2.6 refers to "indexed" identifiers which have
86+
/// 1 or more indices and "simple" identifiers which have no indicies. The
87+
/// internal `smt_identifier_termt` class is used for both kinds of identifier
88+
/// which are distinguished based on whether the collection of `indices` is
89+
/// empty or not.
90+
class smt_identifier_termt : public smt_termt,
91+
private smt_indext::storert<smt_identifier_termt>
8192
{
8293
public:
8394
/// \brief Constructs an identifier term with the given \p identifier and
@@ -91,8 +102,14 @@ class smt_identifier_termt : public smt_termt
91102
/// \param sort: The sort which this term will have. All terms in our abstract
92103
/// form must be sorted, even if those sorts are not printed in all
93104
/// contexts.
94-
smt_identifier_termt(irep_idt identifier, smt_sortt sort);
105+
/// \param indices: This should be collection of indices for an indexed
106+
/// identifier, or an empty collection for simple (non-indexed) identifiers.
107+
smt_identifier_termt(
108+
irep_idt identifier,
109+
smt_sortt sort,
110+
std::vector<smt_indext> indices = {});
95111
irep_idt identifier() const;
112+
std::vector<std::reference_wrapper<const smt_indext>> indices() const;
96113
};
97114

98115
class smt_bit_vector_constant_termt : public smt_termt
@@ -121,6 +138,44 @@ class smt_function_application_termt : public smt_termt
121138
smt_identifier_termt function_identifier,
122139
std::vector<smt_termt> arguments);
123140

141+
// This is used to detect if \p functiont has an `indicies` member function.
142+
// It will resolve to std::true_type if it does or std::false type otherwise.
143+
template <class functiont, class = void>
144+
struct has_indicest : std::false_type
145+
{
146+
};
147+
148+
template <class functiont>
149+
struct has_indicest<
150+
functiont,
151+
void_t<decltype(std::declval<functiont>().indices())>> : std::true_type
152+
{
153+
};
154+
155+
/// Overload for when \p functiont does not have indices.
156+
template <class functiont>
157+
static std::vector<smt_indext>
158+
indices(const functiont &function, const std::false_type &has_indices)
159+
{
160+
return {};
161+
}
162+
163+
/// Overload for when \p functiont has indices member function.
164+
template <class functiont>
165+
static std::vector<smt_indext>
166+
indices(const functiont &function, const std::true_type &has_indices)
167+
{
168+
return function.indices();
169+
}
170+
171+
/// Returns `function.indices` if `functiont` has an `indices` member function
172+
/// or returns an empty collection otherwise.
173+
template <class functiont>
174+
static std::vector<smt_indext> indices(const functiont &function)
175+
{
176+
return indices(function, has_indicest<functiont>{});
177+
}
178+
124179
public:
125180
const smt_identifier_termt &function_identifier() const;
126181
std::vector<std::reference_wrapper<const smt_termt>> arguments() const;
@@ -133,7 +188,7 @@ class smt_function_application_termt : public smt_termt
133188

134189
public:
135190
template <typename... function_type_argument_typest>
136-
explicit factoryt(function_type_argument_typest &&... arguments)
191+
explicit factoryt(function_type_argument_typest &&... arguments) noexcept
137192
: function{std::forward<function_type_argument_typest>(arguments)...}
138193
{
139194
}
@@ -145,7 +200,8 @@ class smt_function_application_termt : public smt_termt
145200
function.validate(arguments...);
146201
auto return_sort = function.return_sort(arguments...);
147202
return smt_function_application_termt{
148-
smt_identifier_termt{function.identifier(), std::move(return_sort)},
203+
smt_identifier_termt{
204+
function.identifier(), std::move(return_sort), indices(function)},
149205
{std::forward<argument_typest>(arguments)...}};
150206
}
151207
};

0 commit comments

Comments
 (0)