CBMC
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 
24 #include "smt_array_theory.h"
25 #include "smt_to_smt2_string.h"
26 
27 #include <regex>
28 
30  const irept &parse_tree,
31  const std::unordered_map<irep_idt, smt_identifier_termt> &identifier_table);
32 
33 // Implementation detail of `collect_error_messages` below.
34 template <typename argumentt, typename... argumentst>
36  std::vector<std::string> &collected_error_messages,
37  argumentt &&argument)
38 {
39  if(const auto messages = argument.get_if_error())
40  {
41  collected_error_messages.insert(
42  collected_error_messages.end(), messages->cbegin(), messages->end());
43  }
44 }
45 
46 // Implementation detail of `collect_error_messages` below.
47 template <typename argumentt, typename... argumentst>
49  std::vector<std::string> &collected_error_messages,
50  argumentt &&argument,
51  argumentst &&... arguments)
52 {
53  collect_error_messages_impl(collected_error_messages, argument);
54  collect_error_messages_impl(collected_error_messages, arguments...);
55 }
56 
61 template <typename... argumentst>
62 std::vector<std::string> collect_error_messages(argumentst &&... arguments)
63 {
64  std::vector<std::string> collected_error_messages;
65  collect_error_messages_impl(collected_error_messages, arguments...);
66  return collected_error_messages;
67 }
68 
83 template <
84  typename smt_to_constructt,
85  typename smt_baset = smt_to_constructt,
86  typename... argumentst>
88 {
89  const auto collected_error_messages = collect_error_messages(arguments...);
90  if(!collected_error_messages.empty())
91  return response_or_errort<smt_baset>(collected_error_messages);
92  else
93  {
95  smt_to_constructt{(*arguments.get_if_valid())...}};
96  }
97 }
98 
105 static std::string print_parse_tree(const irept &parse_tree)
106 {
107  return parse_tree.pretty(0, 0);
108 }
109 
111 validate_string_literal(const irept &parse_tree)
112 {
113  if(!parse_tree.get_sub().empty())
114  {
116  "Expected string literal, found \"" + print_parse_tree(parse_tree) +
117  "\".");
118  }
119  return response_or_errort<irep_idt>{parse_tree.id()};
120 }
121 
128 valid_smt_error_response(const irept &parse_tree)
129 {
130  // Check if the parse tree looks to be an error response.
131  if(!parse_tree.id().empty())
132  return {};
133  if(parse_tree.get_sub().empty())
134  return {};
135  if(parse_tree.get_sub().at(0).id() != "error")
136  return {};
137  // Parse tree is now considered to be an error response and anything
138  // unexpected in the parse tree is now considered to be an invalid response.
139  if(parse_tree.get_sub().size() == 1)
140  {
142  "Error response is missing the error message."}};
143  }
144  if(parse_tree.get_sub().size() > 2)
145  {
147  "Error response has multiple error messages - \"" +
148  print_parse_tree(parse_tree) + "\"."}};
149  }
150  return validation_propagating<smt_error_responset, smt_responset>(
151  validate_string_literal(parse_tree.get_sub()[1]));
152 }
153 
154 static bool all_subs_are_pairs(const irept &parse_tree)
155 {
156  return std::all_of(
157  parse_tree.get_sub().cbegin(),
158  parse_tree.get_sub().cend(),
159  [](const irept &sub) { return sub.get_sub().size() == 2; });
160 }
161 
166 {
167  if(parse_tree.get_sub().size() != 3)
168  return {};
169  if(parse_tree.get_sub().at(0).id() != "_")
170  return {};
171  const auto value_string = id2string(parse_tree.get_sub().at(1).id());
172  std::smatch match_results;
173  static const std::regex bv_value_regex{R"(^bv(\d+)$)", std::regex::optimize};
174  if(!std::regex_search(value_string, match_results, bv_value_regex))
175  return {};
176  INVARIANT(
177  match_results.size() == 2,
178  "Match results should include digits sub-expression if regex is matched.");
179  const std::string value_digits = match_results[1];
180  const auto value = string2integer(value_digits);
181  const auto bit_width_string = id2string(parse_tree.get_sub().at(2).id());
182  const auto bit_width =
183  numeric_cast_v<std::size_t>(string2integer(bit_width_string));
184  if(bit_width == 0)
185  return {};
186  if(value >= power(mp_integer{2}, bit_width))
187  return {};
188  return smt_bit_vector_constant_termt{value, bit_width};
189 }
190 
191 static optionalt<smt_termt> valid_smt_bool(const irept &parse_tree)
192 {
193  if(!parse_tree.get_sub().empty())
194  return {};
195  if(parse_tree.id() == ID_true)
196  return {smt_bool_literal_termt{true}};
197  if(parse_tree.id() == ID_false)
198  return {smt_bool_literal_termt{false}};
199  return {};
200 }
201 
202 static optionalt<smt_termt> valid_smt_binary(const std::string &text)
203 {
204  static const std::regex binary_format{"#b[01]+"};
205  if(!std::regex_match(text, binary_format))
206  return {};
207  const mp_integer value = string2integer({text.begin() + 2, text.end()}, 2);
208  // Width is number of bit values minus the "#b" prefix.
209  const std::size_t width = text.size() - 2;
210  return {smt_bit_vector_constant_termt{value, width}};
211 }
212 
213 static optionalt<smt_termt> valid_smt_hex(const std::string &text)
214 {
215  static const std::regex hex_format{"#x[0-9A-Za-z]+"};
216  if(!std::regex_match(text, hex_format))
217  return {};
218  const std::string hex{text.begin() + 2, text.end()};
219  // SMT-LIB 2 allows hex characters to be upper or lower case, but they should
220  // be upper case for mp_integer.
221  const mp_integer value =
222  string2integer(make_range(hex).map<std::function<int(int)>>(toupper), 16);
223  const std::size_t width = (text.size() - 2) * 4;
224  return {smt_bit_vector_constant_termt{value, width}};
225 }
226 
229 {
230  if(const auto indexed = valid_smt_indexed_bit_vector(parse_tree))
231  return *indexed;
232  if(!parse_tree.get_sub().empty() || parse_tree.id().empty())
233  return {};
234  const auto value_string = id2string(parse_tree.id());
235  if(const auto smt_binary = valid_smt_binary(value_string))
236  return *smt_binary;
237  if(const auto smt_hex = valid_smt_hex(value_string))
238  return *smt_hex;
239  return {};
240 }
241 
243  const irept &parse_tree,
244  const std::unordered_map<irep_idt, smt_identifier_termt> &identifier_table)
245 {
246  if(parse_tree.get_sub().empty())
247  return {};
248  if(parse_tree.get_sub()[0].id() != "select")
249  return {};
250  if(parse_tree.get_sub().size() != 3)
251  {
253  "\"select\" is expected to have 2 arguments, but " +
254  std::to_string(parse_tree.get_sub().size()) +
255  " arguments were found - \"" + print_parse_tree(parse_tree) + "\"."};
256  }
257  const auto array = validate_term(parse_tree.get_sub()[1], identifier_table);
258  const auto index = validate_term(parse_tree.get_sub()[2], identifier_table);
259  const auto error_messages = collect_error_messages(array, index);
260  if(!error_messages.empty())
261  return response_or_errort<smt_termt>{error_messages};
262  return {smt_array_theoryt::select.validation(
263  *array.get_if_valid(), *index.get_if_valid())};
264 }
265 
267  const irept &parse_tree,
268  const std::unordered_map<irep_idt, smt_identifier_termt> &identifier_table)
269 {
270  if(const auto smt_bool = valid_smt_bool(parse_tree))
271  return response_or_errort<smt_termt>{*smt_bool};
272  if(const auto bit_vector_constant = valid_smt_bit_vector_constant(parse_tree))
273  return response_or_errort<smt_termt>{*bit_vector_constant};
274  const auto find_result = identifier_table.find(parse_tree.id());
275  if(find_result != identifier_table.end())
276  return response_or_errort<smt_termt>{find_result->second};
277  if(
278  const auto select_validation =
279  try_select_validation(parse_tree, identifier_table))
280  {
281  return *select_validation;
282  }
283  return response_or_errort<smt_termt>{"Unrecognised SMT term - \"" +
284  print_parse_tree(parse_tree) + "\"."};
285 }
286 
289  const irept &pair_parse_tree,
290  const std::unordered_map<irep_idt, smt_identifier_termt> &identifier_table)
291 {
293  PRECONDITION(pair_parse_tree.get_sub().size() == 2);
294  const auto descriptor_validation =
295  validate_term(pair_parse_tree.get_sub()[0], identifier_table);
296  const auto value_validation =
297  validate_term(pair_parse_tree.get_sub()[1], identifier_table);
298  const auto error_messages =
299  collect_error_messages(descriptor_validation, value_validation);
300  if(!error_messages.empty())
301  return resultt{error_messages};
302  const auto &valid_descriptor = *descriptor_validation.get_if_valid();
303  const auto &valid_value = *value_validation.get_if_valid();
304  if(valid_descriptor.get_sort() != valid_value.get_sort())
305  {
306  return resultt{
307  "Mismatched descriptor and value sorts in - " +
308  print_parse_tree(pair_parse_tree) + "\nDescriptor has sort " +
309  smt_to_smt2_string(valid_descriptor.get_sort()) + "\nValue has sort " +
310  smt_to_smt2_string(valid_value.get_sort())};
311  }
312  return resultt{{valid_descriptor, valid_value}};
313 }
314 
322  const irept &parse_tree,
323  const std::unordered_map<irep_idt, smt_identifier_termt> &identifier_table)
324 {
325  // Shape matching for does this look like a get value response?
326  if(!parse_tree.id().empty())
327  return {};
328  if(parse_tree.get_sub().empty())
329  return {};
330  if(!all_subs_are_pairs(parse_tree))
331  return {};
332  std::vector<std::string> error_messages;
333  std::vector<smt_get_value_responset::valuation_pairt> valuation_pairs;
334  for(const auto &pair : parse_tree.get_sub())
335  {
336  const auto pair_validation_result =
337  validate_valuation_pair(pair, identifier_table);
338  if(const auto error = pair_validation_result.get_if_error())
339  error_messages.insert(error_messages.end(), error->begin(), error->end());
340  if(const auto valid_pair = pair_validation_result.get_if_valid())
341  valuation_pairs.push_back(*valid_pair);
342  }
343  if(!error_messages.empty())
344  return {response_or_errort<smt_responset>{std::move(error_messages)}};
345  else
346  {
348  smt_get_value_responset{valuation_pairs}}};
349  }
350 }
351 
353  const irept &parse_tree,
354  const std::unordered_map<irep_idt, smt_identifier_termt> &identifier_table)
355 {
356  if(parse_tree.id() == "sat")
359  if(parse_tree.id() == "unsat")
362  if(parse_tree.id() == "unknown")
365  if(const auto error_response = valid_smt_error_response(parse_tree))
366  return *error_response;
367  if(parse_tree.id() == "success")
369  if(parse_tree.id() == "unsupported")
371  if(
372  const auto get_value_response =
373  valid_smt_get_value_response(parse_tree, identifier_table))
374  {
375  return *get_value_response;
376  }
377  return response_or_errort<smt_responset>{"Invalid SMT response \"" +
378  id2string(parse_tree.id()) + "\""};
379 }
valid_smt_hex
static optionalt< smt_termt > valid_smt_hex(const std::string &text)
Definition: smt_response_validation.cpp:213
valid_smt_bool
static optionalt< smt_termt > valid_smt_bool(const irept &parse_tree)
Definition: smt_response_validation.cpp:191
mp_integer
BigInt mp_integer
Definition: smt_terms.h:17
response_or_errort
Holds either a valid parsed response or response sub-tree of type.
Definition: response_or_error.h:16
arith_tools.h
mp_arith.h
resultt
resultt
The result of goto verifying.
Definition: properties.h:44
smt_sat_responset
Definition: smt_responses.h:68
irept::pretty
std::string pretty(unsigned indent=0, unsigned max_indent=0) const
Definition: irep.cpp:495
valid_smt_indexed_bit_vector
static optionalt< 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).
Definition: smt_response_validation.cpp:165
smt_unsupported_responset
Definition: smt_responses.h:123
string2integer
const mp_integer string2integer(const std::string &n, unsigned base)
Definition: mp_arith.cpp:54
smt_array_theoryt::select
static const smt_function_application_termt::factoryt< selectt > select
Definition: smt_array_theory.h:20
validate_string_literal
static response_or_errort< irep_idt > validate_string_literal(const irept &parse_tree)
Definition: smt_response_validation.cpp:111
valid_smt_bit_vector_constant
static optionalt< smt_termt > valid_smt_bit_vector_constant(const irept &parse_tree)
Definition: smt_response_validation.cpp:228
to_string
std::string to_string(const string_not_contains_constraintt &expr)
Used for debug printing.
Definition: string_constraint.cpp:58
validation_propagating
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,...
Definition: smt_response_validation.cpp:87
smt_get_value_responset
Definition: smt_responses.h:95
smt_response_validation.h
collect_error_messages
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...
Definition: smt_response_validation.cpp:62
id2string
const std::string & id2string(const irep_idt &d)
Definition: irep.h:47
validate_smt_response
response_or_errort< smt_responset > validate_smt_response(const irept &parse_tree, const std::unordered_map< irep_idt, smt_identifier_termt > &identifier_table)
Definition: smt_response_validation.cpp:352
PRECONDITION
#define PRECONDITION(CONDITION)
Definition: invariant.h:463
validate_valuation_pair
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)
Definition: smt_response_validation.cpp:288
try_select_validation
static optionalt< response_or_errort< smt_termt > > try_select_validation(const irept &parse_tree, const std::unordered_map< irep_idt, smt_identifier_termt > &identifier_table)
Definition: smt_response_validation.cpp:242
smt_unsat_responset
Definition: smt_responses.h:74
validate_term
static response_or_errort< smt_termt > validate_term(const irept &parse_tree, const std::unordered_map< irep_idt, smt_identifier_termt > &identifier_table)
Definition: smt_response_validation.cpp:266
irept::id
const irep_idt & id() const
Definition: irep.h:396
range.h
dstringt::empty
bool empty() const
Definition: dstring.h:88
all_subs_are_pairs
static bool all_subs_are_pairs(const irept &parse_tree)
Definition: smt_response_validation.cpp:154
smt_check_sat_responset
Definition: smt_responses.h:86
optionalt
nonstd::optional< T > optionalt
Definition: optional.h:35
print_parse_tree
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.
Definition: smt_response_validation.cpp:105
smt_to_smt2_string
std::string smt_to_smt2_string(const smt_indext &index)
Definition: smt_to_smt2_string.cpp:53
valid_smt_get_value_response
static optionalt< 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)
Definition: smt_response_validation.cpp:321
irept::get_sub
subt & get_sub()
Definition: irep.h:456
power
mp_integer power(const mp_integer &base, const mp_integer &exponent)
A multi-precision implementation of the power operator.
Definition: arith_tools.cpp:195
smt_bool_literal_termt
Definition: smt_terms.h:76
irept
There are a large number of kinds of tree structured or tree-like data in CPROVER.
Definition: irep.h:359
collect_error_messages_impl
void collect_error_messages_impl(std::vector< std::string > &collected_error_messages, argumentt &&argument)
Definition: smt_response_validation.cpp:35
INVARIANT
#define INVARIANT(CONDITION, REASON)
This macro uses the wrapper function 'invariant_violated_string'.
Definition: invariant.h:423
smt_bit_vector_constant_termt
Definition: smt_terms.h:116
smt_to_smt2_string.h
smt_array_theory.h
make_range
ranget< iteratort > make_range(iteratort begin, iteratort end)
Definition: range.h:524
smt_unknown_responset
Definition: smt_responses.h:80
valid_smt_error_response
static optionalt< response_or_errort< smt_responset > > valid_smt_error_response(const irept &parse_tree)
Definition: smt_response_validation.cpp:128
valid_smt_binary
static optionalt< smt_termt > valid_smt_binary(const std::string &text)
Definition: smt_response_validation.cpp:202
smt_success_responset
Definition: smt_responses.h:28