Skip to content

Commit f9bd83e

Browse files
Merge pull request #7010 from thomasspriggs/tas/smt_quantifier_data_structures
Add SMT quantifier data structures
2 parents 8b53dba + 4a0b099 commit f9bd83e

File tree

8 files changed

+321
-4
lines changed

8 files changed

+321
-4
lines changed

src/solvers/smt2_incremental/construct_value_expr_from_smt.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,18 @@ class value_expr_from_smt_factoryt : public smt_term_const_downcast_visitort
8787
"Unexpected conversion of function application to value expression.");
8888
}
8989

90+
void visit(const smt_forall_termt &forall) override
91+
{
92+
INVARIANT(
93+
false, "Unexpected conversion of forall quantifier to value expression.");
94+
}
95+
96+
void visit(const smt_exists_termt &exists) override
97+
{
98+
INVARIANT(
99+
false, "Unexpected conversion of exists quantifier to value expression.");
100+
}
101+
90102
public:
91103
/// \brief This function is complete the external interface to this class. All
92104
/// construction of this class and construction of expressions should be

src/solvers/smt2_incremental/smt_terms.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,74 @@ smt_function_application_termt::arguments() const
151151
});
152152
}
153153

154+
smt_forall_termt::smt_forall_termt(
155+
std::vector<smt_identifier_termt> bound_variables,
156+
smt_termt predicate)
157+
: smt_termt{ID_smt_forall_term, smt_bool_sortt{}}
158+
{
159+
INVARIANT(
160+
!bound_variables.empty(),
161+
"A forall term should bind at least one variable.");
162+
std::transform(
163+
std::make_move_iterator(bound_variables.begin()),
164+
std::make_move_iterator(bound_variables.end()),
165+
std::back_inserter(get_sub()),
166+
[](smt_identifier_termt &&bound_variable) {
167+
return irept{std::move(bound_variable)};
168+
});
169+
INVARIANT(
170+
predicate.get_sort().cast<smt_bool_sortt>(),
171+
"Predicate of forall quantifier is expected to have bool sort.");
172+
set(ID_body, std::move(predicate));
173+
}
174+
175+
const smt_termt &smt_forall_termt::predicate() const
176+
{
177+
return static_cast<const smt_termt &>(find(ID_body));
178+
}
179+
180+
std::vector<std::reference_wrapper<const smt_identifier_termt>>
181+
smt_forall_termt::bound_variables() const
182+
{
183+
return make_range(get_sub()).map([](const irept &variable) {
184+
return std::cref(static_cast<const smt_identifier_termt &>(variable));
185+
});
186+
}
187+
188+
smt_exists_termt::smt_exists_termt(
189+
std::vector<smt_identifier_termt> bound_variables,
190+
smt_termt predicate)
191+
: smt_termt{ID_smt_exists_term, smt_bool_sortt{}}
192+
{
193+
INVARIANT(
194+
!bound_variables.empty(),
195+
"A exists term should bind at least one variable.");
196+
std::transform(
197+
std::make_move_iterator(bound_variables.begin()),
198+
std::make_move_iterator(bound_variables.end()),
199+
std::back_inserter(get_sub()),
200+
[](smt_identifier_termt &&bound_variable) {
201+
return irept{std::move(bound_variable)};
202+
});
203+
INVARIANT(
204+
predicate.get_sort().cast<smt_bool_sortt>(),
205+
"Predicate of exists quantifier is expected to have bool sort.");
206+
set(ID_body, std::move(predicate));
207+
}
208+
209+
const smt_termt &smt_exists_termt::predicate() const
210+
{
211+
return static_cast<const smt_termt &>(find(ID_body));
212+
}
213+
214+
std::vector<std::reference_wrapper<const smt_identifier_termt>>
215+
smt_exists_termt::bound_variables() const
216+
{
217+
return make_range(get_sub()).map([](const irept &variable) {
218+
return std::cref(static_cast<const smt_identifier_termt &>(variable));
219+
});
220+
}
221+
154222
template <typename visitort>
155223
void accept(const smt_termt &term, const irep_idt &id, visitort &&visitor)
156224
{

src/solvers/smt2_incremental/smt_terms.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@ TERM_ID(bool_literal)
1010
TERM_ID(identifier)
1111
TERM_ID(bit_vector_constant)
1212
TERM_ID(function_application)
13+
TERM_ID(forall)
14+
TERM_ID(exists)

src/solvers/smt2_incremental/smt_terms.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,28 @@ class smt_function_application_termt : public smt_termt
207207
};
208208
};
209209

