CBMC
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
smt_response_validation.cpp
Go to the documentation of this file.
1// Author: Diffblue Ltd.
2
17
19
20#include <util/arith_tools.h>
21#include <util/mp_arith.h>
22#include <util/range.h>
23
25
26#include "smt_to_smt2_string.h"
27
28#include <regex>
29
31 const irept &parse_tree,
32 const std::unordered_map<irep_idt, smt_identifier_termt> &identifier_table);
33
34// Implementation detail of `collect_error_messages` below.
35template <typename argumentt, typename... argumentst>
37 std::vector<std::string> &collected_error_messages,
39{
40 if(const auto messages = argument.get_if_error())
41 {
43 collected_error_messages.end(), messages->cbegin(), messages->end());
44 }
45}
46
47// Implementation detail of `collect_error_messages` below.
48template <typename argumentt, typename... argumentst>
57
62template <typename... argumentst>
63std::vector<std::string> collect_error_messages(argumentst &&... arguments)
64{
65 std::vector<std::string> collected_error_messages;
68}
69
84template <
85 typename smt_to_constructt,
87 typename... argumentst>
89{
90 const auto collected_error_messages = collect_error_messages(arguments...);
91 if(!collected_error_messages.empty())
93 else
94 {
96 smt_to_constructt{(*arguments.get_if_valid())...}};
97 }
98}
99
106static std::string print_parse_tree(const irept &parse_tree)
107{
108 return parse_tree.pretty(0, 0);
109}
110
113{
114 if(!parse_tree.get_sub().empty())
115 {
117 "Expected string literal, found \"" + print_parse_tree(parse_tree) +
118 "\".");
119 }
120 return response_or_errort<irep_idt>{parse_tree.id()};
121}
122
128static std::optional<response_or_errort<smt_responset>>
130{
131 // Check if the parse tree looks to be an error response.
132 if(!parse_tree.id().empty())
133 return {};
134 if(parse_tree.get_sub().empty())
135 return {};
136 if(parse_tree.get_sub().at(0).id() != "error")
137 return {};
138 // Parse tree is now considered to be an error response and anything
139 // unexpected in the parse tree is now considered to be an invalid response.
140 if(parse_tree.get_sub().size() == 1)
141 {
142 return {response_or_errort<smt_responset>{std::vector<std::string>{
143 {"Error response is missing the error message."}}}};
144 }
145 if(parse_tree.get_sub().size() > 2)
146 {
147 return {response_or_errort<smt_responset>{std::vector<std::string>{
148 {"Error response has multiple error messages - \"" +
149 print_parse_tree(parse_tree) + "\"."}}}};
150 }
152 validate_string_literal(parse_tree.get_sub()[1]));
153}
154
155static bool all_subs_are_pairs(const irept &parse_tree)
156{
157 return std::all_of(
158 parse_tree.get_sub().cbegin(),
159 parse_tree.get_sub().cend(),
160 [](const irept &sub) { return sub.get_sub().size() == 2; });
161}
162
165static std::optional<smt_termt>
167{
168 if(parse_tree.get_sub().size() != 3)
169 return {};
170 if(parse_tree.get_sub().at(0).id() != "_")
171 return {};
172 const auto value_string = id2string(parse_tree.get_sub().at(1).id());
173 std::smatch match_results;
174 static const std::regex bv_value_regex{R"(^bv(\d+)$)", std::regex::optimize};
175 if(!std::regex_search(value_string, match_results, bv_value_regex))
176 return {};
177 INVARIANT(
178 match_results.size() == 2,
179 "Match results should include digits sub-expression if regex is matched.");
180 const std::string value_digits = match_results[1];
181 const auto value = string2integer(value_digits);
182 const auto bit_width_string = id2string(parse_tree.get_sub().at(2).id());
183 const auto bit_width =
185 if(bit_width == 0)
186 return {};
187 if(value >= power(mp_integer{2}, bit_width))
188 return {};
189 return smt_bit_vector_constant_termt{value, bit_width};
190}
191
192static std::optional<smt_termt> valid_smt_bool(const irept &parse_tree)
193{
194 if(!parse_tree.get_sub().empty())
195 return {};
196 if(parse_tree.id() == ID_true)
197 return {smt_bool_literal_termt{true}};
198 if(parse_tree.id() == ID_false)
199 return {smt_bool_literal_termt{false}};
200 return {};
201}
202
203static std::optional<smt_termt> valid_smt_binary(const std::string &text)
204{
205 static const std::regex binary_format{"#b[01]+"};
206 if(!std::regex_match(text, binary_format))
207 return {};
208 const mp_integer value = string2integer({text.begin() + 2, text.end()}, 2);
209 // Width is number of bit values minus the "#b" prefix.
210 const std::size_t width = text.size() - 2;
211 return {smt_bit_vector_constant_termt{value, width}};
212}
213
214static std::optional<smt_termt> valid_smt_hex(const std::string &text)
215{
216 static const std::regex hex_format{"#x[0-9A-Za-z]+"};
217 if(!std::regex_match(text, hex_format))
218 return {};
219 const std::string hex{text.begin() + 2, text.end()};
220 // SMT-LIB 2 allows hex characters to be upper or lower case, but they should
221 // be upper case for mp_integer.
222 const mp_integer value =
223 string2integer(make_range(hex).map<std::function<int(int)>>(toupper), 16);
224 const std::size_t width = (text.size() - 2) * 4;
225 return {smt_bit_vector_constant_termt{value, width}};
226}
227
228static std::optional<smt_termt>
230{
231 if(const auto indexed = valid_smt_indexed_bit_vector(parse_tree))
232 return *indexed;
233 if(!parse_tree.get_sub().empty() || parse_tree.id().empty())
234 return {};
235 const auto value_string = id2string(parse_tree.id());
237 return *smt_binary;
238 if(const auto smt_hex = valid_smt_hex(value_string))
239 return *smt_hex;
240 return {};
241}
242
243static std::optional<response_or_errort<smt_termt>> try_select_validation(
244 const irept &parse_tree,
245 const std::unordered_map<irep_idt, smt_identifier_termt> &identifier_table)
246{
247 if(parse_tree.get_sub().empty())
248 return {};
249 if(parse_tree.get_sub()[0].id() != "select")
250 return {};
251 if(parse_tree.get_sub().size() != 3)
252 {
253 return response_or_errort<smt_termt>{std::vector<std::string>{
254 {"\"select\" is expected to have 2 arguments, but " +
255 std::to_string(parse_tree.get_sub().size()) +
256 " arguments were found - \"" + print_parse_tree(parse_tree) + "\"."}}};
257 }
258 const auto array = validate_term(parse_tree.get_sub()[1], identifier_table);
259 const auto index = validate_term(parse_tree.get_sub()[2], identifier_table);
260 const auto error_messages = collect_error_messages(array, index);
261 if(!error_messages.empty())
263 return {smt_array_theoryt::select.validation(
264 *array.get_if_valid(), *index.get_if_valid())};
265}
266
268 const irept &parse_tree,
269 const std::unordered_map<irep_idt, smt_identifier_termt> &identifier_table)
270{
271 if(const auto smt_bool = valid_smt_bool(parse_tree))
273 if(const auto bit_vector_constant = valid_smt_bit_vector_constant(parse_tree))
275 const auto find_result = identifier_table.find(parse_tree.id());
276 if(find_result != identifier_table.end())
278 if(
279 const auto select_validation =
280 try_select_validation(parse_tree, identifier_table))
281 {
282 return *select_validation;
283 }
284 return response_or_errort<smt_termt>{std::vector<std::string>{
285 {"Unrecognised SMT term - \"" + print_parse_tree(parse_tree) + "\"."}}};
286}
287
290 const irept &pair_parse_tree,
291 const std::unordered_map<irep_idt, smt_identifier_termt> &identifier_table)
292{
294 PRECONDITION(pair_parse_tree.get_sub().size() == 2);
295 const auto descriptor_validation =
296 validate_term(pair_parse_tree.get_sub()[0], identifier_table);
297 const auto value_validation =
298 validate_term(pair_parse_tree.get_sub()[1], identifier_table);
299 const auto error_messages =
301 if(!error_messages.empty())
302 return resultt{error_messages};
303 const auto &valid_descriptor = *descriptor_validation.get_if_valid();
304 const auto &valid_value = *value_validation.get_if_valid();
305 if(valid_descriptor.get_sort() != valid_value.get_sort())
306 {
307 return resultt{
308 {"Mismatched descriptor and value sorts in - " +
309 print_parse_tree(pair_parse_tree) + "\nDescriptor has sort " +
310 smt_to_smt2_string(valid_descriptor.get_sort()) + "\nValue has sort " +
311 smt_to_smt2_string(valid_value.get_sort())}};
312 }
313 // see https://github.com/diffblue/cbmc/issues/7464 for why we explicitly name
314 // the valuation_pairt type here:
315 return resultt{
317}
318
324static std::optional<response_or_errort<smt_responset>>
326 const irept &parse_tree,
327 const std::unordered_map<irep_idt, smt_identifier_termt> &identifier_table)
328{
329 // Shape matching for does this look like a get value response?
330 if(!parse_tree.id().empty())
331 return {};
332 if(parse_tree.get_sub().empty())
333 return {};
334 if(!all_subs_are_pairs(parse_tree))
335 return {};
336 std::vector<std::string> error_messages;
337 std::vector<smt_get_value_responset::valuation_pairt> valuation_pairs;
338 for(const auto &pair : parse_tree.get_sub())
339 {
340 const auto pair_validation_result =
341 validate_valuation_pair(pair, identifier_table);
342 if(const auto error = pair_validation_result.get_if_error())
343 error_messages.insert(error_messages.end(), error->begin(), error->end());
344 if(const auto valid_pair = pair_validation_result.get_if_valid())
345 valuation_pairs.push_back(*valid_pair);
346 }
347 if(!error_messages.empty())
349 else
350 {
353 }
354}
355
357 const irept &parse_tree,
358 const std::unordered_map<irep_idt, smt_identifier_termt> &identifier_table)
359{
360 if(parse_tree.id() == "sat")
363 if(parse_tree.id() == "unsat")
366 if(parse_tree.id() == "unknown")
369 if(const auto error_response = valid_smt_error_response(parse_tree))
370 return *error_response;
371 if(parse_tree.id() == "success")
373 if(parse_tree.id() == "unsupported")
375 if(
376 const auto get_value_response =
377 valid_smt_get_value_response(parse_tree, identifier_table))
378 {
379 return *get_value_response;
380 }
381 return response_or_errort<smt_responset>{std::vector<std::string>{
382 {"Invalid SMT response \"" + id2string(parse_tree.id()) + "\""}}};
383}
mp_integer power(const mp_integer &base, const mp_integer &exponent)
A multi-precision implementation of the power operator.
ait supplies three of the four components needed: an abstract interpreter (in this case handling func...
Definition ai.h:562
There are a large number of kinds of tree structured or tree-like data in CPROVER.
Definition irep.h:364
std::string pretty(unsigned indent=0, unsigned max_indent=0) const
Definition irep.cpp:482
subt & get_sub()
Definition irep.h:448
const irep_idt & id() const
Definition irep.h:388
static const smt_function_application_termt::factoryt< selectt > select
int toupper(int c)
Definition ctype.c:134
const std::string & id2string(const irep_idt &d)
Definition irep.h:44
const mp_integer string2integer(const std::string &n, unsigned base)
Definition mp_arith.cpp:54
resultt
The result of goto verifying.
Definition properties.h:45
Ranges: pair of begin and end iterators, which can be initialized from containers,...
ranget< iteratort > make_range(iteratort begin, iteratort end)
Definition range.h:522
static std::optional< smt_termt > valid_smt_bool(const irept &parse_tree)
response_or_errort< smt_responset > validate_smt_response(const irept &parse_tree, const std::unordered_map< irep_idt, smt_identifier_termt > &identifier_table)
static std::string print_parse_tree(const irept &parse_tree)
Produces a human-readable representation of the given parse_tree, for use in error messaging.
void collect_error_messages_impl(std::vector< std::string > &collected_error_messages, argumentt &&argument)
static std::optional< smt_termt > valid_smt_bit_vector_constant(const irept &parse_tree)
static std::optional< smt_termt > valid_smt_binary(const std::string &text)
static response_or_errort< smt_termt > validate_term(const irept &parse_tree, const std::unordered_map< irep_idt, smt_identifier_termt > &identifier_table)
static std::optional< smt_termt > valid_smt_hex(const std::string &text)
static bool all_subs_are_pairs(const irept &parse_tree)
std::vector< std::string > collect_error_messages(argumentst &&... arguments)
Builds a collection of error messages composed all error messages in the response_or_errort typed arg...
static std::optional< smt_termt > valid_smt_indexed_bit_vector(const irept &parse_tree)
Checks for valid bit vector constants of the form (_ bv(value) (width)) for example - (_ bv4 64).
static response_or_errort< smt_get_value_responset::valuation_pairt > validate_valuation_pair(const irept &pair_parse_tree, const std::unordered_map< irep_idt, smt_identifier_termt > &identifier_table)
static std::optional< response_or_errort< smt_responset > > valid_smt_error_response(const irept &parse_tree)
static response_or_errort< irep_idt > validate_string_literal(const irept &parse_tree)
static std::optional< response_or_errort< smt_termt > > try_select_validation(const irept &parse_tree, const std::unordered_map< irep_idt, smt_identifier_termt > &identifier_table)
static std::optional< response_or_errort< smt_responset > > valid_smt_get_value_response(const irept &parse_tree, const std::unordered_map< irep_idt, smt_identifier_termt > &identifier_table)
response_or_errort< smt_baset > validation_propagating(argumentst &&... arguments)
Given a class to construct and a set of arguments to its constructor which may include errors,...
BigInt mp_integer
Definition smt_terms.h:17
std::string smt_to_smt2_string(const smt_indext &index)
Streaming SMT data structures to a string based output stream.
#define PRECONDITION(CONDITION)
Definition invariant.h:463
#define INVARIANT(CONDITION, REASON)
This macro uses the wrapper function 'invariant_violated_string'.
Definition invariant.h:423