CBMC
string_refinement.cpp
Go to the documentation of this file.
1 /*******************************************************************\
2 
3 Module: String support via creating string constraints and progressively
4  instantiating the universal constraints as needed.
5  The procedure is described in the PASS paper at HVC'13:
6  "PASS: String Solving with Parameterized Array and Interval Automaton"
7  by Guodong Li and Indradeep Ghosh.
8 
9 Author: Alberto Griggio, alberto.griggio@gmail.com
10 
11 \*******************************************************************/
12 
19 
20 #include "string_refinement.h"
21 
22 #include <solvers/sat/satcheck.h>
23 #include <stack>
24 #include <unordered_set>
25 
26 #include <util/expr_iterator.h>
27 #include <util/expr_util.h>
28 #include <util/format_type.h>
29 #include <util/magic.h>
30 #include <util/range.h>
31 #include <util/simplify_expr.h>
32 
35 #include "string_dependencies.h"
37 
39  messaget::mstreamt &stream,
40  const namespacet &ns,
41  const string_constraintt &constraint);
42 
43 static std::optional<exprt> find_counter_example(
44  const namespacet &ns,
45  const exprt &axiom,
46  const symbol_exprt &var,
47  message_handlert &message_handler);
48 
65 static std::pair<bool, std::vector<exprt>> check_axioms(
66  const string_axiomst &axioms,
68  const std::function<exprt(const exprt &)> &get,
69  messaget::mstreamt &stream,
70  const namespacet &ns,
71  bool use_counter_example,
72  const union_find_replacet &symbol_resolve,
73  const std::unordered_map<string_not_contains_constraintt, symbol_exprt>
74  &not_contain_witnesses);
75 
76 static void initial_index_set(
77  index_set_pairt &index_set,
78  const namespacet &ns,
79  const string_constraintt &axiom);
80 
81 static void initial_index_set(
82  index_set_pairt &index_set,
83  const namespacet &ns,
84  const string_not_contains_constraintt &axiom);
85 
86 static void initial_index_set(
87  index_set_pairt &index_set,
88  const namespacet &ns,
89  const string_axiomst &axioms);
90 
91 exprt simplify_sum(const exprt &f);
92 
93 static void update_index_set(
94  index_set_pairt &index_set,
95  const namespacet &ns,
96  const std::vector<exprt> &current_constraints);
97 
98 static void update_index_set(
99  index_set_pairt &index_set,
100  const namespacet &ns,
101  const exprt &formula);
102 
103 static std::vector<exprt> instantiate(
104  const string_not_contains_constraintt &axiom,
105  const index_set_pairt &index_set,
106  const std::unordered_map<string_not_contains_constraintt, symbol_exprt>
107  &witnesses);
108 
109 static std::optional<exprt> get_array(
110  const std::function<exprt(const exprt &)> &super_get,
111  const namespacet &ns,
112  messaget::mstreamt &stream,
113  const array_string_exprt &arr,
114  const array_poolt &array_pool);
115 
116 static std::optional<exprt> substitute_array_access(
117  const index_exprt &index_expr,
118  symbol_generatort &symbol_generator,
119  const bool left_propagate);
120 
128 template <typename T>
129 static std::vector<T>
130 fill_in_map_as_vector(const std::map<std::size_t, T> &index_value)
131 {
132  std::vector<T> result;
133  if(!index_value.empty())
134  {
135  result.resize(index_value.rbegin()->first + 1);
136  for(auto it = index_value.rbegin(); it != index_value.rend(); ++it)
137  {
138  const std::size_t index = it->first;
139  const T &value = it->second;
140  const auto next = std::next(it);
141  const std::size_t leftmost_index_to_pad =
142  next != index_value.rend() ? next->first + 1 : 0;
143  for(std::size_t j = leftmost_index_to_pad; j <= index; j++)
144  result[j] = value;
145  }
146  }
147  return result;
148 }
149 
150 static bool validate(const string_refinementt::infot &info)
151 {
152  PRECONDITION(info.ns);
153  PRECONDITION(info.prop);
154  return true;
155 }
156 
158  : supert(info),
159  config_(info),
160  loop_bound_(info.refinement_bound),
161  generator(*info.ns, *info.message_handler)
162 {
163 }
164 
166  : string_refinementt(info, validate(info))
167 {
168 }
169 
171 static void
173 {
174  std::size_t count = 0;
175  std::size_t count_current = 0;
176  for(const auto &i : index_set.cumulative)
177  {
178  const exprt &s = i.first;
179  stream << "IS(" << format(s) << ")=={" << messaget::eom;
180 
181  for(const auto &j : i.second)
182  {
183  const auto it = index_set.current.find(i.first);
184  if(
185  it != index_set.current.end() && it->second.find(j) != it->second.end())
186  {
187  count_current++;
188  stream << "**";
189  }
190  stream << " " << format(j) << ";" << messaget::eom;
191  count++;
192  }
193  stream << "}" << messaget::eom;
194  }
195  stream << count << " elements in index set (" << count_current
196  << " newly added)" << messaget::eom;
197 }
198 
220 static std::vector<exprt> generate_instantiations(
221  const index_set_pairt &index_set,
222  const string_axiomst &axioms,
223  const std::unordered_map<string_not_contains_constraintt, symbol_exprt>
224  &not_contain_witnesses)
225 {
226  std::vector<exprt> lemmas;
227  for(const auto &i : index_set.current)
228  {
229  for(const auto &univ_axiom : axioms.universal)
230  {
231  for(const auto &j : i.second)
232  lemmas.push_back(instantiate(univ_axiom, i.first, j));
233  }
234  }
235  for(const auto &nc_axiom : axioms.not_contains)
236  {
237  for(const auto &instance :
238  instantiate(nc_axiom, index_set, not_contain_witnesses))
239  lemmas.push_back(instance);
240  }
241  return lemmas;
242 }
243 
248  string_constraint_generatort &generator,
249  exprt &expr)
250 {
251  if(const auto equal_expr = expr_try_dynamic_cast<equal_exprt>(expr))
252  {
253  if(
254  const auto fun_app = expr_try_dynamic_cast<function_application_exprt>(
255  as_const(equal_expr->rhs())))
256  {
257  const auto new_equation =
258  generator.make_array_pointer_association(equal_expr->lhs(), *fun_app);
259  if(new_equation)
260  {
261  expr =
262  equal_exprt{from_integer(true, new_equation->type()), *new_equation};
263  }
264  }
265  }
266 }
267 
272 static exprt
273 replace_expr_copy(const union_find_replacet &symbol_resolve, exprt expr)
274 {
275  symbol_resolve.replace_expr(expr);
276  return expr;
277 }
278 
283 void string_refinementt::set_to(const exprt &expr, bool value)
284 {
285  PRECONDITION(expr.is_boolean());
287  if(!value)
288  equations.push_back(not_exprt{expr});
289  else
290  equations.push_back(expr);
291 }
292 
301  union_find_replacet &symbol_solver,
302  const std::vector<exprt> &equations,
303  const namespacet &ns,
304  messaget::mstreamt &stream)
305 {
306  const std::string log_message =
307  "WARNING string_refinement.cpp generate_symbol_resolution_from_equations:";
308  auto equalities = make_range(equations).filter(
309  [&](const exprt &e) { return can_cast_expr<equal_exprt>(e); });
310  for(const exprt &e : equalities)
311  {
312  const equal_exprt &eq = to_equal_expr(e);
313  const exprt &lhs = to_equal_expr(eq).lhs();
314  const exprt &rhs = to_equal_expr(eq).rhs();
315  if(lhs.id() != ID_symbol)
316  {
317  stream << log_message << "non symbol lhs: " << format(lhs)
318  << " with rhs: " << format(rhs) << messaget::eom;
319  continue;
320  }
321 
322  if(lhs.type() != rhs.type())
323  {
324  stream << log_message << "non equal types lhs: " << format(lhs)
325  << "\n####################### rhs: " << format(rhs)
326  << messaget::eom;
327  continue;
328  }
329 
330  if(is_char_pointer_type(rhs.type()))
331  {
332  symbol_solver.make_union(lhs, simplify_expr(rhs, ns));
333  }
334  else if(rhs.id() == ID_function_application)
335  {
336  // function applications can be ignored because they will be replaced
337  // in the convert_function_application step of dec_solve
338  }
339  else if(
340  lhs.type().id() != ID_pointer && has_char_pointer_subtype(lhs.type(), ns))
341  {
342  if(rhs.type().id() == ID_struct || rhs.type().id() == ID_struct_tag)
343  {
344  const struct_typet &struct_type =
345  rhs.type().id() == ID_struct_tag
346  ? ns.follow_tag(to_struct_tag_type(rhs.type()))
347  : to_struct_type(rhs.type());
348  for(const auto &comp : struct_type.components())
349  {
350  if(is_char_pointer_type(comp.type()))
351  {
352  const member_exprt lhs_data(lhs, comp.get_name(), comp.type());
353  const exprt rhs_data = simplify_expr(
354  member_exprt(rhs, comp.get_name(), comp.type()), ns);
355  symbol_solver.make_union(lhs_data, rhs_data);
356  }
357  }
358  }
359  else
360  {
361  stream << log_message << "non struct with char pointer subexpr "
362  << format(rhs) << "\n * of type " << format(rhs.type())
363  << messaget::eom;
364  }
365  }
366  }
367 }
368 
375 static std::vector<exprt>
377 {
378  std::vector<exprt> result;
379  if(lhs.type() == string_typet())
380  result.push_back(lhs);
381  else if(lhs.type().id() == ID_struct || lhs.type().id() == ID_struct_tag)
382  {
383  const struct_typet &struct_type =
384  lhs.type().id() == ID_struct_tag
385  ? ns.follow_tag(to_struct_tag_type(lhs.type()))
386  : to_struct_type(lhs.type());
387  for(const auto &comp : struct_type.components())
388  {
389  const std::vector<exprt> strings_in_comp = extract_strings_from_lhs(
390  member_exprt(lhs, comp.get_name(), comp.type()), ns);
391  result.insert(
392  result.end(), strings_in_comp.begin(), strings_in_comp.end());
393  }
394  }
395  return result;
396 }
397 
403 static std::vector<exprt>
404 extract_strings(const exprt &expr, const namespacet &ns)
405 {
406  std::vector<exprt> result;
407  for(auto it = expr.depth_begin(); it != expr.depth_end();)
408  {
409  if(it->type() == string_typet() && it->id() != ID_if)
410  {
411  result.push_back(*it);
412  it.next_sibling_or_parent();
413  }
414  else if(it->id() == ID_symbol)
415  {
416  for(const exprt &e : extract_strings_from_lhs(*it, ns))
417  result.push_back(e);
418  it.next_sibling_or_parent();
419  }
420  else
421  ++it;
422  }
423  return result;
424 }
425 
433  const equal_exprt &eq,
434  union_find_replacet &symbol_resolve,
435  const namespacet &ns)
436 {
437  if(eq.rhs().type() == string_typet())
438  {
439  symbol_resolve.make_union(eq.lhs(), simplify_expr(eq.rhs(), ns));
440  }
441  else if(has_subtype(eq.lhs().type(), ID_string, ns))
442  {
443  if(
444  eq.rhs().type().id() == ID_struct ||
445  eq.rhs().type().id() == ID_struct_tag)
446  {
447  const struct_typet &struct_type =
448  eq.rhs().type().id() == ID_struct_tag
449  ? ns.follow_tag(to_struct_tag_type(eq.rhs().type()))
450  : to_struct_type(eq.rhs().type());
451  for(const auto &comp : struct_type.components())
452  {
453  const member_exprt lhs_data(eq.lhs(), comp.get_name(), comp.type());
454  const exprt rhs_data = simplify_expr(
455  member_exprt(eq.rhs(), comp.get_name(), comp.type()), ns);
457  equal_exprt(lhs_data, rhs_data), symbol_resolve, ns);
458  }
459  }
460  }
461 }
462 
471  const std::vector<equal_exprt> &equations,
472  const namespacet &ns,
473  messaget::mstreamt &stream)
474 {
475  const std::string log_message =
476  "WARNING string_refinement.cpp "
477  "string_identifiers_resolution_from_equations:";
478 
479  equation_symbol_mappingt equation_map;
480 
481  // Indexes of equations that need to be added to the result
482  std::unordered_set<size_t> required_equations;
483  std::stack<size_t> equations_to_treat;
484 
485  for(std::size_t i = 0; i < equations.size(); ++i)
486  {
487  const equal_exprt &eq = equations[i];
488  if(eq.rhs().id() == ID_function_application)
489  {
490  if(required_equations.insert(i).second)
491  equations_to_treat.push(i);
492 
493  std::vector<exprt> rhs_strings = extract_strings(eq.rhs(), ns);
494  for(const auto &expr : rhs_strings)
495  equation_map.add(i, expr);
496  }
497  else if(
498  eq.lhs().type().id() != ID_pointer &&
499  has_subtype(eq.lhs().type(), ID_string, ns))
500  {
501  std::vector<exprt> lhs_strings = extract_strings_from_lhs(eq.lhs(), ns);
502 
503  for(const auto &expr : lhs_strings)
504  equation_map.add(i, expr);
505 
506  if(lhs_strings.empty())
507  {
508  stream << log_message << "non struct with string subtype "
509  << format(eq.lhs()) << "\n * of type "
510  << format(eq.lhs().type()) << messaget::eom;
511  }
512 
513  for(const exprt &expr : extract_strings(eq.rhs(), ns))
514  equation_map.add(i, expr);
515  }
516  }
517 
518  // transitively add all equations which depend on the equations to treat
519  while(!equations_to_treat.empty())
520  {
521  const std::size_t i = equations_to_treat.top();
522  equations_to_treat.pop();
523  for(const exprt &string : equation_map.find_expressions(i))
524  {
525  for(const std::size_t j : equation_map.find_equations(string))
526  {
527  if(required_equations.insert(j).second)
528  equations_to_treat.push(j);
529  }
530  }
531  }
532 
533  union_find_replacet result;
534  for(const std::size_t i : required_equations)
535  add_string_equation_to_symbol_resolution(equations[i], result, ns);
536 
537  return result;
538 }
539 
540 #ifdef DEBUG
542 static void
543 output_equations(std::ostream &output, const std::vector<exprt> &equations)
544 {
545  for(std::size_t i = 0; i < equations.size(); ++i)
546  output << " [" << i << "] " << format(equations[i]) << std::endl;
547 }
548 #endif
549 
560 // NOLINTNEXTLINE
563 // NOLINTNEXTLINE
568 // NOLINTNEXTLINE
616 {
617 #ifdef DEBUG
618  log.debug() << "dec_solve: Initial set of equations" << messaget::eom;
619  output_equations(log.debug(), equations);
620 #endif
621 
622  log.debug() << "dec_solve: Build symbol solver from equations"
623  << messaget::eom;
624  // symbol_resolve is used by get and is kept between calls to dec_solve,
625  // that's why we use a class member here
628 #ifdef DEBUG
629  log.debug() << "symbol resolve:" << messaget::eom;
630  for(const auto &pair : symbol_resolve.to_vector())
631  log.debug() << format(pair.first) << " --> " << format(pair.second)
632  << messaget::eom;
633 #endif
634 
635  const union_find_replacet string_id_symbol_resolve =
637  [&] {
638  std::vector<equal_exprt> equalities;
639  for(const auto &eq : equations)
640  {
641  if(auto equal_expr = expr_try_dynamic_cast<equal_exprt>(eq))
642  equalities.push_back(*equal_expr);
643  }
644  return equalities;
645  }(),
646  ns,
647  log.debug());
648 #ifdef DEBUG
649  log.debug() << "symbol resolve string:" << messaget::eom;
650  for(const auto &pair : string_id_symbol_resolve.to_vector())
651  {
652  log.debug() << format(pair.first) << " --> " << format(pair.second)
653  << messaget::eom;
654  }
655 #endif
656 
657  log.debug() << "dec_solve: Replacing string ids and simplifying arguments"
658  " in function applications"
659  << messaget::eom;
660  for(exprt &expr : equations)
661  {
662  auto it = expr.depth_begin();
663  while(it != expr.depth_end())
664  {
666  {
667  // Simplification is required because the array pool may not realize
668  // that an expression like
669  // `(unsignedbv[16]*)((signedbv[8]*)&constarray[0] + 0)` is the
670  // same pointer as `&constarray[0]
671  simplify(it.mutate(), ns);
672  string_id_symbol_resolve.replace_expr(it.mutate());
673  it.next_sibling_or_parent();
674  }
675  else
676  ++it;
677  }
678  }
679 
680  // Constraints start clear at each `dec_solve` call.
681  string_constraintst constraints;
682  for(auto &expr : equations)
684 
685 #ifdef DEBUG
686  output_equations(log.debug(), equations);
687 #endif
688 
689  log.debug() << "dec_solve: compute dependency graph and remove function "
690  << "applications captured by the dependencies:" << messaget::eom;
691  std::vector<exprt> local_equations;
692  for(const exprt &eq : equations)
693  {
694  // Ensures that arrays that are equal, are associated to the same nodes
695  // in the graph.
696  const exprt eq_with_char_array_replaced_with_representative_elements =
698  const std::optional<exprt> new_equation = add_node(
699  dependencies,
700  eq_with_char_array_replaced_with_representative_elements,
703  if(new_equation)
704  local_equations.push_back(*new_equation);
705  else
706  local_equations.push_back(eq);
707  }
708  equations.clear();
709 
710 #ifdef DEBUG
712 #endif
713 
714  log.debug() << "dec_solve: add constraints" << messaget::eom;
715  merge(
716  constraints,
718 
719 #ifdef DEBUG
720  output_equations(log.debug(), equations);
721 #endif
722 
723 #ifdef DEBUG
724  log.debug() << "dec_solve: arrays_of_pointers:" << messaget::eom;
725  for(auto pair : generator.array_pool.get_arrays_of_pointers())
726  {
727  log.debug() << " * " << format(pair.first) << "\t--> "
728  << format(pair.second) << " : " << format(pair.second.type())
729  << messaget::eom;
730  }
731 #endif
732 
733  for(const auto &eq : local_equations)
734  {
735 #ifdef DEBUG
736  log.debug() << "dec_solve: set_to " << format(eq) << messaget::eom;
737 #endif
738  supert::set_to(eq, true);
739  }
740 
742  constraints.universal.begin(),
743  constraints.universal.end(),
744  std::back_inserter(axioms.universal),
745  [&](string_constraintt constraint) {
746  constraint.replace_expr(symbol_resolve);
747  DATA_INVARIANT(
748  is_valid_string_constraint(log.error(), ns, constraint),
749  string_refinement_invariantt(
750  "string constraints satisfy their invariant"));
751  return constraint;
752  });
753 
755  constraints.not_contains.begin(),
756  constraints.not_contains.end(),
757  std::back_inserter(axioms.not_contains),
759  replace(symbol_resolve, axiom);
760  return axiom;
761  });
762 
763  // Used to store information about witnesses for not_contains constraints
764  std::unordered_map<string_not_contains_constraintt, symbol_exprt>
765  not_contain_witnesses;
766  for(const auto &nc_axiom : axioms.not_contains)
767  {
768  const auto &witness_type = [&] {
769  const auto &rtype = to_array_type(nc_axiom.s0.type());
770  const typet &index_type = rtype.size().type();
771  return array_typet(index_type, infinity_exprt(index_type));
772  }();
773  not_contain_witnesses.emplace(
774  nc_axiom, generator.fresh_symbol("not_contains_witness", witness_type));
775  }
776 
777  for(const exprt &lemma : constraints.existential)
778  {
780  }
781 
782  // All generated strings should have non-negative length
783  for(const auto &pair : generator.array_pool.created_strings())
784  {
785  exprt length = generator.array_pool.get_or_create_length(pair.first);
786  add_lemma(
787  binary_relation_exprt{length, ID_ge, from_integer(0, length.type())});
788  }
789 
790  // Initial try without index set
791  const auto get = [this](const exprt &expr) { return this->get(expr); };
793  const decision_proceduret::resultt initial_result =
795  if(initial_result == resultt::D_SATISFIABLE)
796  {
797  bool satisfied;
798  std::vector<exprt> counter_examples;
799  std::tie(satisfied, counter_examples) = check_axioms(
800  axioms,
801  generator,
802  get,
803  log.debug(),
804  ns,
807  not_contain_witnesses);
808  if(satisfied)
809  {
810  log.debug() << "check_SAT: the model is correct" << messaget::eom;
811  return resultt::D_SATISFIABLE;
812  }
813  log.debug() << "check_SAT: got SAT but the model is not correct"
814  << messaget::eom;
815  }
816  else
817  {
818  log.debug() << "check_SAT: got UNSAT or ERROR" << messaget::eom;
819  return initial_result;
820  }
821 
824  current_constraints.clear();
825  const auto initial_instances =
826  generate_instantiations(index_sets, axioms, not_contain_witnesses);
827  for(const auto &instance : initial_instances)
828  {
830  }
831 
832  while((loop_bound_--) > 0)
833  {
835  const decision_proceduret::resultt refined_result =
837 
838  if(refined_result == resultt::D_SATISFIABLE)
839  {
840  bool satisfied;
841  std::vector<exprt> counter_examples;
842  std::tie(satisfied, counter_examples) = check_axioms(
843  axioms,
844  generator,
845  get,
846  log.debug(),
847  ns,
850  not_contain_witnesses);
851  if(satisfied)
852  {
853  log.debug() << "check_SAT: the model is correct" << messaget::eom;
854  return resultt::D_SATISFIABLE;
855  }
856 
857  log.debug()
858  << "check_SAT: got SAT but the model is not correct, refining..."
859  << messaget::eom;
860 
861  // Since the model is not correct although we got SAT, we need to refine
862  // the property we are checking by adding more indices to the index set,
863  // and instantiating universal formulas with this indices.
864  // We will then relaunch the solver with these added lemmas.
865  index_sets.current.clear();
867 
869 
870  if(index_sets.current.empty())
871  {
872  if(axioms.not_contains.empty())
873  {
874  log.error() << "dec_solve: current index set is empty, "
875  << "this should not happen" << messaget::eom;
876  return resultt::D_ERROR;
877  }
878  else
879  {
880  log.debug() << "dec_solve: current index set is empty, "
881  << "adding counter examples" << messaget::eom;
882  for(const auto &counter : counter_examples)
883  add_lemma(counter);
884  }
885  }
886  current_constraints.clear();
887  const auto instances =
888  generate_instantiations(index_sets, axioms, not_contain_witnesses);
889  for(const auto &instance : instances)
890  add_lemma(
892  }
893  else
894  {
895  log.debug() << "check_SAT: default return "
896  << static_cast<int>(refined_result) << messaget::eom;
897  return refined_result;
898  }
899  }
900  log.debug() << "string_refinementt::dec_solve reached the maximum number"
901  << "of steps allowed" << messaget::eom;
902  return resultt::D_ERROR;
903 }
909  const exprt &lemma,
910  const bool simplify_lemma)
911 {
912  if(!seen_instances.insert(lemma).second)
913  return;
914 
915  current_constraints.push_back(lemma);
916 
917  exprt simple_lemma = lemma;
918  if(simplify_lemma)
919  {
920  simplify(simple_lemma, ns);
921  }
922 
923  if(simple_lemma.is_true())
924  {
925 #if 0
926  log.debug() << "string_refinementt::add_lemma : tautology" << messaget::eom;
927 #endif
928  return;
929  }
930 
931  symbol_resolve.replace_expr(simple_lemma);
932 
933  // Replace empty arrays with array_of expression because the solver cannot
934  // handle empty arrays.
935  for(auto it = simple_lemma.depth_begin(); it != simple_lemma.depth_end();)
936  {
937  if(it->id() == ID_array && it->operands().empty())
938  {
939  it.mutate() = array_of_exprt(
940  from_integer(
942  to_array_type(it->type()));
943  it.next_sibling_or_parent();
944  }
945  else
946  ++it;
947  }
948 
949  log.debug() << "adding lemma " << format(simple_lemma) << messaget::eom;
950 
951  prop.l_set_to_true(convert(simple_lemma));
952 }
953 
968 static std::optional<exprt> get_valid_array_size(
969  const std::function<exprt(const exprt &)> &super_get,
970  const namespacet &ns,
971  messaget::mstreamt &stream,
972  const array_string_exprt &arr,
973  const array_poolt &array_pool)
974 {
975  const auto &size_from_pool = array_pool.get_length_if_exists(arr);
976  exprt size_val;
977  if(size_from_pool.has_value())
978  {
979  const exprt size = size_from_pool.value();
980  size_val = simplify_expr(super_get(size), ns);
981  if(!size_val.is_constant())
982  {
983  stream << "(sr::get_valid_array_size) string of unknown size: "
984  << format(size_val) << messaget::eom;
985  return {};
986  }
987  }
988  else if(to_array_type(arr.type()).size().is_constant())
989  size_val = simplify_expr(to_array_type(arr.type()).size(), ns);
990  else
991  return {};
992 
993  auto n_opt = numeric_cast<std::size_t>(size_val);
994  if(!n_opt)
995  {
996  stream << "(sr::get_valid_array_size) size is not valid" << messaget::eom;
997  return {};
998  }
999 
1000  return size_val;
1001 }
1002 
1012 static std::optional<exprt> get_array(
1013  const std::function<exprt(const exprt &)> &super_get,
1014  const namespacet &ns,
1015  messaget::mstreamt &stream,
1016  const array_string_exprt &arr,
1017  const array_poolt &array_pool)
1018 {
1019  const auto size =
1020  get_valid_array_size(super_get, ns, stream, arr, array_pool);
1021  if(!size.has_value())
1022  {
1023  return {};
1024  }
1025 
1026  const size_t n = numeric_cast<std::size_t>(size.value()).value();
1027 
1028  if(n > MAX_CONCRETE_STRING_SIZE)
1029  {
1030  stream << "(sr::get_valid_array_size) long string (size "
1031  << " = " << n << ") " << format(arr) << messaget::eom;
1032  stream << "(sr::get_valid_array_size) consider reducing "
1033  "max-nondet-string-length so "
1034  "that no string exceeds "
1036  << " in length and "
1037  "make sure all functions returning strings are loaded"
1038  << messaget::eom;
1039  stream << "(sr::get_valid_array_size) this can also happen on invalid "
1040  "object access"
1041  << messaget::eom;
1042  return nil_exprt();
1043  }
1044 
1045  const exprt arr_val = simplify_expr(super_get(arr), ns);
1046  const typet char_type = to_array_type(arr.type()).element_type();
1047  const typet &index_type = size.value().type();
1048 
1049  if(
1050  const auto &array = interval_sparse_arrayt::of_expr(
1052  return array->concretize(n, index_type);
1053  return {};
1054 }
1055 
1060 static std::string string_of_array(const array_exprt &arr)
1061 {
1062  if(arr.type().id() != ID_array)
1063  return std::string("");
1064 
1065  exprt size_expr = to_array_type(arr.type()).size();
1066  auto n = numeric_cast_v<std::size_t>(to_constant_expr(size_expr));
1067  return utf16_constant_array_to_java(arr, n);
1068 }
1069 
1079  const std::function<exprt(const exprt &)> &super_get,
1080  const namespacet &ns,
1081  messaget::mstreamt &stream,
1082  const array_string_exprt &arr,
1083  array_poolt &array_pool)
1084 {
1085  stream << "- " << format(arr) << ":\n";
1086  stream << std::string(4, ' ') << "- type: " << format(arr.type())
1087  << messaget::eom;
1088  const auto arr_model_opt = get_array(super_get, ns, stream, arr, array_pool);
1089  if(arr_model_opt)
1090  {
1091  stream << std::string(4, ' ') << "- char_array: " << format(*arr_model_opt)
1092  << '\n';
1093  stream << std::string(4, ' ')
1094  << "- type : " << format(arr_model_opt->type()) << messaget::eom;
1095  const exprt simple = simplify_expr(*arr_model_opt, ns);
1096  stream << std::string(4, ' ')
1097  << "- simplified_char_array: " << format(simple) << messaget::eom;
1098  if(
1099  const auto concretized_array = get_array(
1100  super_get, ns, stream, to_array_string_expr(simple), array_pool))
1101  {
1102  stream << std::string(4, ' ')
1103  << "- concretized_char_array: " << format(*concretized_array)
1104  << messaget::eom;
1105 
1106  if(
1107  const auto array_expr =
1108  expr_try_dynamic_cast<array_exprt>(*concretized_array))
1109  {
1110  stream << std::string(4, ' ') << "- as_string: \""
1111  << string_of_array(*array_expr) << "\"\n";
1112  }
1113  else
1114  stream << std::string(2, ' ') << "- warning: not an array"
1115  << messaget::eom;
1116  return *concretized_array;
1117  }
1118  return simple;
1119  }
1120  stream << std::string(4, ' ') << "- incomplete model" << messaget::eom;
1121  return arr;
1122 }
1123 
1127  const string_constraint_generatort &generator,
1128  messaget::mstreamt &stream,
1129  const namespacet &ns,
1130  const std::function<exprt(const exprt &)> &super_get,
1131  const std::vector<symbol_exprt> &symbols,
1132  array_poolt &array_pool)
1133 {
1134  stream << "debug_model:" << '\n';
1135  for(const auto &pointer_array : generator.array_pool.get_arrays_of_pointers())
1136  {
1137  const auto arr = pointer_array.second;
1138  const exprt model =
1139  get_char_array_and_concretize(super_get, ns, stream, arr, array_pool);
1140 
1141  stream << "- " << format(arr) << ":\n"
1142  << " - pointer: " << format(pointer_array.first) << "\n"
1143  << " - model: " << format(model) << messaget::eom;
1144  }
1145 
1146  for(const auto &symbol : symbols)
1147  {
1148  stream << " - " << symbol.get_identifier() << ": "
1149  << format(super_get(symbol)) << '\n';
1150  }
1151  stream << messaget::eom;
1152 }
1153 
1167  const with_exprt &expr,
1168  const exprt &index,
1169  const bool left_propagate)
1170 {
1171  return left_propagate ? interval_sparse_arrayt(expr).to_if_expression(index)
1172  : sparse_arrayt::to_if_expression(expr, index);
1173 }
1174 
1182  const array_exprt &array_expr,
1183  const exprt &index,
1184  symbol_generatort &symbol_generator)
1185 {
1186  const typet &char_type = array_expr.type().element_type();
1187  const exprt default_val = symbol_generator("out_of_bound_access", char_type);
1188  const interval_sparse_arrayt sparse_array(array_expr, default_val);
1189  return sparse_array.to_if_expression(index);
1190 }
1191 
1193  const if_exprt &if_expr,
1194  const exprt &index,
1195  symbol_generatort &symbol_generator,
1196  const bool left_propagate)
1197 {
1198  exprt true_index = index_exprt(if_expr.true_case(), index);
1199  exprt false_index = index_exprt(if_expr.false_case(), index);
1200 
1201  // Substitute recursively in branches of conditional expressions
1202  std::optional<exprt> substituted_true_case =
1203  substitute_array_access(true_index, symbol_generator, left_propagate);
1204  std::optional<exprt> substituted_false_case =
1205  substitute_array_access(false_index, symbol_generator, left_propagate);
1206 
1207  return if_exprt(
1208  if_expr.cond(),
1209  substituted_true_case ? *substituted_true_case : true_index,
1210  substituted_false_case ? *substituted_false_case : false_index);
1211 }
1212 
1213 static std::optional<exprt> substitute_array_access(
1214  const index_exprt &index_expr,
1215  symbol_generatort &symbol_generator,
1216  const bool left_propagate)
1217 {
1218  const exprt &array = index_expr.array();
1219  if(auto array_of = expr_try_dynamic_cast<array_of_exprt>(array))
1220  return array_of->op();
1221  if(auto array_with = expr_try_dynamic_cast<with_exprt>(array))
1222  return substitute_array_access(
1223  *array_with, index_expr.index(), left_propagate);
1224  if(auto array_expr = expr_try_dynamic_cast<array_exprt>(array))
1225  return substitute_array_access(
1226  *array_expr, index_expr.index(), symbol_generator);
1227  if(auto if_expr = expr_try_dynamic_cast<if_exprt>(array))
1228  return substitute_array_access(
1229  *if_expr, index_expr.index(), symbol_generator, left_propagate);
1230 
1231  INVARIANT(
1232  array.is_nil() || array.id() == ID_symbol || array.id() == ID_nondet_symbol,
1233  std::string(
1234  "in case the array is unknown, it should be a symbol or nil, id: ") +
1235  id2string(array.id()));
1236  return {};
1237 }
1238 
1243  exprt &expr,
1244  symbol_generatort &symbol_generator,
1245  const bool left_propagate)
1246 {
1247  // Recurse down structure and modify on the way.
1248  for(auto it = expr.depth_begin(), itend = expr.depth_end(); it != itend; ++it)
1249  {
1250  if(const auto index_expr = expr_try_dynamic_cast<index_exprt>(*it))
1251  {
1252  std::optional<exprt> result =
1253  substitute_array_access(*index_expr, symbol_generator, left_propagate);
1254 
1255  // Only perform a write when we have something changed.
1256  if(result)
1257  it.mutate() = *result;
1258  }
1259  }
1260 }
1261 
1282  exprt expr,
1283  symbol_generatort &symbol_generator,
1284  const bool left_propagate)
1285 {
1286  substitute_array_access_in_place(expr, symbol_generator, left_propagate);
1287  return expr;
1288 }
1289 
1301  const string_not_contains_constraintt &constraint,
1302  const symbol_exprt &univ_var,
1303  const std::function<exprt(const exprt &)> &get)
1304 {
1305  // If the for all is vacuously true, the negation is false.
1306  const auto lbe = numeric_cast_v<mp_integer>(
1307  to_constant_expr(get(constraint.exists_lower_bound)));
1308  const auto ube = numeric_cast_v<mp_integer>(
1309  to_constant_expr(get(constraint.exists_upper_bound)));
1310  const auto univ_bounds = and_exprt(
1311  binary_relation_exprt(get(constraint.univ_lower_bound), ID_le, univ_var),
1312  binary_relation_exprt(get(constraint.univ_upper_bound), ID_gt, univ_var));
1313 
1314  // The negated existential becomes an universal, and this is the unrolling of
1315  // that universal quantifier.
1316  // Ff the upper bound is smaller than the lower bound (specifically, it might
1317  // actually be negative as it is initially unconstrained) then there is
1318  // nothing to do (and the reserve call would fail).
1319  if(ube < lbe)
1320  return and_exprt(univ_bounds, get(constraint.premise));
1321 
1322  std::vector<exprt> conjuncts;
1323  conjuncts.reserve(numeric_cast_v<std::size_t>(ube - lbe));
1324  for(mp_integer i = lbe; i < ube; ++i)
1325  {
1326  const constant_exprt i_expr = from_integer(i, univ_var.type());
1327  const exprt s0_char =
1328  get(index_exprt(constraint.s0, plus_exprt(univ_var, i_expr)));
1329  const exprt s1_char = get(index_exprt(constraint.s1, i_expr));
1330  conjuncts.push_back(equal_exprt(s0_char, s1_char));
1331  }
1332  const exprt equal_strings = conjunction(conjuncts);
1333  return and_exprt(univ_bounds, get(constraint.premise), equal_strings);
1334 }
1335 
1340 template <typename T>
1342  messaget::mstreamt &stream,
1343  const T &axiom,
1344  const T &axiom_in_model,
1345  const exprt &negaxiom,
1346  const exprt &with_concretized_arrays)
1347 {
1348  stream << std::string(4, ' ') << "- axiom:\n" << std::string(6, ' ');
1349  stream << to_string(axiom);
1350  stream << '\n'
1351  << std::string(4, ' ') << "- axiom_in_model:\n"
1352  << std::string(6, ' ');
1353  stream << to_string(axiom_in_model) << '\n'
1354  << std::string(4, ' ') << "- negated_axiom:\n"
1355  << std::string(6, ' ') << format(negaxiom) << '\n';
1356  stream << std::string(4, ' ') << "- negated_axiom_with_concretized_arrays:\n"
1357  << std::string(6, ' ') << format(with_concretized_arrays) << '\n';
1358 }
1359 
1361 static std::pair<bool, std::vector<exprt>> check_axioms(
1362  const string_axiomst &axioms,
1363  string_constraint_generatort &generator,
1364  const std::function<exprt(const exprt &)> &get,
1365  messaget::mstreamt &stream,
1366  const namespacet &ns,
1367  bool use_counter_example,
1368  const union_find_replacet &symbol_resolve,
1369  const std::unordered_map<string_not_contains_constraintt, symbol_exprt>
1370  &not_contain_witnesses)
1371 {
1372  stream << "string_refinementt::check_axioms:" << messaget::eom;
1373 
1374  stream << "symbol_resolve:" << messaget::eom;
1375  auto pairs = symbol_resolve.to_vector();
1376  for(const auto &pair : pairs)
1377  stream << " - " << format(pair.first) << " --> " << format(pair.second)
1378  << messaget::eom;
1379 
1380 #ifdef DEBUG
1381  debug_model(
1382  generator,
1383  stream,
1384  ns,
1385  get,
1386  generator.fresh_symbol.created_symbols,
1387  generator.array_pool);
1388 #endif
1389 
1390  // Maps from indexes of violated universal axiom to a witness of violation
1391  std::map<size_t, exprt> violated;
1392 
1393  stream << "string_refinement::check_axioms: " << axioms.universal.size()
1394  << " universal axioms:" << messaget::eom;
1395  for(size_t i = 0; i < axioms.universal.size(); i++)
1396  {
1397  const string_constraintt &axiom = axioms.universal[i];
1398  const string_constraintt axiom_in_model(
1399  axiom.univ_var,
1400  get(axiom.lower_bound),
1401  get(axiom.upper_bound),
1402  get(axiom.body),
1403  stream.message.get_message_handler());
1404 
1405  exprt negaxiom = axiom_in_model.negation();
1406  negaxiom = simplify_expr(negaxiom, ns);
1407 
1408  stream << std::string(2, ' ') << i << ".\n";
1409  const exprt with_concretized_arrays =
1410  substitute_array_access(negaxiom, generator.fresh_symbol, true);
1412  stream, axiom, axiom_in_model, negaxiom, with_concretized_arrays);
1413 
1414  if(
1415  const auto &witness = find_counter_example(
1416  ns,
1417  with_concretized_arrays,
1418  axiom.univ_var,
1419  stream.message.get_message_handler()))
1420  {
1421  stream << std::string(4, ' ')
1422  << "- violated_for: " << format(axiom.univ_var) << "="
1423  << format(*witness) << messaget::eom;
1424  violated[i] = *witness;
1425  }
1426  else
1427  stream << std::string(4, ' ') << "- correct" << messaget::eom;
1428  }
1429 
1430  // Maps from indexes of violated not_contains axiom to a witness of violation
1431  std::map<std::size_t, exprt> violated_not_contains;
1432 
1433  stream << "there are " << axioms.not_contains.size() << " not_contains axioms"
1434  << messaget::eom;
1435  for(std::size_t i = 0; i < axioms.not_contains.size(); i++)
1436  {
1437  const string_not_contains_constraintt &nc_axiom = axioms.not_contains[i];
1438  const symbol_exprt univ_var = generator.fresh_symbol(
1439  "not_contains_univ_var", nc_axiom.s0.length_type());
1440  const exprt negated_axiom = negation_of_not_contains_constraint(
1441  nc_axiom, univ_var, [&](const exprt &expr) {
1442  return simplify_expr(get(expr), ns);
1443  });
1444 
1445  stream << std::string(2, ' ') << i << ".\n";
1447  stream, nc_axiom, nc_axiom, negated_axiom, negated_axiom);
1448 
1449  if(
1450  const auto witness = find_counter_example(
1451  ns, negated_axiom, univ_var, stream.message.get_message_handler()))
1452  {
1453  stream << std::string(4, ' ')
1454  << "- violated_for: " << univ_var.get_identifier() << "="
1455  << format(*witness) << messaget::eom;
1456  violated_not_contains[i] = *witness;
1457  }
1458  }
1459 
1460  if(violated.empty() && violated_not_contains.empty())
1461  {
1462  stream << "no violated property" << messaget::eom;
1463  return {true, std::vector<exprt>()};
1464  }
1465  else
1466  {
1467  stream << violated.size() << " universal string axioms can be violated"
1468  << messaget::eom;
1469  stream << violated_not_contains.size()
1470  << " not_contains string axioms can be violated" << messaget::eom;
1471 
1472  if(use_counter_example)
1473  {
1474  std::vector<exprt> lemmas;
1475 
1476  for(const auto &v : violated)
1477  {
1478  const exprt &val = v.second;
1479  const string_constraintt &axiom = axioms.universal[v.first];
1480 
1481  exprt instance(axiom.body);
1482  replace_expr(axiom.univ_var, val, instance);
1483  // We are not sure the index set contains only positive numbers
1484  and_exprt bounds(
1485  axiom.univ_within_bounds(),
1486  binary_relation_exprt(from_integer(0, val.type()), ID_le, val));
1487  replace_expr(axiom.univ_var, val, bounds);
1488  const implies_exprt counter(bounds, instance);
1489  lemmas.push_back(counter);
1490  }
1491 
1492  for(const auto &v : violated_not_contains)
1493  {
1494  const exprt &val = v.second;
1495  const string_not_contains_constraintt &axiom =
1496  axioms.not_contains[v.first];
1497 
1498  const exprt func_val =
1499  index_exprt(not_contain_witnesses.at(axiom), val);
1500  const exprt comp_val = simplify_sum(plus_exprt(val, func_val));
1501 
1502  std::set<std::pair<exprt, exprt>> indices;
1503  indices.insert(std::pair<exprt, exprt>(comp_val, func_val));
1504  const exprt counter =
1505  ::instantiate_not_contains(axiom, indices, not_contain_witnesses)[0];
1506  lemmas.push_back(counter);
1507  }
1508  return {false, lemmas};
1509  }
1510  }
1511  return {false, std::vector<exprt>()};
1512 }
1513 
1517 {
1518  return linear_functiont{f}.to_expr();
1519 }
1520 
1526 static void initial_index_set(
1527  index_set_pairt &index_set,
1528  const namespacet &ns,
1529  const string_axiomst &axioms)
1530 {
1531  for(const auto &axiom : axioms.universal)
1532  initial_index_set(index_set, ns, axiom);
1533  for(const auto &axiom : axioms.not_contains)
1534  initial_index_set(index_set, ns, axiom);
1535 }
1536 
1541 static void update_index_set(
1542  index_set_pairt &index_set,
1543  const namespacet &ns,
1544  const std::vector<exprt> &current_constraints)
1545 {
1546  for(const auto &axiom : current_constraints)
1547  update_index_set(index_set, ns, axiom);
1548 }
1549 
1556 static void get_sub_arrays(const exprt &array_expr, std::vector<exprt> &accu)
1557 {
1558  if(array_expr.id() == ID_if)
1559  {
1560  get_sub_arrays(to_if_expr(array_expr).true_case(), accu);
1561  get_sub_arrays(to_if_expr(array_expr).false_case(), accu);
1562  }
1563  else
1564  {
1565  if(array_expr.type().id() == ID_array)
1566  {
1567  // TODO: check_that it does not contain any sub_array
1568  accu.push_back(array_expr);
1569  }
1570  else
1571  {
1572  for(const auto &operand : array_expr.operands())
1573  get_sub_arrays(operand, accu);
1574  }
1575  }
1576 }
1577 
1583 static void add_to_index_set(
1584  index_set_pairt &index_set,
1585  const namespacet &ns,
1586  const exprt &s,
1587  exprt i)
1588 {
1589  simplify(i, ns);
1590  const bool is_size_t = numeric_cast<std::size_t>(i).has_value();
1591  if(!i.is_constant() || is_size_t)
1592  {
1593  std::vector<exprt> sub_arrays;
1594  get_sub_arrays(s, sub_arrays);
1595  for(const auto &sub : sub_arrays)
1596  if(index_set.cumulative[sub].insert(i).second)
1597  index_set.current[sub].insert(i);
1598  }
1599 }
1600 
1616 static void initial_index_set(
1617  index_set_pairt &index_set,
1618  const namespacet &ns,
1619  const exprt &qvar,
1620  const exprt &upper_bound,
1621  const exprt &s,
1622  const exprt &i)
1623 {
1624  PRECONDITION(
1625  s.id() == ID_symbol || s.id() == ID_nondet_symbol || s.id() == ID_array ||
1626  s.id() == ID_if);
1627  if(s.id() == ID_array)
1628  {
1629  for(std::size_t j = 0; j < s.operands().size(); ++j)
1630  add_to_index_set(index_set, ns, s, from_integer(j, i.type()));
1631  return;
1632  }
1633  if(auto ite = expr_try_dynamic_cast<if_exprt>(s))
1634  {
1635  initial_index_set(index_set, ns, qvar, upper_bound, ite->true_case(), i);
1636  initial_index_set(index_set, ns, qvar, upper_bound, ite->false_case(), i);
1637  return;
1638  }
1639  const minus_exprt u_minus_1(upper_bound, from_integer(1, upper_bound.type()));
1640  exprt i_copy = i;
1641  replace_expr(qvar, u_minus_1, i_copy);
1642  add_to_index_set(index_set, ns, s, i_copy);
1643 }
1644 
1645 static void initial_index_set(
1646  index_set_pairt &index_set,
1647  const namespacet &ns,
1648  const string_constraintt &axiom)
1649 {
1650  const symbol_exprt &qvar = axiom.univ_var;
1651  const auto &bound = axiom.upper_bound;
1652  auto it = axiom.body.depth_begin();
1653  const auto end = axiom.body.depth_end();
1654  while(it != end)
1655  {
1656  if(it->id() == ID_index && is_char_type(it->type()))
1657  {
1658  const auto &index_expr = to_index_expr(*it);
1659  const auto &s = index_expr.array();
1660  initial_index_set(index_set, ns, qvar, bound, s, index_expr.index());
1661  it.next_sibling_or_parent();
1662  }
1663  else
1664  ++it;
1665  }
1666 }
1667 
1668 static void initial_index_set(
1669  index_set_pairt &index_set,
1670  const namespacet &ns,
1671  const string_not_contains_constraintt &axiom)
1672 {
1673  auto it = axiom.premise.depth_begin();
1674  const auto end = axiom.premise.depth_end();
1675  while(it != end)
1676  {
1677  if(it->id() == ID_index && is_char_type(it->type()))
1678  {
1679  const exprt &s = to_index_expr(*it).array();
1680  const exprt &i = to_index_expr(*it).index();
1681 
1682  // cur is of the form s[i] and no quantified variable appears in i
1683  add_to_index_set(index_set, ns, s, i);
1684 
1685  it.next_sibling_or_parent();
1686  }
1687  else
1688  ++it;
1689  }
1690 
1691  const minus_exprt kminus1(
1693  add_to_index_set(index_set, ns, axiom.s1.content(), kminus1);
1694 }
1695 
1700 static void update_index_set(
1701  index_set_pairt &index_set,
1702  const namespacet &ns,
1703  const exprt &formula)
1704 {
1705  std::list<exprt> to_process;
1706  to_process.push_back(formula);
1707 
1708  while(!to_process.empty())
1709  {
1710  exprt cur = to_process.back();
1711  to_process.pop_back();
1712  if(cur.id() == ID_index && is_char_type(cur.type()))
1713  {
1714  const exprt &s = to_index_expr(cur).array();
1715  const exprt &i = to_index_expr(cur).index();
1717  s.type().id() == ID_array,
1718  string_refinement_invariantt("index expressions must index on arrays"));
1719  exprt simplified = simplify_sum(i);
1720  if(s.id() != ID_array) // do not update index set of constant arrays
1721  add_to_index_set(index_set, ns, s, simplified);
1722  }
1723  else
1724  {
1725  for(const auto &op : as_const(cur).operands())
1726  to_process.push_back(op);
1727  }
1728  }
1729 }
1730 
1749 static std::vector<exprt> instantiate(
1750  const string_not_contains_constraintt &axiom,
1751  const index_set_pairt &index_set,
1752  const std::unordered_map<string_not_contains_constraintt, symbol_exprt>
1753  &witnesses)
1754 {
1755  const array_string_exprt &s0 = axiom.s0;
1756  const array_string_exprt &s1 = axiom.s1;
1757 
1758  const auto &index_set0 = index_set.cumulative.find(s0.content());
1759  const auto &index_set1 = index_set.cumulative.find(s1.content());
1760  const auto &current_index_set0 = index_set.current.find(s0.content());
1761  const auto &current_index_set1 = index_set.current.find(s1.content());
1762 
1763  if(
1764  index_set0 != index_set.cumulative.end() &&
1765  index_set1 != index_set.cumulative.end() &&
1766  current_index_set0 != index_set.current.end() &&
1767  current_index_set1 != index_set.current.end())
1768  {
1769  typedef std::pair<exprt, exprt> expr_pairt;
1770  std::set<expr_pairt> index_pairs;
1771 
1772  for(const auto &ic0 : current_index_set0->second)
1773  for(const auto &i1 : index_set1->second)
1774  index_pairs.insert(expr_pairt(ic0, i1));
1775  for(const auto &ic1 : current_index_set1->second)
1776  for(const auto &i0 : index_set0->second)
1777  index_pairs.insert(expr_pairt(i0, ic1));
1778 
1779  return ::instantiate_not_contains(axiom, index_pairs, witnesses);
1780  }
1781  return {};
1782 }
1783 
1791 exprt substitute_array_lists(exprt expr, size_t string_max_length)
1792 {
1793  for(auto &operand : expr.operands())
1794  operand = substitute_array_lists(operand, string_max_length);
1795 
1796  if(expr.id() == ID_array_list)
1797  {
1799  expr.operands().size() >= 2,
1800  string_refinement_invariantt("array-lists must have at least two "
1801  "operands"));
1802  const typet &char_type = expr.operands()[1].type();
1804  exprt ret_expr = array_of_exprt(from_integer(0, char_type), arr_type);
1805 
1806  for(size_t i = 0; i < expr.operands().size(); i += 2)
1807  {
1808  const exprt &index = expr.operands()[i];
1809  const exprt &value = expr.operands()[i + 1];
1810  const auto index_value = numeric_cast<std::size_t>(index);
1811  if(
1812  !index.is_constant() ||
1813  (index_value && *index_value < string_max_length))
1814  ret_expr = with_exprt(ret_expr, index, value);
1815  }
1816  return ret_expr;
1817  }
1818 
1819  return expr;
1820 }
1821 
1830 {
1831  const auto super_get = [this](const exprt &expr) {
1832  return supert::get(expr);
1833  };
1834  exprt ecopy(expr);
1835  (void)symbol_resolve.replace_expr(ecopy);
1836 
1837  // Special treatment for index expressions
1838  const auto &index_expr = expr_try_dynamic_cast<index_exprt>(ecopy);
1839  if(index_expr && is_char_type(index_expr->type()))
1840  {
1841  std::reference_wrapper<const exprt> current(index_expr->array());
1842  while(current.get().id() == ID_if)
1843  {
1844  const auto &if_expr = expr_dynamic_cast<if_exprt>(current.get());
1845  const exprt cond = get(if_expr.cond());
1846  if(cond.is_true())
1847  current = std::cref(if_expr.true_case());
1848  else if(cond.is_false())
1849  current = std::cref(if_expr.false_case());
1850  else
1851  UNREACHABLE;
1852  }
1853  const auto array = supert::get(current.get());
1854  const auto index = get(index_expr->index());
1855 
1856  // If the underlying solver does not know about the existence of an array,
1857  // it can return nil, which cannot be used in the expression returned here.
1858  if(array.is_nil())
1859  return index_exprt(current, index);
1860 
1861  const exprt unknown =
1862  from_integer(CHARACTER_FOR_UNKNOWN, index_expr->type());
1863  if(
1864  const auto sparse_array = interval_sparse_arrayt::of_expr(array, unknown))
1865  {
1866  if(const auto index_value = numeric_cast<std::size_t>(index))
1867  return sparse_array->at(*index_value);
1868  return sparse_array->to_if_expression(index);
1869  }
1870 
1871  INVARIANT(array.id() == ID_symbol || array.id() == ID_nondet_symbol,
1872  "Apart from symbols, array valuations can be interpreted as "
1873  "sparse arrays. Array model : " + array.pretty());
1874  return index_exprt(array, index);
1875  }
1876 
1877  if(is_char_array_type(ecopy.type(), ns))
1878  {
1880 
1881  if(
1882  const auto from_dependencies =
1883  dependencies.eval(arr, [&](const exprt &expr) { return get(expr); }))
1884  return *from_dependencies;
1885 
1886  if(
1887  const auto arr_model_opt =
1888  get_array(super_get, ns, log.debug(), arr, generator.array_pool))
1889  return *arr_model_opt;
1890 
1891  if(
1892  const auto &length_from_pool =
1894  {
1895  const exprt length = super_get(length_from_pool.value());
1896 
1897  if(const auto n = numeric_cast<std::size_t>(length))
1898  {
1899  const interval_sparse_arrayt sparse_array(from_integer(
1901  return sparse_array.concretize(*n, length.type());
1902  }
1903  }
1904  return arr;
1905  }
1906  return supert::get(ecopy);
1907 }
1908 
1918 static std::optional<exprt> find_counter_example(
1919  const namespacet &ns,
1920  const exprt &axiom,
1921  const symbol_exprt &var,
1922  message_handlert &message_handler)
1923 {
1924  satcheck_no_simplifiert sat_check(message_handler);
1925  bv_pointerst solver(ns, sat_check, message_handler);
1926  solver << axiom;
1927 
1929  return solver.get(var);
1930  else
1931  return {};
1932 }
1933 
1935 typedef std::map<exprt, std::vector<exprt>> array_index_mapt;
1936 
1939 {
1940  array_index_mapt indices;
1941  // clang-format off
1942  std::for_each(
1943  expr.depth_begin(),
1944  expr.depth_end(),
1945  [&](const exprt &expr)
1946  {
1947  const auto index_expr = expr_try_dynamic_cast<const index_exprt>(expr);
1948  if(index_expr)
1949  indices[index_expr->array()].push_back(index_expr->index());
1950  });
1951  // clang-format on
1952  return indices;
1953 }
1954 
1960 static bool
1962 {
1963  for(auto it = expr.depth_begin(); it != expr.depth_end();)
1964  {
1965  if(
1966  it->id() != ID_plus && it->id() != ID_minus &&
1967  it->id() != ID_unary_minus && *it != var)
1968  {
1969  if(std::find(it->depth_begin(), it->depth_end(), var) != it->depth_end())
1970  return false;
1971  else
1972  it.next_sibling_or_parent();
1973  }
1974  else
1975  ++it;
1976  }
1977  return true;
1978 }
1979 
1988 {
1989  for(auto it = constr.body.depth_begin(); it != constr.body.depth_end();)
1990  {
1991  if(*it == constr.univ_var)
1992  return false;
1993  if(it->id() == ID_index)
1994  it.next_sibling_or_parent();
1995  else
1996  ++it;
1997  }
1998  return true;
1999 }
2000 
2008  messaget::mstreamt &stream,
2009  const namespacet &ns,
2010  const string_constraintt &constraint)
2011 {
2012  const array_index_mapt body_indices = gather_indices(constraint.body);
2013  // Must validate for each string. Note that we have an invariant that the
2014  // second value in the pair is non-empty.
2015  for(const auto &pair : body_indices)
2016  {
2017  // Condition 1: All indices of the same string must be the of the same form
2018  const exprt rep = pair.second.back();
2019  for(size_t j = 0; j < pair.second.size() - 1; j++)
2020  {
2021  const exprt i = pair.second[j];
2022  const equal_exprt equals(rep, i);
2023  const exprt result = simplify_expr(equals, ns);
2024  if(result.is_false())
2025  {
2026  stream << "Indices not equal: " << to_string(constraint)
2027  << ", str: " << format(pair.first) << messaget::eom;
2028  return false;
2029  }
2030  }
2031 
2032  // Condition 2: f must be linear in the quantified variable
2033  if(!is_linear_arithmetic_expr(rep, constraint.univ_var))
2034  {
2035  stream << "f is not linear: " << to_string(constraint)
2036  << ", str: " << format(pair.first) << messaget::eom;
2037  return false;
2038  }
2039 
2040  // Condition 3: the quantified variable can only occur in indices in the
2041  // body
2042  if(!universal_only_in_index(constraint))
2043  {
2044  stream << "Universal variable outside of index:" << to_string(constraint)
2045  << messaget::eom;
2046  return false;
2047  }
2048  }
2049 
2050  return true;
2051 }
static abstract_object_pointert transform(const exprt &expr, const std::vector< abstract_object_pointert > &operands, const abstract_environmentt &environment, const namespacet &ns)
constant_exprt from_integer(const mp_integer &int_value, const typet &type)
const T & as_const(T &value)
Return a reference to the same object but ensures the type is const.
Definition: as_const.h:14
int8_t s1
Definition: bytecode_info.h:59
bitvector_typet char_type()
Definition: c_types.cpp:106
Boolean AND.
Definition: std_expr.h:2120
Array constructor from list of elements.
Definition: std_expr.h:1616
const array_typet & type() const
Definition: std_expr.h:1623
Array constructor from single element.
Definition: std_expr.h:1553
Correspondance between arrays and pointers string representations.
Definition: array_pool.h:42
const std::unordered_map< exprt, array_string_exprt, irep_hash > & get_arrays_of_pointers() const
Definition: array_pool.h:50
std::optional< exprt > get_length_if_exists(const array_string_exprt &s) const
As opposed to get_length(), do not create a new symbol if the length of the array_string_exprt does n...
Definition: array_pool.cpp:48
exprt get_or_create_length(const array_string_exprt &s)
Get the length of an array_string_exprt from the array_pool.
Definition: array_pool.cpp:26
const std::unordered_map< array_string_exprt, exprt, irep_hash > & created_strings() const
Return a map mapping all array_string_exprt of the array_pool to their length.
Definition: array_pool.cpp:179
const typet & length_type() const
Definition: string_expr.h:70
exprt & content()
Definition: string_expr.h:75
Arrays with given size.
Definition: std_types.h:807
const typet & element_type() const
The type of the elements of the array.
Definition: std_types.h:827
const exprt & size() const
Definition: std_types.h:840
const namespacet & ns
Definition: arrays.h:56
messaget log
Definition: arrays.h:57
exprt & lhs()
Definition: std_expr.h:668
exprt & rhs()
Definition: std_expr.h:678
A base class for relations, i.e., binary predicates whose two operands have the same type.
Definition: std_expr.h:762
void set_to(const exprt &expr, bool value) override
For a Boolean expression expr, add the constraint 'expr' if value is true, otherwise add 'not expr'.
Definition: boolbv.cpp:523
exprt get(const exprt &expr) const override
Return expr with variables replaced by values from satisfying assignment if available.
Definition: boolbv_get.cpp:21
decision_proceduret::resultt dec_solve(const exprt &) override
Implementation of the decision procedure.
A constant literal expression.
Definition: std_expr.h:2990
resultt
Result of running the decision procedure.
Equality.
Definition: std_expr.h:1361
Maps equation to expressions contained in them and conversely expressions to equations that contain t...
void add(const std::size_t i, const exprt &expr)
Record the fact that equation i contains expression expr
std::vector< std::size_t > find_equations(const exprt &expr)
std::vector< exprt > find_expressions(const std::size_t i)
Base class for all expressions.
Definition: expr.h:56
bool is_true() const
Return whether the expression is a constant representing true.
Definition: expr.cpp:27
depth_iteratort depth_end()
Definition: expr.cpp:249
bool is_boolean() const
Return whether the expression represents a Boolean.
Definition: expr.h:224
depth_iteratort depth_begin()
Definition: expr.cpp:247
bool is_false() const
Return whether the expression is a constant representing false.
Definition: expr.cpp:34
typet & type()
Return the type of the expression.
Definition: expr.h:84
bool is_constant() const
Return whether the expression is a constant.
Definition: expr.h:212
operandst & operands()
Definition: expr.h:94
The trinary if-then-else operator.
Definition: std_expr.h:2370
exprt & true_case()
Definition: std_expr.h:2397
exprt & false_case()
Definition: std_expr.h:2407
exprt & cond()
Definition: std_expr.h:2387
Boolean implication.
Definition: std_expr.h:2183
Array index operator.
Definition: std_expr.h:1465
exprt & array()
Definition: std_expr.h:1495
exprt & index()
Definition: std_expr.h:1505
An expression denoting infinity.
Definition: std_expr.h:3097
Represents arrays by the indexes up to which the value remains the same.
exprt to_if_expression(const exprt &index) const
static std::optional< interval_sparse_arrayt > of_expr(const exprt &expr, const exprt &extra_value)
If the expression is an array_exprt or a with_exprt uses the appropriate constructor,...
array_exprt concretize(std::size_t size, const typet &index_type) const
Convert to an array representation, ignores elements at index >= size.
const irep_idt & id() const
Definition: irep.h:388
bool is_nil() const
Definition: irep.h:368
Canonical representation of linear function, for instance, expression $x + x - y + 5 - 3$ would given...
Extract member of struct or union.
Definition: std_expr.h:2844
messaget & message
Definition: message.h:238
mstreamt & error() const
Definition: message.h:391
mstreamt & debug() const
Definition: message.h:421
message_handlert & get_message_handler()
Definition: message.h:183
static eomt eom
Definition: message.h:289
Binary minus.
Definition: std_expr.h:1061
const union_typet & follow_tag(const union_tag_typet &) const
Follow type tag of union type.
Definition: namespace.cpp:63
A namespacet is essentially one or two symbol tables bound together, to allow for symbol lookups in t...
Definition: namespace.h:94
The NIL expression.
Definition: std_expr.h:3081
Boolean negation.
Definition: std_expr.h:2327
The plus expression Associativity is not specified.
Definition: std_expr.h:1002
literalt convert(const exprt &expr) override
Convert a Boolean expression and return the corresponding literal.
void l_set_to_true(literalt a)
Definition: prop.h:52
static exprt to_if_expression(const with_exprt &expr, const exprt &index)
Creates an if_expr corresponding to the result of accessing the array at the given index.
std::optional< exprt > make_array_pointer_association(const exprt &return_code, const function_application_exprt &expr)
Associate array to pointer, and array to length.
static bool is_valid_string_constraint(messaget::mstreamt &stream, const namespacet &ns, const string_constraintt &constraint)
Checks the data invariant for string_constraintt.
exprt univ_within_bounds() const
static array_index_mapt gather_indices(const exprt &expr)
exprt negation() const
static bool universal_only_in_index(const string_constraintt &constr)
The universally quantified variable is only allowed to occur in index expressions in the body of a st...
static bool is_linear_arithmetic_expr(const exprt &expr, const symbol_exprt &var)
std::map< exprt, std::vector< exprt > > array_index_mapt
void output_dot(std::ostream &stream) const
void clean_cache()
Clean the cache used by eval
std::optional< exprt > eval(const array_string_exprt &s, const std::function< exprt(const exprt &)> &get_value) const
Attempt to evaluate the given string from the dependencies and valuation of strings on which it depen...
string_constraintst add_constraints(string_constraint_generatort &generatort, message_handlert &message_handler)
For all builtin call on which a test (or an unsupported buitin) result depends, add the corresponding...
string_constraint_generatort generator
union_find_replacet symbol_resolve
std::vector< exprt > equations
string_axiomst axioms
decision_proceduret::resultt dec_solve(const exprt &) override
Main decision procedure of the solver.
string_refinementt(const infot &)
const configt config_
std::set< exprt > seen_instances
void set_to(const exprt &expr, bool value) override
Record the constraints to ensure that the expression is true when the boolean is true and false other...
string_dependenciest dependencies
index_set_pairt index_sets
exprt get(const exprt &expr) const override
Evaluates the given expression in the valuation found by string_refinementt::dec_solve.
std::vector< exprt > instantiate_not_contains(const string_not_contains_constraintt &axiom, const std::set< std::pair< exprt, exprt >> &index_pairs, const std::unordered_map< string_not_contains_constraintt, symbol_exprt > &witnesses)
Instantiates a quantified formula representing not_contains by substituting the quantifiers and gener...
std::vector< exprt > current_constraints
void add_lemma(const exprt &lemma, bool simplify_lemma=true)
Add the given lemma to the solver.
String type.
Definition: std_types.h:966
Structure type, corresponds to C style structs.
Definition: std_types.h:231
const componentst & components() const
Definition: std_types.h:147
Expression to hold a symbol (variable)
Definition: std_expr.h:131
const irep_idt & get_identifier() const
Definition: std_expr.h:160
Generation of fresh symbols of a given type.
Definition: array_pool.h:22
The type of an expression, extends irept.
Definition: type.h:29
Similar interface to union-find for expressions, with a function for replacing sub-expressions by the...
std::vector< std::pair< exprt, exprt > > to_vector() const
exprt make_union(const exprt &a, const exprt &b)
Merge the set containing a and the set containing b.
bool replace_expr(exprt &expr) const
Replace subexpressions of expr by the representative element of the set they belong to.
Operator to update elements in structs and arrays.
Definition: std_expr.h:2471
Forward depth-first search iterators These iterators' copy operations are expensive,...
bool has_subtype(const typet &type, const std::function< bool(const typet &)> &pred, const namespacet &ns)
returns true if any of the contained types satisfies pred
Definition: expr_util.cpp:129
Deprecated expression utility functions.
static format_containert< T > format(const T &o)
Definition: format.h:37
const std::string & id2string(const irep_idt &d)
Definition: irep.h:44
Magic numbers used throughout the codebase.
const std::size_t MAX_CONCRETE_STRING_SIZE
Definition: magic.h:14
bool can_cast_expr< function_application_exprt >(const exprt &base)
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
bool replace_expr(const exprt &what, const exprt &by, exprt &dest)
bool simplify(exprt &expr, const namespacet &ns)
exprt simplify_expr(exprt src, const namespacet &ns)
BigInt mp_integer
Definition: smt_terms.h:17
void solver(std::vector< framet > &frames, const std::unordered_set< symbol_exprt, irep_hash > &address_taken, const solver_optionst &solver_options, const namespacet &ns, std::vector< propertyt > &properties, std::size_t property_index)
Definition: solver.cpp:44
#define UNREACHABLE
This should be used to mark dead code.
Definition: invariant.h:525
#define DATA_INVARIANT(CONDITION, REASON)
This condition should be used to document that assumptions that are made on goto_functions,...
Definition: invariant.h:534
#define PRECONDITION(CONDITION)
Definition: invariant.h:463
#define INVARIANT(CONDITION, REASON)
This macro uses the wrapper function 'invariant_violated_string'.
Definition: invariant.h:423
exprt conjunction(const exprt::operandst &op)
1) generates a conjunction for two or more operands 2) for one operand, returns the operand 3) return...
Definition: std_expr.cpp:83
bool can_cast_expr< equal_exprt >(const exprt &base)
Definition: std_expr.h:1386
const if_exprt & to_if_expr(const exprt &expr)
Cast an exprt to an if_exprt.
Definition: std_expr.h:2450
const constant_exprt & to_constant_expr(const exprt &expr)
Cast an exprt to a constant_exprt.
Definition: std_expr.h:3045
const equal_exprt & to_equal_expr(const exprt &expr)
Cast an exprt to an equal_exprt.
Definition: std_expr.h:1402
const index_exprt & to_index_expr(const exprt &expr)
Cast an exprt to an index_exprt.
Definition: std_expr.h:1533
const struct_typet & to_struct_type(const typet &type)
Cast a typet to a struct_typet.
Definition: std_types.h:308
const array_typet & to_array_type(const typet &type)
Cast a typet to an array_typet.
Definition: std_types.h:888
const struct_tag_typet & to_struct_tag_type(const typet &type)
Cast a typet to a struct_tag_typet.
Definition: std_types.h:518
#define CHARACTER_FOR_UNKNOWN
Module: String solver Author: Diffblue Ltd.
std::string to_string(const string_not_contains_constraintt &expr)
Used for debug printing.
void merge(string_constraintst &result, string_constraintst other)
Merge two sets of constraints by appending to the first one.
Defines related function for string constraints.
std::vector< exprt > instantiate_not_contains(const string_not_contains_constraintt &axiom, const std::set< std::pair< exprt, exprt >> &index_pairs, const std::unordered_map< string_not_contains_constraintt, symbol_exprt > &witnesses)
std::optional< exprt > add_node(string_dependenciest &dependencies, const exprt &expr, array_poolt &array_pool, symbol_generatort &fresh_symbol)
When a sub-expression of expr is a builtin_function, add a "string_builtin_function" node to the grap...
Keeps track of dependencies between strings.
array_string_exprt & to_array_string_expr(exprt &expr)
Definition: string_expr.h:96
static void initial_index_set(index_set_pairt &index_set, const namespacet &ns, const string_constraintt &axiom)
static std::vector< exprt > extract_strings_from_lhs(const exprt &lhs, const namespacet &ns)
This is meant to be used on the lhs of an equation with string subtype.
static std::string string_of_array(const array_exprt &arr)
convert the content of a string to a more readable representation.
static void update_index_set(index_set_pairt &index_set, const namespacet &ns, const std::vector< exprt > &current_constraints)
Add to the index set all the indices that appear in the formulas.
static std::vector< exprt > extract_strings(const exprt &expr, const namespacet &ns)
exprt substitute_array_lists(exprt expr, size_t string_max_length)
Replace array-lists by 'with' expressions.
static std::optional< exprt > substitute_array_access(const index_exprt &index_expr, symbol_generatort &symbol_generator, const bool left_propagate)
static std::vector< T > fill_in_map_as_vector(const std::map< std::size_t, T > &index_value)
Convert index-value map to a vector of values.
static std::vector< exprt > generate_instantiations(const index_set_pairt &index_set, const string_axiomst &axioms, const std::unordered_map< string_not_contains_constraintt, symbol_exprt > &not_contain_witnesses)
Instantiation of all constraints.
static std::pair< bool, std::vector< exprt > > check_axioms(const string_axiomst &axioms, string_constraint_generatort &generator, const std::function< exprt(const exprt &)> &get, messaget::mstreamt &stream, const namespacet &ns, bool use_counter_example, const union_find_replacet &symbol_resolve, const std::unordered_map< string_not_contains_constraintt, symbol_exprt > &not_contain_witnesses)
Check axioms takes the model given by the underlying solver and answers whether it satisfies the stri...
static void add_equations_for_symbol_resolution(union_find_replacet &symbol_solver, const std::vector< exprt > &equations, const namespacet &ns, messaget::mstreamt &stream)
Add association for each char pointer in the equation.
static void make_char_array_pointer_associations(string_constraint_generatort &generator, exprt &expr)
If expr is an equation whose right-hand-side is a associate_array_to_pointer call,...
static std::optional< exprt > get_valid_array_size(const std::function< exprt(const exprt &)> &super_get, const namespacet &ns, messaget::mstreamt &stream, const array_string_exprt &arr, const array_poolt &array_pool)
Get a model of the size of the input string.
static void add_string_equation_to_symbol_resolution(const equal_exprt &eq, union_find_replacet &symbol_resolve, const namespacet &ns)
Given an equation on strings, mark these strings as belonging to the same set in the symbol_resolve s...
static bool validate(const string_refinementt::infot &info)
static void add_to_index_set(index_set_pairt &index_set, const namespacet &ns, const exprt &s, exprt i)
Add i to the index set all the indices that appear in the formula.
exprt simplify_sum(const exprt &f)
static std::optional< exprt > get_array(const std::function< exprt(const exprt &)> &super_get, const namespacet &ns, messaget::mstreamt &stream, const array_string_exprt &arr, const array_poolt &array_pool)
Get a model of an array and put it in a certain form.
union_find_replacet string_identifiers_resolution_from_equations(const std::vector< equal_exprt > &equations, const namespacet &ns, messaget::mstreamt &stream)
Symbol resolution for expressions of type string typet.
static void display_index_set(messaget::mstreamt &stream, const index_set_pairt &index_set)
Write index set to the given stream, use for debugging.
static exprt negation_of_not_contains_constraint(const string_not_contains_constraintt &constraint, const symbol_exprt &univ_var, const std::function< exprt(const exprt &)> &get)
Negates the constraint to be fed to a solver.
static exprt replace_expr_copy(const union_find_replacet &symbol_resolve, exprt expr)
Substitute sub-expressions in equation by representative elements of symbol_resolve whenever possible...
static void substitute_array_access_in_place(exprt &expr, symbol_generatort &symbol_generator, const bool left_propagate)
Auxiliary function for substitute_array_access Performs the same operation but modifies the argument ...
static void get_sub_arrays(const exprt &array_expr, std::vector< exprt > &accu)
An expression representing an array of characters can be in the form of an if expression for instance...
void debug_model(const string_constraint_generatort &generator, messaget::mstreamt &stream, const namespacet &ns, const std::function< exprt(const exprt &)> &super_get, const std::vector< symbol_exprt > &symbols, array_poolt &array_pool)
Display part of the current model by mapping the variables created by the solver to constant expressi...
static std::vector< exprt > instantiate(const string_not_contains_constraintt &axiom, const index_set_pairt &index_set, const std::unordered_map< string_not_contains_constraintt, symbol_exprt > &witnesses)
Instantiates a quantified formula representing not_contains by substituting the quantifiers and gener...
static std::optional< exprt > find_counter_example(const namespacet &ns, const exprt &axiom, const symbol_exprt &var, message_handlert &message_handler)
Creates a solver with axiom as the only formula added and runs it.
static void debug_check_axioms_step(messaget::mstreamt &stream, const T &axiom, const T &axiom_in_model, const exprt &negaxiom, const exprt &with_concretized_arrays)
Debugging function which outputs the different steps an axiom goes through to be checked in check axi...
static exprt get_char_array_and_concretize(const std::function< exprt(const exprt &)> &super_get, const namespacet &ns, messaget::mstreamt &stream, const array_string_exprt &arr, array_poolt &array_pool)
Debugging function which finds the valuation of the given array in super_get and concretize unknown c...
String support via creating string constraints and progressively instantiating the universal constrai...
#define string_refinement_invariantt(reason)
std::string utf16_constant_array_to_java(const array_exprt &arr, std::size_t length)
Construct a string from a constant array.
bool is_char_type(const typet &type)
For now, any unsigned bitvector type of width smaller or equal to 16 is considered a character.
bool has_char_pointer_subtype(const typet &type, const namespacet &ns)
bool is_char_array_type(const typet &type, const namespacet &ns)
Distinguish char array from other types.
bool is_char_pointer_type(const typet &type)
For now, any unsigned bitvector type is considered a character.
const namespacet * ns
Definition: bv_refinement.h:35
std::map< exprt, std::set< exprt > > current
std::map< exprt, std::set< exprt > > cumulative
std::vector< string_constraintt > universal
std::vector< string_not_contains_constraintt > not_contains
Collection of constraints of different types: existential formulas, universal formulas,...
std::vector< string_not_contains_constraintt > not_contains
std::vector< exprt > existential
std::vector< string_constraintt > universal
Constraints to encode non containement of strings.
string_refinementt constructor arguments