From 7586f7629395379f14ea3295968ebf31fd9656d7 Mon Sep 17 00:00:00 2001 From: Romain Brenguier Date: Tue, 13 Mar 2018 08:46:01 +0000 Subject: [PATCH 1/6] Only add counter examples when index set exhausted Adding counter examples during the solver procedure make it very sensitive to small changes: if the counter examples are different between two executions then the lemmas will be different, which make the execution diverge. It is best to avoid them when possible, that is as long as the index set is not exhausted. --- src/solvers/refinement/string_refinement.cpp | 28 +++++++++----------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/solvers/refinement/string_refinement.cpp b/src/solvers/refinement/string_refinement.cpp index 202c2b4f8b8..84beca5d142 100644 --- a/src/solvers/refinement/string_refinement.cpp +++ b/src/solvers/refinement/string_refinement.cpp @@ -772,17 +772,12 @@ decision_proceduret::resultt string_refinementt::dec_solve() config_.use_counter_example, supert::config_.ui, symbol_resolve); - if(!satisfied) - { - for(const auto &counter : counter_examples) - add_lemma(counter); - debug() << "check_SAT: got SAT but the model is not correct" << eom; - } - else + if(satisfied) { debug() << "check_SAT: the model is correct" << eom; return resultt::D_SATISFIABLE; } + debug() << "check_SAT: got SAT but the model is not correct" << eom; } else { @@ -821,19 +816,15 @@ decision_proceduret::resultt string_refinementt::dec_solve() config_.use_counter_example, supert::config_.ui, symbol_resolve); - if(!satisfied) - { - for(const auto &counter : counter_examples) - add_lemma(counter); - debug() << "check_SAT: got SAT but the model is not correct" << eom; - } - else + if(satisfied) { debug() << "check_SAT: the model is correct" << eom; return resultt::D_SATISFIABLE; } - debug() << "refining..." << eom; + debug() << "check_SAT: got SAT but the model is not correct, refining..." + << eom; + // Since the model is not correct although we got SAT, we need to refine // the property we are checking by adding more indices to the index set, // and instantiating universal formulas with this indices. @@ -852,7 +843,12 @@ decision_proceduret::resultt string_refinementt::dec_solve() return resultt::D_ERROR; } else - debug() << "dec_solve: current index set is empty" << eom; + { + debug() << "dec_solve: current index set is empty, " + << "adding counter examples" << eom; + for(const auto &counter : counter_examples) + add_lemma(counter); + } } current_constraints.clear(); for(const auto &instance : From 0910010841992fcbcb86485a34282308e4e4d78a Mon Sep 17 00:00:00 2001 From: Romain Brenguier Date: Tue, 6 Mar 2018 13:11:24 +0000 Subject: [PATCH 2/6] Fix string constraints For the PASS algorithm, the premise should be of a form that can be analyzed by the index set construction. For now the index set only works with the explicit bounds of the string constraints, so the premise should not be used for now. --- src/solvers/refinement/string_constraint.h | 13 +----- .../refinement/string_constraint_generator.h | 3 ++ ...string_constraint_generator_comparison.cpp | 9 ++-- .../string_constraint_generator_indexof.cpp | 19 +++++---- .../string_constraint_generator_main.cpp | 7 +++- .../string_constraint_generator_testing.cpp | 12 ++++-- ...ng_constraint_generator_transformation.cpp | 41 +++++++++++-------- src/solvers/refinement/string_refinement.cpp | 4 +- 8 files changed, 59 insertions(+), 49 deletions(-) diff --git a/src/solvers/refinement/string_constraint.h b/src/solvers/refinement/string_constraint.h index 55aacd7f4d5..e72ead2495d 100644 --- a/src/solvers/refinement/string_constraint.h +++ b/src/solvers/refinement/string_constraint.h @@ -93,11 +93,10 @@ class string_constraintt : public exprt const symbol_exprt &_univ_var, const exprt &bound_inf, const exprt &bound_sup, - const exprt &prem, const exprt &body): exprt(ID_string_constraint) { - copy_to_operands(prem, body); + copy_to_operands(true_exprt(), body); copy_to_operands(_univ_var, bound_sup, bound_inf); } @@ -105,24 +104,14 @@ class string_constraintt : public exprt string_constraintt( const symbol_exprt &_univ_var, const exprt &bound_sup, - const exprt &prem, const exprt &body): string_constraintt( _univ_var, from_integer(0, _univ_var.type()), bound_sup, - prem, body) {} - // Default premise is true - string_constraintt( - const symbol_exprt &_univ_var, - const exprt &bound_sup, - const exprt &body): - string_constraintt(_univ_var, bound_sup, true_exprt(), body) - {} - exprt univ_within_bounds() const { return and_exprt( diff --git a/src/solvers/refinement/string_constraint_generator.h b/src/solvers/refinement/string_constraint_generator.h index 778ede29cff..68e77136a15 100644 --- a/src/solvers/refinement/string_constraint_generator.h +++ b/src/solvers/refinement/string_constraint_generator.h @@ -436,4 +436,7 @@ size_t max_printed_string_length(const typet &type, unsigned long ul_radix); std::string utf16_constant_array_to_java(const array_exprt &arr, std::size_t length); +/// \return expression representing the minimum of two expressions +exprt minimum(const exprt &a, const exprt &b); + #endif diff --git a/src/solvers/refinement/string_constraint_generator_comparison.cpp b/src/solvers/refinement/string_constraint_generator_comparison.cpp index 7b8590e7836..ca555921144 100644 --- a/src/solvers/refinement/string_constraint_generator_comparison.cpp +++ b/src/solvers/refinement/string_constraint_generator_comparison.cpp @@ -43,7 +43,8 @@ exprt string_constraint_generatort::add_axioms_for_equals( lemmas.push_back(a1); symbol_exprt qvar=fresh_univ_index("QA_equal", index_type); - string_constraintt a2(qvar, s1.length(), eq, equal_exprt(s1[qvar], s2[qvar])); + string_constraintt a2( + qvar, s1.length(), implies_exprt(eq, equal_exprt(s1[qvar], s2[qvar]))); constraints.push_back(a2); symbol_exprt witness=fresh_exist_index("witness_unequal", index_type); @@ -130,7 +131,7 @@ exprt string_constraint_generatort::add_axioms_for_equals_ignore_case( fresh_univ_index("QA_equal_ignore_case", index_type); const exprt constr2 = character_equals_ignore_case(s1[qvar], s2[qvar], char_a, char_A, char_Z); - const string_constraintt a2(qvar, s1.length(), eq, constr2); + const string_constraintt a2(qvar, s1.length(), implies_exprt(eq, constr2)); constraints.push_back(a2); const symbol_exprt witness = @@ -224,7 +225,7 @@ exprt string_constraint_generatort::add_axioms_for_compare_to( const symbol_exprt i = fresh_univ_index("QA_compare_to", index_type); const string_constraintt a2( - i, s1.length(), res_null, equal_exprt(s1[i], s2[i])); + i, s1.length(), implies_exprt(res_null, equal_exprt(s1[i], s2[i]))); constraints.push_back(a2); const symbol_exprt x = fresh_exist_index("index_compare_to", index_type); @@ -255,7 +256,7 @@ exprt string_constraint_generatort::add_axioms_for_compare_to( const symbol_exprt i2 = fresh_univ_index("QA_compare_to", index_type); const string_constraintt a4( - i2, x, not_exprt(res_null), equal_exprt(s1[i2], s2[i2])); + i2, x, implies_exprt(not_exprt(res_null), equal_exprt(s1[i2], s2[i2]))); constraints.push_back(a4); return res; diff --git a/src/solvers/refinement/string_constraint_generator_indexof.cpp b/src/solvers/refinement/string_constraint_generator_indexof.cpp index 77086d849f3..e2336e598a9 100644 --- a/src/solvers/refinement/string_constraint_generator_indexof.cpp +++ b/src/solvers/refinement/string_constraint_generator_indexof.cpp @@ -65,7 +65,7 @@ exprt string_constraint_generatort::add_axioms_for_index_of( symbol_exprt n=fresh_univ_index("QA_index_of", index_type); string_constraintt a4( - n, lower_bound, index, contains, not_exprt(equal_exprt(str[n], c))); + n, lower_bound, index, implies_exprt(contains, notequal_exprt(str[n], c))); constraints.push_back(a4); symbol_exprt m=fresh_univ_index("QA_index_of", index_type); @@ -73,8 +73,7 @@ exprt string_constraint_generatort::add_axioms_for_index_of( m, lower_bound, str.length(), - not_exprt(contains), - not_exprt(equal_exprt(str[m], c))); + implies_exprt(not_exprt(contains), not_exprt(equal_exprt(str[m], c)))); constraints.push_back(a5); return index; @@ -130,8 +129,8 @@ exprt string_constraint_generatort::add_axioms_for_index_of_string( string_constraintt a3( qvar, needle.length(), - contains, - equal_exprt(haystack[plus_exprt(qvar, offset)], needle[qvar])); + implies_exprt( + contains, equal_exprt(haystack[plus_exprt(qvar, offset)], needle[qvar]))); constraints.push_back(a3); // string_not contains_constraintt are formulas of the form: @@ -221,7 +220,8 @@ exprt string_constraint_generatort::add_axioms_for_last_index_of_string( symbol_exprt qvar=fresh_univ_index("QA_index_of_string", index_type); equal_exprt constr3(haystack[plus_exprt(qvar, offset)], needle[qvar]); - string_constraintt a3(qvar, needle.length(), contains, constr3); + const string_constraintt a3( + qvar, needle.length(), implies_exprt(contains, constr3)); constraints.push_back(a3); // end_index is min(from_index, |str| - |substring|) @@ -364,13 +364,14 @@ exprt string_constraint_generatort::add_axioms_for_last_index_of( n, plus_exprt(index, index1), end_index, - contains, - notequal_exprt(str[n], c)); + implies_exprt(contains, notequal_exprt(str[n], c))); constraints.push_back(a4); const symbol_exprt m = fresh_univ_index("QA_last_index_of2", index_type); const string_constraintt a5( - m, end_index, not_exprt(contains), notequal_exprt(str[m], c)); + m, + end_index, + implies_exprt(not_exprt(contains), notequal_exprt(str[m], c))); constraints.push_back(a5); return index; diff --git a/src/solvers/refinement/string_constraint_generator_main.cpp b/src/solvers/refinement/string_constraint_generator_main.cpp index 286ee7fa095..a739aa18efe 100644 --- a/src/solvers/refinement/string_constraint_generator_main.cpp +++ b/src/solvers/refinement/string_constraint_generator_main.cpp @@ -369,7 +369,7 @@ void string_constraint_generatort::add_constraint_on_characters( const and_exprt char_in_set( binary_relation_exprt(chr, ID_ge, from_integer(low_char, chr.type())), binary_relation_exprt(chr, ID_le, from_integer(high_char, chr.type()))); - const string_constraintt sc(qvar, start, end, true_exprt(), char_in_set); + const string_constraintt sc(qvar, start, end, char_in_set); constraints.push_back(sc); } @@ -662,3 +662,8 @@ exprt string_constraint_generatort::add_axioms_for_char_at( lemmas.push_back(equal_exprt(char_sym, str[f.arguments()[1]])); return char_sym; } + +exprt minimum(const exprt &a, const exprt &b) +{ + return if_exprt(binary_relation_exprt(a, ID_le, b), a, b); +} diff --git a/src/solvers/refinement/string_constraint_generator_testing.cpp b/src/solvers/refinement/string_constraint_generator_testing.cpp index b8dbfaa95ec..09948073b49 100644 --- a/src/solvers/refinement/string_constraint_generator_testing.cpp +++ b/src/solvers/refinement/string_constraint_generator_testing.cpp @@ -46,8 +46,8 @@ exprt string_constraint_generatort::add_axioms_for_is_prefix( string_constraintt a2( qvar, prefix.length(), - isprefix, - equal_exprt(str[plus_exprt(qvar, offset)], prefix[qvar])); + implies_exprt( + isprefix, equal_exprt(str[plus_exprt(qvar, offset)], prefix[qvar]))); constraints.push_back(a2); symbol_exprt witness=fresh_exist_index("witness_not_isprefix", index_type); @@ -153,7 +153,9 @@ exprt string_constraint_generatort::add_axioms_for_is_suffix( symbol_exprt qvar=fresh_univ_index("QA_suffix", index_type); const plus_exprt qvar_shifted(qvar, minus_exprt(s1.length(), s0.length())); string_constraintt a2( - qvar, s0.length(), issuffix, equal_exprt(s0[qvar], s1[qvar_shifted])); + qvar, + s0.length(), + implies_exprt(issuffix, equal_exprt(s0[qvar], s1[qvar_shifted]))); constraints.push_back(a2); symbol_exprt witness=fresh_exist_index("witness_not_suffix", index_type); @@ -221,7 +223,9 @@ exprt string_constraint_generatort::add_axioms_for_contains( symbol_exprt qvar=fresh_univ_index("QA_contains", index_type); const plus_exprt qvar_shifted(qvar, startpos); string_constraintt a4( - qvar, s1.length(), contains, equal_exprt(s1[qvar], s0[qvar_shifted])); + qvar, + s1.length(), + implies_exprt(contains, equal_exprt(s1[qvar], s0[qvar_shifted]))); constraints.push_back(a4); string_not_contains_constraintt a5( diff --git a/src/solvers/refinement/string_constraint_generator_transformation.cpp b/src/solvers/refinement/string_constraint_generator_transformation.cpp index 1271c11184a..e4df62603fd 100644 --- a/src/solvers/refinement/string_constraint_generator_transformation.cpp +++ b/src/solvers/refinement/string_constraint_generator_transformation.cpp @@ -45,24 +45,21 @@ exprt string_constraint_generatort::add_axioms_for_set_length( // We add axioms: // a1 : |res|=k - // a2 : forall i<|res|. i < |s1| ==> res[i] = s1[i] - // a3 : forall i<|res|. i >= |s1| ==> res[i] = 0 + // a2 : forall i< min(|s1|, k) .res[i] = s1[i] + // a3 : forall |s1| <= i < |res|. res[i] = 0 lemmas.push_back(res.axiom_for_has_length(k)); - symbol_exprt idx = fresh_univ_index("QA_index_set_length", index_type); - string_constraintt a2( - idx, - res.length(), - s1.axiom_for_length_gt(idx), - equal_exprt(s1[idx], res[idx])); + const symbol_exprt idx = fresh_univ_index("QA_index_set_length", index_type); + const string_constraintt a2( + idx, minimum(s1.length(), k), equal_exprt(s1[idx], res[idx])); constraints.push_back(a2); symbol_exprt idx2 = fresh_univ_index("QA_index_set_length2", index_type); string_constraintt a3( idx2, + s1.length(), res.length(), - s1.axiom_for_length_le(idx2), equal_exprt(res[idx2], constant_char(0, char_type))); constraints.push_back(a3); @@ -395,8 +392,8 @@ exprt string_constraint_generatort::add_axioms_for_to_upper_case( /// These axioms are: /// 1. \f$ |{\tt res}| = |{\tt str}|\f$ /// 2. \f$ {\tt res}[{\tt pos}]={\tt char}\f$ -/// 3. \f$ \forall i<|{\tt res}|.\ i \ne {\tt pos} -/// \Rightarrow {\tt res}[i] = {\tt str}[i]\f$ +/// 3. \f$ \forall i < min(|{\tt res}|, pos). {\tt res}[i] = {\tt str}[i]\f$ +/// 4. \f$ \forall pos+1 <= i < |{\tt res}|.\ {\tt res}[i] = {\tt str}[i]\f$ /// \param f: function application with arguments integer `|res|`, character /// pointer `&res[0]`, refined_string `str`, integer `pos`, /// and character `char` @@ -413,14 +410,22 @@ exprt string_constraint_generatort::add_axioms_for_char_set( const exprt &character = f.arguments()[4]; const binary_relation_exprt out_of_bounds(position, ID_ge, str.length()); - lemmas.push_back(equal_exprt(res.length(), str.length())); - lemmas.push_back(equal_exprt(res[position], character)); + const equal_exprt a1(res.length(), str.length()); + lemmas.push_back(a1); + const equal_exprt a2(res[position], character); + lemmas.push_back(a2); + const symbol_exprt q = fresh_univ_index("QA_char_set", position.type()); - equal_exprt a3_body(res[q], str[q]); - notequal_exprt a3_guard(q, position); - constraints.push_back( - string_constraintt( - q, from_integer(0, q.type()), res.length(), a3_guard, a3_body)); + const equal_exprt a3_body(res[q], str[q]); + const string_constraintt a3(q, minimum(res.length(), position), a3_body); + constraints.push_back(a3); + + const symbol_exprt q2 = fresh_univ_index("QA_char_set2", position.type()); + const plus_exprt lower_bound(position, from_integer(1, position.type())); + const equal_exprt a4_body(res[q2], str[q2]); + const string_constraintt a4(q2, lower_bound, res.length(), a4_body); + constraints.push_back(a4); + return if_exprt( out_of_bounds, from_integer(1, f.type()), from_integer(0, f.type())); } diff --git a/src/solvers/refinement/string_refinement.cpp b/src/solvers/refinement/string_refinement.cpp index 84beca5d142..f67011e1b27 100644 --- a/src/solvers/refinement/string_refinement.cpp +++ b/src/solvers/refinement/string_refinement.cpp @@ -1549,10 +1549,12 @@ static std::pair> check_axioms( const exprt &bound_inf=axiom.lower_bound(); const exprt &bound_sup=axiom.upper_bound(); const exprt &prem=axiom.premise(); + INVARIANT( + prem == true_exprt(), "string constraint premises are not supported"); const exprt &body=axiom.body(); const string_constraintt axiom_in_model( - univ_var, get(bound_inf), get(bound_sup), get(prem), get(body)); + univ_var, get(bound_inf), get(bound_sup), get(body)); exprt negaxiom=negation_of_constraint(axiom_in_model); negaxiom = simplify_expr(negaxiom, ns); From a2a314022ab29f75d66d226257de6156a71a94f8 Mon Sep 17 00:00:00 2001 From: Romain Brenguier Date: Thu, 1 Mar 2018 16:17:58 +0000 Subject: [PATCH 3/6] Correct bounds in instantiate method This was not checking that the instantiate variable does not exceed the upper bound. --- src/solvers/refinement/string_refinement.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/solvers/refinement/string_refinement.cpp b/src/solvers/refinement/string_refinement.cpp index f67011e1b27..8971f51b227 100644 --- a/src/solvers/refinement/string_refinement.cpp +++ b/src/solvers/refinement/string_refinement.cpp @@ -2128,19 +2128,19 @@ static exprt instantiate( const exprt &str, const exprt &val) { - exprt idx=find_index(axiom.body(), str, axiom.univ_var()); + const exprt idx = find_index(axiom.body(), str, axiom.univ_var()); if(idx.is_nil()) return true_exprt(); - exprt r=compute_inverse_function(stream, axiom.univ_var(), val, idx); - implies_exprt instance(axiom.premise(), axiom.body()); + const exprt r = compute_inverse_function(stream, axiom.univ_var(), val, idx); + implies_exprt instance( + and_exprt( + binary_relation_exprt(axiom.univ_var(), ID_ge, axiom.lower_bound()), + binary_relation_exprt(axiom.univ_var(), ID_lt, axiom.upper_bound()), + axiom.premise()), + axiom.body()); replace_expr(axiom.univ_var(), r, instance); - // We are not sure the index set contains only positive numbers - and_exprt bounds( - axiom.univ_within_bounds(), - binary_relation_exprt(from_integer(0, val.type()), ID_le, val)); - replace_expr(axiom.univ_var(), r, bounds); - return implies_exprt(bounds, instance); + return instance; } /// Instantiates a quantified formula representing `not_contains` by From 7085f9ca09a49d07062fd6dcf45a8d1c2d4c8de3 Mon Sep 17 00:00:00 2001 From: Romain Brenguier Date: Tue, 13 Mar 2018 13:44:46 +0000 Subject: [PATCH 4/6] Correct find_index function for `if`-expressions find_index would give up if the array in the index expression does not correspond exactly to the given `str`, however it could be an expression that contains str. For instance `(cond?str1:str2)[i+1]` is equivalent to `cond?(str1[i+1]):(str2[i+1])`, so in both cases `find_index(e, str1, i)` should return `i+1`. --- src/solvers/refinement/string_refinement.cpp | 55 ++++++++++++++------ 1 file changed, 38 insertions(+), 17 deletions(-) diff --git a/src/solvers/refinement/string_refinement.cpp b/src/solvers/refinement/string_refinement.cpp index 8971f51b227..0d615dd2d96 100644 --- a/src/solvers/refinement/string_refinement.cpp +++ b/src/solvers/refinement/string_refinement.cpp @@ -2088,26 +2088,47 @@ static void update_index_set( /// Finds an index on `str` used in `expr` that contains `qvar`, for instance /// with arguments ``(str[k]=='a')``, `str`, and `k`, the function should /// return `k`. +/// If two different such indexes exist, an invariant will fail. /// \param [in] expr: the expression to search /// \param [in] str: the string which must be indexed /// \param [in] qvar: the universal variable that must be in the index -/// \return an index expression in `expr` on `str` containing `qvar` -static exprt find_index( - const exprt &expr, const exprt &str, const symbol_exprt &qvar) +/// \return an index expression in `expr` on `str` containing `qvar`. Returns +/// an empty optional when `expr` does not contain `str`. +static optionalt +find_index(const exprt &expr, const exprt &str, const symbol_exprt &qvar) { - const auto it=std::find_if( - expr.depth_begin(), - expr.depth_end(), - [&] (const exprt &e) -> bool + auto index_str_containing_qvar = [&](const exprt &e) { // NOLINT + if(auto index_expr = expr_try_dynamic_cast(e)) { - return e.id()==ID_index - && to_index_expr(e).array()==str - && find_qvar(to_index_expr(e).index(), qvar); - }); + const auto &arr = index_expr->array(); + const auto str_it = std::find(arr.depth_begin(), arr.depth_end(), str); + return str_it != arr.depth_end() && find_qvar(index_expr->index(), qvar); + } + return false; + }; + + auto it = std::find_if( + expr.depth_begin(), expr.depth_end(), index_str_containing_qvar); + if(it == expr.depth_end()) + return {}; + const exprt &index = to_index_expr(*it).index(); + + // Check that there are no two such indexes + it.next_sibling_or_parent(); + while(it != expr.depth_end()) + { + if(index_str_containing_qvar(*it)) + { + INVARIANT( + to_index_expr(*it).index() == index, + "string should always be indexed by same value in a given formula"); + it.next_sibling_or_parent(); + } + else + ++it; + } - return it==expr.depth_end() - ?nil_exprt() - :to_index_expr(*it).index(); + return index; } /// Instantiates a string constraint by substituting the quantifiers. @@ -2128,11 +2149,11 @@ static exprt instantiate( const exprt &str, const exprt &val) { - const exprt idx = find_index(axiom.body(), str, axiom.univ_var()); - if(idx.is_nil()) + const optionalt idx = find_index(axiom.body(), str, axiom.univ_var()); + if(!idx.has_value()) return true_exprt(); - const exprt r = compute_inverse_function(stream, axiom.univ_var(), val, idx); + const exprt r = compute_inverse_function(stream, axiom.univ_var(), val, *idx); implies_exprt instance( and_exprt( binary_relation_exprt(axiom.univ_var(), ID_ge, axiom.lower_bound()), From c905c2c713c20f586cfaf04f643f11bfcc21325d Mon Sep 17 00:00:00 2001 From: Romain Brenguier Date: Wed, 14 Mar 2018 07:34:45 +0000 Subject: [PATCH 5/6] Remove "Adding counter-examples" message This can be confusing now that we don't necessarily add the counter-examples. --- src/solvers/refinement/string_refinement.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/solvers/refinement/string_refinement.cpp b/src/solvers/refinement/string_refinement.cpp index 0d615dd2d96..df7c5946783 100644 --- a/src/solvers/refinement/string_refinement.cpp +++ b/src/solvers/refinement/string_refinement.cpp @@ -1641,8 +1641,6 @@ static std::pair> check_axioms( if(use_counter_example) { - stream << "Adding counter-examples: " << eom; - std::vector lemmas; for(const auto &v : violated) @@ -1658,8 +1656,6 @@ static std::pair> check_axioms( binary_relation_exprt(from_integer(0, val.type()), ID_le, val)); replace_expr(axiom.univ_var(), val, bounds); const implies_exprt counter(bounds, instance); - - stream << " - " << format(counter) << eom; lemmas.push_back(counter); } @@ -1676,8 +1672,6 @@ static std::pair> check_axioms( indices.insert(std::pair(comp_val, func_val)); const exprt counter=::instantiate_not_contains( axiom, indices, generator)[0]; - - stream << " - " << format(counter) << eom; lemmas.push_back(counter); } return { false, lemmas }; From b5f12ff305d5239966092a9739fdfa6fba1ba00a Mon Sep 17 00:00:00 2001 From: Romain Brenguier Date: Wed, 14 Mar 2018 08:48:15 +0000 Subject: [PATCH 6/6] Correct initial index set computation The initial_index_set function was not taking into account if expressions. We now recursively add the components of the if expression. The function was also performing an unecessary look-up for a quantified variable. --- src/solvers/refinement/string_refinement.cpp | 89 ++++++++++++-------- 1 file changed, 52 insertions(+), 37 deletions(-) diff --git a/src/solvers/refinement/string_refinement.cpp b/src/solvers/refinement/string_refinement.cpp index df7c5946783..53da9d1cbb6 100644 --- a/src/solvers/refinement/string_refinement.cpp +++ b/src/solvers/refinement/string_refinement.cpp @@ -1965,53 +1965,68 @@ static void add_to_index_set( } } +/// Given an array access of the form \a s[i] assumed to be part of a formula +/// \f$ \forall q < u. charconstraint \f$, initialize the index set of \a s +/// so that: +/// - \f$ i[q -> u - 1] \f$ appears in the index set of \a s if \a s is a +/// symbol +/// - if \a s is an array expression, all index from \a 0 to +/// \f$ s.length - 1 \f$ are in the index set +/// - if \a s is an if expression we apply this procedure to the true and +/// false cases +/// \param index_set: the index_set to initialize +/// \param ns: namespace, used for simplifying indexes +/// \param qvar: the quantified variable \a q +/// \param upper_bound: bound \a u on the quantified variable +/// \param s: expression representing a string +/// \param i: expression representing the index at which \a s is accessed +static void initial_index_set( + index_set_pairt &index_set, + const namespacet &ns, + const exprt &qvar, + const exprt &upper_bound, + const exprt &s, + const exprt &i) +{ + PRECONDITION(s.id() == ID_symbol || s.id() == ID_array || s.id() == ID_if); + if(s.id() == ID_array) + { + for(std::size_t j = 0; j < s.operands().size(); ++j) + add_to_index_set(index_set, ns, s, from_integer(j, i.type())); + return; + } + if(auto ite = expr_try_dynamic_cast(s)) + { + initial_index_set(index_set, ns, qvar, upper_bound, ite->true_case(), i); + initial_index_set(index_set, ns, qvar, upper_bound, ite->false_case(), i); + return; + } + const minus_exprt u_minus_1(upper_bound, from_integer(1, upper_bound.type())); + exprt i_copy = i; + replace_expr(qvar, u_minus_1, i_copy); + add_to_index_set(index_set, ns, s, i_copy); +} + static void initial_index_set( index_set_pairt &index_set, const namespacet &ns, const string_constraintt &axiom) { const symbol_exprt &qvar=axiom.univ_var(); - std::list to_process; - to_process.push_back(axiom.body()); - - while(!to_process.empty()) + const auto &bound = axiom.upper_bound(); + auto it = axiom.body().depth_begin(); + const auto end = axiom.body().depth_end(); + while(it != end) { - const exprt cur = to_process.back(); - to_process.pop_back(); - if(cur.id() == ID_index && is_char_type(cur.type())) + if(it->id() == ID_index && is_char_type(it->type())) { - const index_exprt &index_expr = to_index_expr(cur); - const exprt &s = index_expr.array(); - const exprt &i = index_expr.index(); - - if(s.id() == ID_array) - { - for(std::size_t j = 0; j < s.operands().size(); ++j) - add_to_index_set(index_set, ns, s, from_integer(j, i.type())); - } - else - { - const bool has_quant_var = find_qvar(i, qvar); - - // if cur is of the form s[i] and no quantified variable appears in i - if(!has_quant_var) - { - add_to_index_set(index_set, ns, s, i); - } - else - { - // otherwise we add k-1 - exprt copy(i); - const minus_exprt kminus1( - axiom.upper_bound(), from_integer(1, axiom.upper_bound().type())); - replace_expr(qvar, kminus1, copy); - add_to_index_set(index_set, ns, s, copy); - } - } + const auto &index_expr = to_index_expr(*it); + const auto &s = index_expr.array(); + initial_index_set(index_set, ns, qvar, bound, s, index_expr.index()); + it.next_sibling_or_parent(); } else - forall_operands(it, cur) - to_process.push_back(*it); + ++it; } }