210+
class smt_forall_termt : public smt_termt
211+
{
212+
public:
213+
smt_forall_termt(
214+
std::vector<smt_identifier_termt> bound_variables,
215+
smt_termt predicate);
216+
std::vector<std::reference_wrapper<const smt_identifier_termt>>
217+
bound_variables() const;
218+
const smt_termt &predicate() const;
219+
};
220+
221+
class smt_exists_termt : public smt_termt
222+
{
223+
public:
224+
smt_exists_termt(
225+
std::vector<smt_identifier_termt> bound_variables,
226+
smt_termt predicate);
227+
std::vector<std::reference_wrapper<const smt_identifier_termt>>
228+
bound_variables() const;
229+
const smt_termt &predicate() const;
230+
};
231+
210232
class smt_term_const_downcast_visitort
211233
{
212234
public:

src/solvers/smt2_incremental/smt_to_smt2_string.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@ std::string smt_to_smt2_string(const smt_sortt &sort)
9696
return ss.str();
9797
}
9898

99+
struct sorted_variablest final
100+
{
101+
std::vector<std::reference_wrapper<const smt_identifier_termt>> identifiers;
102+
};
103+
99104
/// \note The printing algorithm in the `smt_term_to_string_convertert` class is
100105
/// implemented using an explicit `std::stack` rather than using recursion
101106
/// and the call stack. This is done in order to ensure we can print smt terms
@@ -124,6 +129,7 @@ class smt_term_to_string_convertert : private smt_term_const_downcast_visitort
124129
template <typename elementt>
125130
output_functiont make_output_function(
126131
const std::vector<std::reference_wrapper<const elementt>> &output);
132+
output_functiont make_output_function(const sorted_variablest &output);
127133

128134
/// \brief Single argument version of `push_outputs`.
129135
template <typename outputt>
@@ -153,6 +159,8 @@ class smt_term_to_string_convertert : private smt_term_const_downcast_visitort
153159
void visit(const smt_bit_vector_constant_termt &bit_vector_constant) override;
154160
void
155161
visit(const smt_function_application_termt &function_application) override;
162+
void visit(const smt_forall_termt &forall) override;
163+
void visit(const smt_exists_termt &exists) override;
156164

157165
public:
158166
/// \brief This function is complete the external interface to this class. All
@@ -199,6 +207,25 @@ smt_term_to_string_convertert::make_output_function(
199207
};
200208
}
201209

