CBMC
Loading...
Searching...
No Matches
string_refinement.cpp
Go to the documentation of this file.
1/*******************************************************************\
2
3Module: 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
9Author: Alberto Griggio, alberto.griggio@gmail.com
10
11\*******************************************************************/
12
19
20#include "string_refinement.h"
21
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
40 const namespacet &ns,
41 const string_constraintt &constraint);
42
43static 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
65static std::pair<bool, std::vector<exprt>> check_axioms(
66 const string_axiomst &axioms,
68 const std::function<exprt(const exprt &)> &get,
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>
75
76static void initial_index_set(
78 const namespacet &ns,
80
81static void initial_index_set(
83 const namespacet &ns,
85
86static void initial_index_set(
88 const namespacet &ns,
89 const string_axiomst &axioms);
90
91exprt simplify_sum(const exprt &f);
92
93static void update_index_set(
95 const namespacet &ns,
96 const std::vector<exprt> &current_constraints);
97
98static void update_index_set(
100 const namespacet &ns,
101 const exprt &formula);
102
103static std::vector<exprt> instantiate(
106 const std::unordered_map<string_not_contains_constraintt, symbol_exprt>
107 &witnesses);
108
109static std::optional<exprt> get_array(
110 const std::function<exprt(const exprt &)> &super_get,
111 const namespacet &ns,
113 const array_string_exprt &arr,
114 const array_poolt &array_pool);
115
116static std::optional<exprt> substitute_array_access(
117 const index_exprt &index_expr,
119 const bool left_propagate);
120
128template <typename T>
129static std::vector<T>
130fill_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
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
169
171static 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 {
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
220static std::vector<exprt> generate_instantiations(
222 const string_axiomst &axioms,
223 const std::unordered_map<string_not_contains_constraintt, symbol_exprt>
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 :
239 lemmas.push_back(instance);
240 }
241 return lemmas;
242}
243
249 exprt &expr)
250{
252 {
253 if(
255 as_const(equal_expr->rhs())))
256 {
257 const auto new_equation =
259 if(new_equation)
260 {
261 expr =
263 }
264 }
265 }
266}
267
272static exprt
273replace_expr_copy(const union_find_replacet &symbol_resolve, exprt expr)
274{
275 symbol_resolve.replace_expr(expr);
276 return expr;
277}
278
283void 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
302 const std::vector<exprt> &equations,
303 const namespacet &ns,
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 {
345 rhs.type().id() == ID_struct_tag
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());
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
375static 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 {
384 lhs.type().id() == ID_struct_tag
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
403static std::vector<exprt>
404extract_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 {
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());
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,
474{
475 const std::string log_message =
476 "WARNING string_refinement.cpp "
477 "string_identifiers_resolution_from_equations:";
478
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
542static void
543output_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;
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
637 [&] {
638 std::vector<equal_exprt> equalities;
639 for(const auto &eq : equations)
640 {
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
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.
698 const std::optional<exprt> new_equation = add_node(
703 if(new_equation)
704 local_equations.push_back(*new_equation);
705 else
706 local_equations.push_back(eq);
707 }
709
710#ifdef DEBUG
712#endif
713
714 log.debug() << "dec_solve: add constraints" << messaget::eom;
715 merge(
716 constraints,
718
719#ifdef DEBUG
721#endif
722
723#ifdef DEBUG
724 log.debug() << "dec_solve: arrays_of_pointers:" << messaget::eom;
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
741 std::transform(
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
754 std::transform(
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>
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 {
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); };
796 {
797 bool satisfied;
798 std::vector<exprt> counter_examples;
800 axioms,
801 generator,
802 get,
803 log.debug(),
804 ns,
805 config_.use_counter_example,
808 if(satisfied)
809 {
810 log.debug() << "check_SAT: the model is correct" << messaget::eom;
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 =
827 for(const auto &instance : initial_instances)
828 {
830 }
831
832 while((loop_bound_--) > 0)
833 {
837
839 {
840 bool satisfied;
841 std::vector<exprt> counter_examples;
843 axioms,
844 generator,
845 get,
846 log.debug(),
847 ns,
848 config_.use_counter_example,
851 if(satisfied)
852 {
853 log.debug() << "check_SAT: the model is correct" << messaget::eom;
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 }
887 const auto instances =
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
919 {
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
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(
941 CHARACTER_FOR_UNKNOWN, to_array_type(it->type()).element_type()),
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
952}
953
968static std::optional<exprt> get_valid_array_size(
969 const std::function<exprt(const exprt &)> &super_get,
970 const namespacet &ns,
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);
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: "
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
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
1012static std::optional<exprt> get_array(
1013 const std::function<exprt(const exprt &)> &super_get,
1014 const namespacet &ns,
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
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
1060static 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();
1068}
1069
1079 const std::function<exprt(const exprt &)> &super_get,
1080 const namespacet &ns,
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;
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 =
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,
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 =
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 }
1152}
1153
1167 const with_exprt &expr,
1168 const exprt &index,
1169 const bool left_propagate)
1170{
1172 : sparse_arrayt::to_if_expression(expr, index);
1173}
1174
1182 const array_exprt &array_expr,
1183 const exprt &index,
1185{
1186 const typet &char_type = array_expr.type().element_type();
1187 const exprt default_val = symbol_generator("out_of_bound_access", char_type);
1189 return sparse_array.to_if_expression(index);
1190}
1191
1193 const if_exprt &if_expr,
1194 const exprt &index,
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 =
1204 std::optional<exprt> substituted_false_case =
1206
1207 return if_exprt(
1208 if_expr.cond(),
1211}
1212
1213static std::optional<exprt> substitute_array_access(
1214 const index_exprt &index_expr,
1216 const bool left_propagate)
1217{
1218 const exprt &array = index_expr.array();
1220 return array_of->op();
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,
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 {
1251 {
1252 std::optional<exprt> result =
1254
1255 // Only perform a write when we have something changed.
1256 if(result)
1257 it.mutate() = *result;
1258 }
1259 }
1260}
1261
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;
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 }
1333 return and_exprt(univ_bounds, get(constraint.premise), equal_strings);
1334}
1335
1340template <typename T>
1343 const T &axiom,
1344 const T &axiom_in_model,
1345 const exprt &negaxiom,
1347{
1348 stream << std::string(4, ' ') << "- axiom:\n" << std::string(6, ' ');
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
1361static std::pair<bool, std::vector<exprt>> check_axioms(
1362 const string_axiomst &axioms,
1364 const std::function<exprt(const exprt &)> &get,
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>
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
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];
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();
1407
1408 stream << std::string(2, ' ') << i << ".\n";
1413
1414 if(
1415 const auto &witness = find_counter_example(
1416 ns,
1418 axiom.univ_var,
1419 stream.message.get_message_handler()))
1420 {
1421 stream << std::string(4, ' ')
1422 << "- violated_for: " << format(axiom.univ_var) << "="
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 {
1438 const symbol_exprt univ_var = generator.fresh_symbol(
1439 "not_contains_univ_var", nc_axiom.s0.length_type());
1441 nc_axiom, univ_var, [&](const exprt &expr) {
1442 return simplify_expr(get(expr), ns);
1443 });
1444
1445 stream << std::string(2, ' ') << i << ".\n";
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() << "="
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;
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
1485 axiom.univ_within_bounds(),
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;
1496 axioms.not_contains[v.first];
1497
1498 const exprt 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 =
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
1528 const namespacet &ns,
1529 const string_axiomst &axioms)
1530{
1531 for(const auto &axiom : axioms.universal)
1533 for(const auto &axiom : axioms.not_contains)
1535}
1536
1543 const namespacet &ns,
1544 const std::vector<exprt> &current_constraints)
1545{
1546 for(const auto &axiom : current_constraints)
1548}
1549
1556static void get_sub_arrays(const exprt &array_expr, std::vector<exprt> &accu)
1557{
1558 if(array_expr.id() == ID_if)
1559 {
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())
1574 }
1575 }
1576}
1577
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;
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
1618 const namespacet &ns,
1619 const exprt &qvar,
1620 const exprt &upper_bound,
1621 const exprt &s,
1622 const exprt &i)
1623{
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)
1631 return;
1632 }
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;
1643}
1644
1647 const namespacet &ns,
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
1670 const namespacet &ns,
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(
1692 axiom.exists_upper_bound, from_integer(1, axiom.exists_upper_bound.type()));
1693 add_to_index_set(index_set, ns, axiom.s1.content(), kminus1);
1694}
1695
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"));
1720 if(s.id() != ID_array) // do not update index set of constant arrays
1722 }
1723 else
1724 {
1725 for(const auto &op : as_const(cur).operands())
1726 to_process.push_back(op);
1727 }
1728 }
1729}
1730
1749static std::vector<exprt> instantiate(
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
1792{
1793 for(auto &operand : expr.operands())
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();
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() ||
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);
1836
1837 // Special treatment for index expressions
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
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 =
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 =
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 {
1900 CHARACTER_FOR_UNKNOWN, to_array_type(arr.type()).element_type()));
1901 return sparse_array.concretize(*n, length.type());
1902 }
1903 }
1904 return arr;
1905 }
1906 return supert::get(ecopy);
1907}
1908
1918static 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
1935typedef 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
1960static 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
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}
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
static bool convert(const irep_idt &identifier, const std::ostringstream &s, symbol_table_baset &symbol_table, message_handlert &message_handler)
bitvector_typet char_type()
Definition c_types.cpp:106
virtual void clear()
Reset the abstract state.
Definition ai.h:265
ait supplies three of the four components needed: an abstract interpreter (in this case handling func...
Definition ai.h:562
Boolean AND.
Definition std_expr.h:2125
Array constructor from list of elements.
Definition std_expr.h:1621
Array constructor from single element.
Definition std_expr.h:1558
Correspondance between arrays and pointers string representations.
Definition array_pool.h:42
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...
exprt get_or_create_length(const array_string_exprt &s)
Get the length of an array_string_exprt from the array_pool.
const std::unordered_map< exprt, array_string_exprt, irep_hash > & get_arrays_of_pointers() const
Definition array_pool.h:50
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.
Arrays with given size.
Definition std_types.h:807
const namespacet & ns
Definition arrays.h:56
messaget log
Definition arrays.h:57
A base class for relations, i.e., binary predicates whose two operands have the same type.
Definition std_expr.h:762
decision_proceduret::resultt dec_solve(const exprt &) override
Implementation of the decision procedure.
A constant literal expression.
Definition std_expr.h:3117
virtual exprt get(const exprt &) const =0
Return expr with variables replaced by values from satisfying assignment if available.
resultt
Result of running the decision procedure.
virtual void set_to(const exprt &, bool value)=0
For a Boolean expression expr, add the constraint 'expr' if value is true, otherwise add 'not expr'.
Equality.
Definition std_expr.h:1366
Maps equation to expressions contained in them and conversely expressions to equations that contain t...
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
bool is_constant() const
Return whether the expression is a constant.
Definition expr.h:212
typet & type()
Return the type of the expression.
Definition expr.h:84
operandst & operands()
Definition expr.h:94
The trinary if-then-else operator.
Definition std_expr.h:2497
Boolean implication.
Definition std_expr.h:2225
Array index operator.
Definition std_expr.h:1470
An expression denoting infinity.
Definition std_expr.h:3224
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,...
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:2971
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:49
A namespacet is essentially one or two symbol tables bound together, to allow for symbol lookups in t...
Definition namespace.h:91
The NIL expression.
Definition std_expr.h:3208
Boolean negation.
Definition std_expr.h:2454
The plus expression Associativity is not specified.
Definition std_expr.h:1002
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.
static array_index_mapt gather_indices(const exprt &expr)
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
decision_proceduret::resultt dec_solve(const exprt &) override
Main decision procedure of the solver.
string_refinementt(const infot &)
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 > 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
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:2598
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
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
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:1391
const index_exprt & to_index_expr(const exprt &expr)
Cast an exprt to an index_exprt.
Definition std_expr.h:1538
const if_exprt & to_if_expr(const exprt &expr)
Cast an exprt to an if_exprt.
Definition std_expr.h:2577
const constant_exprt & to_constant_expr(const exprt &expr)
Cast an exprt to a constant_exprt.
Definition std_expr.h:3172
const equal_exprt & to_equal_expr(const exprt &expr)
Cast an exprt to an equal_exprt.
Definition std_expr.h:1407
const struct_typet & to_struct_type(const typet &type)
Cast a typet to a struct_typet.
Definition std_types.h:308
const struct_tag_typet & to_struct_tag_type(const typet &type)
Cast a typet to a struct_tag_typet.
Definition std_types.h:518
const array_typet & to_array_type(const typet &type)
Cast a typet to an array_typet.
Definition std_types.h:888
#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 bool is_valid_string_constraint(messaget::mstreamt &stream, const namespacet &ns, const string_constraintt &constraint)
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_from_lhs(const exprt &lhs, const namespacet &ns)
This is meant to be used on the lhs of an equation with string subtype.
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< exprt > extract_strings(const exprt &expr, const namespacet &ns)
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 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_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 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 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 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...
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.
String support via creating string constraints and progressively instantiating the universal constrai...
exprt substitute_array_access(exprt expr, symbol_generatort &symbol_generator, const bool left_propagate)
Create an equivalent expression where array accesses and 'with' expressions are replaced by 'if' expr...
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.
#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.
std::map< exprt, std::set< exprt > > current
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