210+
smt_term_to_string_convertert::output_functiont
211+
smt_term_to_string_convertert::make_output_function(
212+
const sorted_variablest &output)
213+
{
214+
return [=](std::ostream &os) {
215+
const auto push_sorted_variable =
216+
[&](const smt_identifier_termt &identifier) {
217+
push_outputs("(", identifier, " ", identifier.get_sort(), ")");
218+
};
219+
for(const auto &bound_variable :
220+
make_range(output.identifiers.rbegin(), --output.identifiers.rend()))
221+
{
222+
push_sorted_variable(bound_variable);
223+
push_output(" ");
224+
}
225+
push_sorted_variable(output.identifiers.front());
226+
};
227+
}
228+
202229
template <typename outputt>
203230
void smt_term_to_string_convertert::push_output(outputt &&output)
204231
{
@@ -255,6 +282,20 @@ void smt_term_to_string_convertert::visit(
255282
push_outputs("(", id, std::move(arguments), ")");
256283
}
257284

285+
void smt_term_to_string_convertert::visit(const smt_forall_termt &forall)
286+
{
287+
sorted_variablest bound_variables{forall.bound_variables()};
288+
auto predicate = forall.predicate();
289+
push_outputs("(forall (", bound_variables, ") ", std::move(predicate), ")");
290+
}
291+
292+
void smt_term_to_string_convertert::visit(const smt_exists_termt &exists)
293+
{
294+
sorted_variablest bound_variables{exists.bound_variables()};
295+
auto predicate = exists.predicate();
296+
push_outputs("(exists (", bound_variables, ") ", std::move(predicate), ")");
297+
}
298+
258299
std::ostream &
259300
smt_term_to_string_convertert::convert(std::ostream &os, const smt_termt &term)
260301
{

unit/solvers/smt2_incremental/construct_value_expr_from_smt.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,18 @@ TEST_CASE(
127127
smt_core_theoryt::make_not(smt_bool_literal_termt{true}),
128128
unsignedbv_typet{16},
129129
"Unexpected conversion of function application to value expression."},
130+
rowt{
131+
smt_forall_termt{
132+
{smt_identifier_termt{"i", smt_bool_sortt{}}},
133+
smt_bool_literal_termt{true}},
134+
bool_typet{},
135+
"Unexpected conversion of forall quantifier to value expression."},
136+
rowt{
137+
smt_exists_termt{
138+
{smt_identifier_termt{"j", smt_bool_sortt{}}},
139+
smt_bool_literal_termt{true}},
140+
bool_typet{},
141+
"Unexpected conversion of exists quantifier to value expression."},
130142
rowt{
131143
smt_bit_vector_constant_termt{0, 16},
132144
pointer_typet{unsigned_int_type(), 0},

unit/solvers/smt2_incremental/smt_terms.cpp

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

3-
#include <testing-utils/use_catch.h>
3+
#include <util/mp_arith.h>
44

55
#include <solvers/smt2_incremental/smt_core_theory.h>
66
#include <solvers/smt2_incremental/smt_terms.h>
7-
8-
#include <util/mp_arith.h>
7+
#include <testing-utils/invariant.h>
8+
#include <testing-utils/use_catch.h>
99

1010
#include <type_traits>
1111

@@ -85,6 +85,82 @@ TEST_CASE("smt_termt equality.", "[core][smt2_incremental]")
8585
smt_bit_vector_constant_termt{12, 8});
8686
}
8787

88+
template <typename term_typet>
89+
std::string term_description();
90+
91+
template <>
92+
std::string term_description<smt_forall_termt>()
93+
{
94+
return "forall";
95+
}
96+
97+
template <>
98+
std::string term_description<smt_exists_termt>()
99+
{
100+
return "exists";
101+
}
102+
103+
TEMPLATE_TEST_CASE(
104+
"smt quantifier terms",
105+
"[core][smt2_incremental]",
106+
smt_forall_termt,
107+
smt_exists_termt)
108+
{
109+
using quantifiert = TestType;
110+
const smt_identifier_termt i{"i", smt_bit_vector_sortt{8}};
111+
const smt_identifier_termt j{"j", smt_bit_vector_sortt{8}};
112+
SECTION("Getters")
113+
{
114+
SECTION("One bound variable")
115+
{
116+
const auto predicate = smt_core_theoryt::equal(i, i);
117+
const quantifiert quantifier{{i}, predicate};
118+
CHECK(quantifier.get_sort() == smt_bool_sortt{});
119+
const auto variables = quantifier.bound_variables();
120+
CHECK(quantifier.predicate() == predicate);
121+
REQUIRE(variables.size() == 1);
122+
CHECK(variables[0].get() == i);
123+
}
124+
SECTION("Two bound variables")
125+
{
126+
const auto predicate = smt_core_theoryt::distinct(i, j);
127+
const quantifiert quantifier{{i, j}, predicate};
128+
CHECK(quantifier.get_sort() == smt_bool_sortt{});
129+
const auto variables = quantifier.bound_variables();
130+
CHECK(quantifier.predicate() == predicate);
131+
REQUIRE(variables.size() == 2);
132+
CHECK(variables[0].get() == i);
133+
CHECK(variables[1].get() == j);
134+
}
135+
}
136+
SECTION("Constructor validation")
137+
{
138+
cbmc_invariants_should_throwt invariants_throw;
139+
SECTION("Empty variables")
140+
{
141+
const auto generate_error = [&]() {
142+
quantifiert{{}, smt_core_theoryt::equal(i, i)};
143+
};
144+
REQUIRE_THROWS_MATCHES(
145+
generate_error(),
146+
invariant_failedt,
147+
invariant_failure_containing(
148+
"A " + term_description<quantifiert>() +
149+
" term should bind at least one variable."));
150+
}
151+
SECTION("Non bool predicate")
152+
{
153+
const auto generate_error = [&]() { quantifiert{{i}, i}; };
154+
REQUIRE_THROWS_MATCHES(
155+
generate_error(),
156+
invariant_failedt,
157+
invariant_failure_containing(
158+
"Predicate of " + term_description<quantifiert>() +
159+
" quantifier is expected to have bool sort."));
160+
}
161+
}
162+
}
163+
88164
template <typename expected_termt>
89165
class term_visit_type_checkert final : public smt_term_const_downcast_visitort
90166
{
@@ -139,6 +215,30 @@ class term_visit_type_checkert final : public smt_term_const_downcast_visitort
139215
unexpected_term_visited = true;
140216
}
141217
}
218+
219+
void visit(const smt_forall_termt &) override
220+
{
221+
if(std::is_same<expected_termt, smt_forall_termt>::value)
222+
{
223+
expected_term_visited = true;
224+
}
225+
else
226+
{
227+
unexpected_term_visited = true;
228+
}
229+
}
230+
231+
void visit(const smt_exists_termt &) override
232+
{
233+
if(std::is_same<expected_termt, smt_exists_termt>::value)
234+
{
235+
expected_term_visited = true;
236+
}
237+
else
238+
{
239+
unexpected_term_visited = true;
240+
}
241+
}
142242
};
143243

144244
template <typename term_typet>
@@ -168,13 +268,31 @@ smt_function_application_termt make_test_term<smt_function_application_termt>()
168268
return smt_core_theoryt::make_not(smt_bool_literal_termt{true});
169269
}
170270

271+
template <>
272+
smt_forall_termt make_test_term<smt_forall_termt>()
273+
{
274+
const smt_identifier_termt identifier{"i", smt_bit_vector_sortt{8}};
275+
return smt_forall_termt{
276+
{identifier}, smt_core_theoryt::equal(identifier, identifier)};
277+
}
278+
279+
template <>
280+
smt_exists_termt make_test_term<smt_exists_termt>()
281+
{
282+
const smt_identifier_termt identifier{"i", smt_bit_vector_sortt{8}};
283+
return smt_exists_termt{
284+
{identifier}, smt_core_theoryt::equal(identifier, identifier)};
285+
}
286+
171287
TEMPLATE_TEST_CASE(
172288
"smt_termt::accept(visitor)",
173289
"[core][smt2_incremental]",
174290
smt_bool_literal_termt,
175291
smt_identifier_termt,
176292
smt_bit_vector_constant_termt,
177-
smt_function_application_termt)
293+
smt_function_application_termt,
294+
smt_forall_termt,
295+
smt_exists_termt)
178296
{
179297
term_visit_type_checkert<TestType> checker;
180298
make_test_term<TestType>().accept(checker);

0 commit comments

Comments
 (0)