Merge pull request #1162 from jasigal/fix/invalid-string-constraint-transformations#779
Fixed invalid transformations on string constraints and added invariant checking.
This commit is contained in:
commit
bcbb6fa0fb
Binary file not shown.
|
@ -1,6 +1,6 @@
|
||||||
CORE
|
CORE
|
||||||
test_contains.class
|
test_contains.class
|
||||||
--refine-strings
|
--refine-strings --string-max-length 100
|
||||||
^EXIT=10$
|
^EXIT=10$
|
||||||
^SIGNAL=0$
|
^SIGNAL=0$
|
||||||
^\[.*assertion.1\].* line 8.* SUCCESS$
|
^\[.*assertion.1\].* line 8.* SUCCESS$
|
||||||
|
|
|
@ -1473,7 +1473,7 @@ FORMULA_TRANSPARENT = YES
|
||||||
# The default value is: NO.
|
# The default value is: NO.
|
||||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||||
|
|
||||||
USE_MATHJAX = NO
|
USE_MATHJAX = YES
|
||||||
|
|
||||||
# When MathJax is enabled you can set the default output format to be used for
|
# When MathJax is enabled you can set the default output format to be used for
|
||||||
# the MathJax output. See the MathJax site (see:
|
# the MathJax output. See the MathJax site (see:
|
||||||
|
|
|
@ -24,6 +24,30 @@ Author: Romain Brenguier, romain.brenguier@diffblue.com
|
||||||
#include <solvers/refinement/bv_refinement.h>
|
#include <solvers/refinement/bv_refinement.h>
|
||||||
#include <solvers/refinement/string_refinement_invariant.h>
|
#include <solvers/refinement/string_refinement_invariant.h>
|
||||||
#include <util/refined_string_type.h>
|
#include <util/refined_string_type.h>
|
||||||
|
#include <langapi/language_util.h>
|
||||||
|
|
||||||
|
/*! \brief Universally quantified string constraint
|
||||||
|
|
||||||
|
This represents a universally quantified string constraint as laid out in
|
||||||
|
DOI: 10.1007/978-3-319-03077-7. The paper seems to specify a universal
|
||||||
|
constraint as follows.
|
||||||
|
|
||||||
|
A universal constraint is of the form \f$\forall n. L(n) \rightarrow
|
||||||
|
P(n, s_0,\ldots, s_k)\f$ where
|
||||||
|
|
||||||
|
1. \f$L(n)\f$ does not contain string indices [possibly not required, but
|
||||||
|
implied by examples]
|
||||||
|
2. \f$\forall n. L(n) \rightarrow P'\left(s_0[f_0(n)],\ldots, s_k[f_k(n)]
|
||||||
|
\right)\f$, i.e. when focusing on one specific string, all indices are
|
||||||
|
the same [stated in a roundabout manner]
|
||||||
|
3. Each \f$f\f$ is linear and therefore has an inverse [explicitly stated]
|
||||||
|
4. \f$L(n)\f$ and \f$P(n, s_0,\ldots, s_k)\f$ may contain other (free)
|
||||||
|
variables, but in \f$P\f$, \f$n\f$ can only occur as an argument to an
|
||||||
|
\f$f\f$ [explicitly stated, implied]
|
||||||
|
|
||||||
|
We extend this slightly by restricting n to be in a specific range, but this
|
||||||
|
is another implication which can be pushed in to \f$L(n)\f$.
|
||||||
|
*/
|
||||||
|
|
||||||
class string_constraintt: public exprt
|
class string_constraintt: public exprt
|
||||||
{
|
{
|
||||||
|
@ -114,6 +138,15 @@ extern inline string_constraintt &to_string_constraint(exprt &expr)
|
||||||
return static_cast<string_constraintt &>(expr);
|
return static_cast<string_constraintt &>(expr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline static std::string from_expr(
|
||||||
|
const namespacet &ns,
|
||||||
|
const irep_idt &identifier,
|
||||||
|
const string_constraintt &expr)
|
||||||
|
{
|
||||||
|
return from_expr(ns, identifier, expr.premise())+" => "+
|
||||||
|
from_expr(ns, identifier, expr.body());
|
||||||
|
}
|
||||||
|
|
||||||
class string_not_contains_constraintt: public exprt
|
class string_not_contains_constraintt: public exprt
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -15,7 +15,7 @@ Author: Romain Brenguier, romain.brenguier@diffblue.com
|
||||||
#include <solvers/refinement/string_constraint_generator.h>
|
#include <solvers/refinement/string_constraint_generator.h>
|
||||||
|
|
||||||
/// Add axioms stating that the returned value is the index within str of the
|
/// Add axioms stating that the returned value is the index within str of the
|
||||||
/// first occurence of c starting the search at from_index, or -1 if no such
|
/// first occurrence of c starting the search at from_index, or -1 if no such
|
||||||
/// character occurs at or after position from_index.
|
/// character occurs at or after position from_index.
|
||||||
/// \param str: a string expression
|
/// \param str: a string expression
|
||||||
/// \param c: an expression representing a character
|
/// \param c: an expression representing a character
|
||||||
|
@ -69,7 +69,7 @@ exprt string_constraint_generatort::add_axioms_for_index_of(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add axioms stating that the returned value is the index within haystack of
|
/// Add axioms stating that the returned value is the index within haystack of
|
||||||
/// the first occurence of needle starting the search at from_index, or -1 if
|
/// the first occurrence of needle starting the search at from_index, or -1 if
|
||||||
/// needle does not occur at or after position from_index.
|
/// needle does not occur at or after position from_index.
|
||||||
/// \param haystack: a string expression
|
/// \param haystack: a string expression
|
||||||
/// \param needle: a string expression
|
/// \param needle: a string expression
|
||||||
|
@ -92,7 +92,7 @@ exprt string_constraint_generatort::add_axioms_for_index_of_string(
|
||||||
// contains ==> haystack[n+offset]=needle[n]
|
// contains ==> haystack[n+offset]=needle[n]
|
||||||
// a4 : forall n:[from_index,offset[.
|
// a4 : forall n:[from_index,offset[.
|
||||||
// contains ==> (exists m:[0,|needle|[. haystack[m+n] != needle[m]])
|
// contains ==> (exists m:[0,|needle|[. haystack[m+n] != needle[m]])
|
||||||
// a5: forall n:[from_index,|haystack|-|needle|[.
|
// a5: forall n:[from_index,|haystack|-|needle|].
|
||||||
// !contains ==> (exists m:[0,|needle|[. haystack[m+n] != needle[m])
|
// !contains ==> (exists m:[0,|needle|[. haystack[m+n] != needle[m])
|
||||||
|
|
||||||
implies_exprt a1(
|
implies_exprt a1(
|
||||||
|
@ -116,70 +116,35 @@ exprt string_constraint_generatort::add_axioms_for_index_of_string(
|
||||||
equal_exprt(haystack[plus_exprt(qvar, offset)], needle[qvar]));
|
equal_exprt(haystack[plus_exprt(qvar, offset)], needle[qvar]));
|
||||||
axioms.push_back(a3);
|
axioms.push_back(a3);
|
||||||
|
|
||||||
if(!is_constant_string(needle))
|
// string_not contains_constraintt are formulas of the form:
|
||||||
{
|
// forall x in [lb,ub[. p(x) => exists y in [lb,ub[. s1[x+y] != s2[y]
|
||||||
// string_not contains_constraintt are formulas of the form:
|
string_not_contains_constraintt a4(
|
||||||
// forall x in [lb,ub[. p(x) => exists y in [lb,ub[. s1[x+y] != s2[y]
|
from_index,
|
||||||
string_not_contains_constraintt a4(
|
offset,
|
||||||
from_index,
|
contains,
|
||||||
offset,
|
from_integer(0, index_type),
|
||||||
contains,
|
needle.length(),
|
||||||
from_integer(0, index_type),
|
haystack,
|
||||||
needle.length(),
|
needle);
|
||||||
haystack,
|
axioms.push_back(a4);
|
||||||
needle);
|
|
||||||
axioms.push_back(a4);
|
|
||||||
|
|
||||||
string_not_contains_constraintt a5(
|
string_not_contains_constraintt a5(
|
||||||
from_index,
|
from_index,
|
||||||
|
plus_exprt( // Add 1 for inclusive upper bound.
|
||||||
minus_exprt(haystack.length(), needle.length()),
|
minus_exprt(haystack.length(), needle.length()),
|
||||||
not_exprt(contains),
|
from_integer(1, index_type)),
|
||||||
from_integer(0, index_type),
|
not_exprt(contains),
|
||||||
needle.length(),
|
from_integer(0, index_type),
|
||||||
haystack,
|
needle.length(),
|
||||||
needle);
|
haystack,
|
||||||
axioms.push_back(a5);
|
needle);
|
||||||
}
|
axioms.push_back(a5);
|
||||||
else
|
|
||||||
{
|
|
||||||
// Unfold the existential quantifier as a disjunction in case of a constant
|
|
||||||
// a4 && a5 <=> a6:
|
|
||||||
// forall n:[from_index,|haystack|-|needle|].
|
|
||||||
// !contains || n < offset ==>
|
|
||||||
// haystack[n] != needle[0] || ... ||
|
|
||||||
// haystack[n+|needle|-1] != needle[|needle|-1]
|
|
||||||
symbol_exprt qvar2=fresh_univ_index("QA_index_of_string_2", index_type);
|
|
||||||
mp_integer sub_length;
|
|
||||||
INVARIANT(
|
|
||||||
!to_integer(needle.length(), sub_length),
|
|
||||||
string_refinement_invariantt("a constant string must have constant "
|
|
||||||
"length"));
|
|
||||||
exprt::operandst disjuncts;
|
|
||||||
for(mp_integer offset=0; offset<sub_length; ++offset)
|
|
||||||
{
|
|
||||||
exprt expr_offset=from_integer(offset, index_type);
|
|
||||||
plus_exprt shifted(expr_offset, qvar2);
|
|
||||||
disjuncts.push_back(
|
|
||||||
not_exprt(equal_exprt(haystack[shifted], needle[expr_offset])));
|
|
||||||
}
|
|
||||||
|
|
||||||
or_exprt premise(
|
|
||||||
not_exprt(contains), binary_relation_exprt(qvar2, ID_lt, offset));
|
|
||||||
minus_exprt length_diff(haystack.length(), needle.length());
|
|
||||||
string_constraintt a6(
|
|
||||||
qvar2,
|
|
||||||
from_index,
|
|
||||||
plus_exprt(from_integer(1, index_type), length_diff),
|
|
||||||
premise,
|
|
||||||
disjunction(disjuncts));
|
|
||||||
axioms.push_back(a6);
|
|
||||||
}
|
|
||||||
|
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add axioms stating that the returned value is the index within haystack of
|
/// Add axioms stating that the returned value is the index within haystack of
|
||||||
/// the last occurence of needle starting the search backward at from_index (ie
|
/// the last occurrence of needle starting the search backward at from_index (ie
|
||||||
/// the index is smaller or equal to from_index), or -1 if needle does not occur
|
/// the index is smaller or equal to from_index), or -1 if needle does not occur
|
||||||
/// before from_index.
|
/// before from_index.
|
||||||
/// \param haystack: a string expression
|
/// \param haystack: a string expression
|
||||||
|
@ -235,62 +200,25 @@ exprt string_constraint_generatort::add_axioms_for_last_index_of_string(
|
||||||
from_index,
|
from_index,
|
||||||
length_diff);
|
length_diff);
|
||||||
|
|
||||||
if(!is_constant_string(needle))
|
string_not_contains_constraintt a4(
|
||||||
{
|
plus_exprt(offset, from_integer(1, index_type)),
|
||||||
string_not_contains_constraintt a4(
|
plus_exprt(end_index, from_integer(1, index_type)),
|
||||||
plus_exprt(offset, from_integer(1, index_type)),
|
contains,
|
||||||
plus_exprt(end_index, from_integer(1, index_type)),
|
from_integer(0, index_type),
|
||||||
contains,
|
needle.length(),
|
||||||
from_integer(0, index_type),
|
haystack,
|
||||||
needle.length(),
|
needle);
|
||||||
haystack,
|
axioms.push_back(a4);
|
||||||
needle);
|
|
||||||
axioms.push_back(a4);
|
|
||||||
|
|
||||||
string_not_contains_constraintt a5(
|
string_not_contains_constraintt a5(
|
||||||
from_integer(0, index_type),
|
from_integer(0, index_type),
|
||||||
plus_exprt(end_index, from_integer(1, index_type)),
|
plus_exprt(end_index, from_integer(1, index_type)),
|
||||||
not_exprt(contains),
|
not_exprt(contains),
|
||||||
from_integer(0, index_type),
|
from_integer(0, index_type),
|
||||||
needle.length(),
|
needle.length(),
|
||||||
haystack,
|
haystack,
|
||||||
needle);
|
needle);
|
||||||
axioms.push_back(a5);
|
axioms.push_back(a5);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Unfold the existential quantifier as a disjunction in case of a constant
|
|
||||||
// a4 && a5 <=> a6:
|
|
||||||
// forall n:[0, min(from_index, |haystack| - |needle|)].
|
|
||||||
// !contains || (n > offset) ==>
|
|
||||||
// haystack[n] != needle[0] || ... ||
|
|
||||||
// haystack[n+|needle|-1] != needle[|needle|-1]
|
|
||||||
symbol_exprt qvar2=fresh_univ_index("QA_index_of_string_2", index_type);
|
|
||||||
mp_integer sub_length;
|
|
||||||
INVARIANT(
|
|
||||||
!to_integer(needle.length(), sub_length),
|
|
||||||
string_refinement_invariantt("a constant string must have constant "
|
|
||||||
"length"));
|
|
||||||
exprt::operandst disjuncts;
|
|
||||||
for(mp_integer offset=0; offset<sub_length; ++offset)
|
|
||||||
{
|
|
||||||
exprt expr_offset=from_integer(offset, index_type);
|
|
||||||
plus_exprt shifted(expr_offset, qvar2);
|
|
||||||
disjuncts.push_back(
|
|
||||||
not_exprt(equal_exprt(haystack[shifted], needle[expr_offset])));
|
|
||||||
}
|
|
||||||
|
|
||||||
or_exprt premise(
|
|
||||||
not_exprt(contains), binary_relation_exprt(qvar2, ID_gt, offset));
|
|
||||||
|
|
||||||
string_constraintt a6(
|
|
||||||
qvar2,
|
|
||||||
from_integer(0, index_type),
|
|
||||||
plus_exprt(from_integer(1, index_type), end_index),
|
|
||||||
premise,
|
|
||||||
disjunction(disjuncts));
|
|
||||||
axioms.push_back(a6);
|
|
||||||
}
|
|
||||||
|
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
@ -333,7 +261,7 @@ exprt string_constraint_generatort::add_axioms_for_index_of(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add axioms stating that the returned value is the index within str of the
|
/// Add axioms stating that the returned value is the index within str of the
|
||||||
/// last occurence of c starting the search backward at from_index, or -1 if no
|
/// last occurrence of c starting the search backward at from_index, or -1 if no
|
||||||
/// such character occurs at or before position from_index.
|
/// such character occurs at or before position from_index.
|
||||||
/// \param str: a string expression
|
/// \param str: a string expression
|
||||||
/// \param c: an expression representing a character
|
/// \param c: an expression representing a character
|
||||||
|
@ -373,7 +301,7 @@ exprt string_constraint_generatort::add_axioms_for_last_index_of(
|
||||||
equal_exprt(str[index], c)));
|
equal_exprt(str[index], c)));
|
||||||
axioms.push_back(a3);
|
axioms.push_back(a3);
|
||||||
|
|
||||||
symbol_exprt n=fresh_univ_index("QA_last_index_of", index_type);
|
symbol_exprt n=fresh_univ_index("QA_last_index_of1", index_type);
|
||||||
string_constraintt a4(
|
string_constraintt a4(
|
||||||
n,
|
n,
|
||||||
plus_exprt(index, index1),
|
plus_exprt(index, index1),
|
||||||
|
@ -382,7 +310,7 @@ exprt string_constraint_generatort::add_axioms_for_last_index_of(
|
||||||
not_exprt(equal_exprt(str[n], c)));
|
not_exprt(equal_exprt(str[n], c)));
|
||||||
axioms.push_back(a4);
|
axioms.push_back(a4);
|
||||||
|
|
||||||
symbol_exprt m=fresh_univ_index("QA_last_index_of", index_type);
|
symbol_exprt m=fresh_univ_index("QA_last_index_of2", index_type);
|
||||||
string_constraintt a5(
|
string_constraintt a5(
|
||||||
m,
|
m,
|
||||||
from_index_plus_one,
|
from_index_plus_one,
|
||||||
|
|
|
@ -164,7 +164,6 @@ string_exprt string_constraint_generatort::convert_java_string_to_string_exprt(
|
||||||
{
|
{
|
||||||
java_content=dereference_exprt(java_content, java_content.type());
|
java_content=dereference_exprt(java_content, java_content.type());
|
||||||
}
|
}
|
||||||
|
|
||||||
refined_string_typet type(java_int_type(), java_char_type());
|
refined_string_typet type(java_int_type(), java_char_type());
|
||||||
|
|
||||||
return string_exprt(length, java_content, type);
|
return string_exprt(length, java_content, type);
|
||||||
|
|
|
@ -187,7 +187,6 @@ exprt string_constraint_generatort::add_axioms_for_contains(
|
||||||
PRECONDITION(f.type()==bool_typet() || f.type().id()==ID_c_bool);
|
PRECONDITION(f.type()==bool_typet() || f.type().id()==ID_c_bool);
|
||||||
string_exprt s0=get_string_expr(args(f, 2)[0]);
|
string_exprt s0=get_string_expr(args(f, 2)[0]);
|
||||||
string_exprt s1=get_string_expr(args(f, 2)[1]);
|
string_exprt s1=get_string_expr(args(f, 2)[1]);
|
||||||
bool constant=is_constant_string(s1);
|
|
||||||
|
|
||||||
symbol_exprt contains=fresh_boolean("contains");
|
symbol_exprt contains=fresh_boolean("contains");
|
||||||
const refined_string_typet ref_type=to_refined_string_type(s0.type());
|
const refined_string_typet ref_type=to_refined_string_type(s0.type());
|
||||||
|
@ -218,66 +217,24 @@ exprt string_constraint_generatort::add_axioms_for_contains(
|
||||||
equal_exprt(startpos, from_integer(-1, index_type)));
|
equal_exprt(startpos, from_integer(-1, index_type)));
|
||||||
axioms.push_back(a3);
|
axioms.push_back(a3);
|
||||||
|
|
||||||
if(constant)
|
symbol_exprt qvar=fresh_univ_index("QA_contains", index_type);
|
||||||
{
|
exprt qvar_shifted=plus_exprt(qvar, startpos);
|
||||||
// If the string is constant, we can use a more efficient axiom for a4:
|
string_constraintt a4(
|
||||||
// contains ==> AND_{i < |s1|} s1[i] = s0[startpos + i]
|
qvar, s1.length(), contains, equal_exprt(s1[qvar], s0[qvar_shifted]));
|
||||||
mp_integer s1_length;
|
axioms.push_back(a4);
|
||||||
INVARIANT(
|
|
||||||
!to_integer(s1.length(), s1_length),
|
|
||||||
string_refinement_invariantt("a constant string expression must have a "
|
|
||||||
"constant length"));
|
|
||||||
exprt::operandst conjuncts;
|
|
||||||
for(mp_integer i=0; i<s1_length; ++i)
|
|
||||||
{
|
|
||||||
exprt expr_i=from_integer(i, index_type);
|
|
||||||
plus_exprt shifted_i(expr_i, startpos);
|
|
||||||
conjuncts.push_back(equal_exprt(s1[expr_i], s0[shifted_i]));
|
|
||||||
}
|
|
||||||
implies_exprt a4(contains, conjunction(conjuncts));
|
|
||||||
axioms.push_back(a4);
|
|
||||||
|
|
||||||
// The a5 constraint for constant strings translates to:
|
// We rewrite axiom a4 as:
|
||||||
// !contains ==> |s1| > |s0| ||
|
// forall startpos <= |s0|-|s1|. (!contains && |s0| >= |s1|)
|
||||||
// (forall qvar <= |s0| - |s1|.
|
// ==> exists witness < |s1|. s1[witness] != s0[startpos+witness]
|
||||||
// !(AND_{i < |s1|} s1[i] == s0[i + qvar])
|
string_not_contains_constraintt a5(
|
||||||
//
|
from_integer(0, index_type),
|
||||||
// which we implement as:
|
plus_exprt(from_integer(1, index_type), length_diff),
|
||||||
// forall qvar <= |s0| - |s1|. (!contains && |s0| >= |s1|)
|
and_exprt(not_exprt(contains), s0.axiom_for_is_longer_than(s1)),
|
||||||
// ==> !(AND_{i < |s1|} (s1[i] == s0[qvar+i]))
|
from_integer(0, index_type),
|
||||||
symbol_exprt qvar=fresh_univ_index("QA_contains_constant", index_type);
|
s1.length(),
|
||||||
exprt::operandst conjuncts1;
|
s0,
|
||||||
for(mp_integer i=0; i<s1_length; ++i)
|
s1);
|
||||||
{
|
axioms.push_back(a5);
|
||||||
exprt expr_i=from_integer(i, index_type);
|
|
||||||
plus_exprt shifted_i(expr_i, qvar);
|
|
||||||
conjuncts1.push_back(equal_exprt(s1[expr_i], s0[shifted_i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
string_constraintt a5(
|
|
||||||
qvar,
|
|
||||||
plus_exprt(from_integer(1, index_type), length_diff),
|
|
||||||
and_exprt(not_exprt(contains), s0.axiom_for_is_longer_than(s1)),
|
|
||||||
not_exprt(conjunction(conjuncts1)));
|
|
||||||
axioms.push_back(a5);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
symbol_exprt qvar=fresh_univ_index("QA_contains", index_type);
|
|
||||||
exprt qvar_shifted=plus_exprt(qvar, startpos);
|
|
||||||
string_constraintt a4(
|
|
||||||
qvar, s1.length(), contains, equal_exprt(s1[qvar], s0[qvar_shifted]));
|
|
||||||
axioms.push_back(a4);
|
|
||||||
|
|
||||||
// We rewrite axiom a4 as:
|
|
||||||
// forall startpos <= |s0|-|s1|. (!contains && |s0| >= |s1|)
|
|
||||||
// ==> exists witness < |s1|. s1[witness] != s0[startpos+witness]
|
|
||||||
string_not_contains_constraintt a5(
|
|
||||||
from_integer(0, index_type),
|
|
||||||
plus_exprt(from_integer(1, index_type), length_diff),
|
|
||||||
and_exprt(not_exprt(contains), s0.axiom_for_is_longer_than(s1)),
|
|
||||||
from_integer(0, index_type), s1.length(), s0, s1);
|
|
||||||
axioms.push_back(a5);
|
|
||||||
}
|
|
||||||
return typecast_exprt(contains, f.type());
|
return typecast_exprt(contains, f.type());
|
||||||
}
|
}
|
||||||
|
|
|
@ -287,30 +287,38 @@ string_exprt string_constraint_generatort::add_axioms_for_to_upper_case(
|
||||||
exprt char_a=constant_char('a', char_type);
|
exprt char_a=constant_char('a', char_type);
|
||||||
exprt char_A=constant_char('A', char_type);
|
exprt char_A=constant_char('A', char_type);
|
||||||
exprt char_z=constant_char('z', char_type);
|
exprt char_z=constant_char('z', char_type);
|
||||||
exprt char_Z=constant_char('Z', char_type);
|
|
||||||
|
|
||||||
// TODO: add support for locales using case mapping information
|
// TODO: add support for locales using case mapping information
|
||||||
// from the UnicodeData file.
|
// from the UnicodeData file.
|
||||||
|
|
||||||
// We add axioms:
|
// We add axioms:
|
||||||
// a1 : |res| = |str|
|
// a1 : |res| = |str|
|
||||||
// a2 : forall idx<str.length, 'a'<=str[idx]<='z' => res[idx]=str[idx]+'A'-'a'
|
// a2 : forall idx1<str.length, 'a'<=str[idx1]<='z' =>
|
||||||
// a3 : forall idx<str.length, !('a'<=str[idx]<='z') => res[idx]=str[idx]
|
// res[idx1]=str[idx1]+'A'-'a'
|
||||||
|
// a3 : forall idx2<str.length, !('a'<=str[idx2]<='z') => res[idx2]=str[idx2]
|
||||||
|
// Note that index expressions are only allowed in the body of universal
|
||||||
|
// axioms, so we use a trivial premise and push our premise into the body.
|
||||||
|
|
||||||
exprt a1=res.axiom_for_has_same_length_as(str);
|
exprt a1=res.axiom_for_has_same_length_as(str);
|
||||||
axioms.push_back(a1);
|
axioms.push_back(a1);
|
||||||
|
|
||||||
symbol_exprt idx=fresh_univ_index("QA_upper_case", index_type);
|
symbol_exprt idx1=fresh_univ_index("QA_upper_case1", index_type);
|
||||||
exprt is_lower_case=and_exprt(
|
exprt is_lower_case=and_exprt(
|
||||||
binary_relation_exprt(char_a, ID_le, str[idx]),
|
binary_relation_exprt(char_a, ID_le, str[idx1]),
|
||||||
binary_relation_exprt(str[idx], ID_le, char_z));
|
binary_relation_exprt(str[idx1], ID_le, char_z));
|
||||||
minus_exprt diff(char_A, char_a);
|
minus_exprt diff(char_A, char_a);
|
||||||
equal_exprt convert(res[idx], plus_exprt(str[idx], diff));
|
equal_exprt convert(res[idx1], plus_exprt(str[idx1], diff));
|
||||||
string_constraintt a2(idx, res.length(), is_lower_case, convert);
|
implies_exprt body1(is_lower_case, convert);
|
||||||
|
string_constraintt a2(idx1, res.length(), body1);
|
||||||
axioms.push_back(a2);
|
axioms.push_back(a2);
|
||||||
|
|
||||||
equal_exprt eq(res[idx], str[idx]);
|
symbol_exprt idx2=fresh_univ_index("QA_upper_case2", index_type);
|
||||||
string_constraintt a3(idx, res.length(), not_exprt(is_lower_case), eq);
|
exprt is_not_lower_case=not_exprt(and_exprt(
|
||||||
|
binary_relation_exprt(char_a, ID_le, str[idx2]),
|
||||||
|
binary_relation_exprt(str[idx2], ID_le, char_z)));
|
||||||
|
equal_exprt eq(res[idx2], str[idx2]);
|
||||||
|
implies_exprt body2(is_not_lower_case, eq);
|
||||||
|
string_constraintt a3(idx2, res.length(), body2);
|
||||||
axioms.push_back(a3);
|
axioms.push_back(a3);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ Author: Alberto Griggio, alberto.griggio@gmail.com
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
#include <stack>
|
||||||
#include <ansi-c/string_constant.h>
|
#include <ansi-c/string_constant.h>
|
||||||
#include <util/cprover_prefix.h>
|
#include <util/cprover_prefix.h>
|
||||||
#include <util/replace_expr.h>
|
#include <util/replace_expr.h>
|
||||||
|
@ -210,7 +211,7 @@ void string_refinementt::set_char_array_equality(
|
||||||
|
|
||||||
/// remove functions applications and create the necessary axioms
|
/// remove functions applications and create the necessary axioms
|
||||||
/// \par parameters: an expression containing function applications
|
/// \par parameters: an expression containing function applications
|
||||||
/// \return an epression containing no function application
|
/// \return an expression containing no function application
|
||||||
exprt string_refinementt::substitute_function_applications(exprt expr)
|
exprt string_refinementt::substitute_function_applications(exprt expr)
|
||||||
{
|
{
|
||||||
for(size_t i=0; i<expr.operands().size(); ++i)
|
for(size_t i=0; i<expr.operands().size(); ++i)
|
||||||
|
@ -517,6 +518,10 @@ decision_proceduret::resultt string_refinementt::dec_solve()
|
||||||
if(axiom.id()==ID_string_constraint)
|
if(axiom.id()==ID_string_constraint)
|
||||||
{
|
{
|
||||||
string_constraintt c=to_string_constraint(axiom);
|
string_constraintt c=to_string_constraint(axiom);
|
||||||
|
DATA_INVARIANT(
|
||||||
|
is_valid_string_constraint(c),
|
||||||
|
string_refinement_invariantt(
|
||||||
|
"string constraints satisfy their invariant"));
|
||||||
universal_axioms.push_back(c);
|
universal_axioms.push_back(c);
|
||||||
}
|
}
|
||||||
else if(axiom.id()==ID_string_not_contains_constraint)
|
else if(axiom.id()==ID_string_not_contains_constraint)
|
||||||
|
@ -790,7 +795,7 @@ exprt string_refinementt::get_array(const exprt &arr) const
|
||||||
}
|
}
|
||||||
|
|
||||||
/// convert the content of a string to a more readable representation. This
|
/// convert the content of a string to a more readable representation. This
|
||||||
/// should only be used for debbuging.
|
/// should only be used for debugging.
|
||||||
/// \par parameters: a constant array expression and a integer expression
|
/// \par parameters: a constant array expression and a integer expression
|
||||||
/// \return a string
|
/// \return a string
|
||||||
std::string string_refinementt::string_of_array(const array_exprt &arr)
|
std::string string_refinementt::string_of_array(const array_exprt &arr)
|
||||||
|
@ -1017,20 +1022,19 @@ void string_refinementt::substitute_array_access(exprt &expr) const
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Negates the constraint to be fed to a solver. The intended usage is to find
|
/// Negates the constraint to be fed to a solver. The intended usage is to find
|
||||||
/// an assignement of the universal variable that would violate the axiom. To
|
/// an assignment of the universal variable that would violate the axiom. To
|
||||||
/// avoid false positives, the symbols other than the universal variable should
|
/// avoid false positives, the symbols other than the universal variable should
|
||||||
/// have been replaced by their valuation in the current model.
|
/// have been replaced by their valuation in the current model.
|
||||||
/// \pre Symbols other than the universal variable should have been replaced by
|
/// \pre Symbols other than the universal variable should have been replaced by
|
||||||
/// their valuation in the current model.
|
/// their valuation in the current model.
|
||||||
/// \param axiom: the not_contains constraint to add the negation of
|
/// \param axiom: the not_contains constraint to add the negation of
|
||||||
/// \param val: the existential witness for the axiom
|
|
||||||
/// \param univ_var: the universal variable for the negation of the axiom
|
/// \param univ_var: the universal variable for the negation of the axiom
|
||||||
/// \return: the negation of the axiom under the current evaluation
|
/// \return: the negation of the axiom under the current evaluation
|
||||||
exprt string_refinementt::negation_of_not_contains_constraint(
|
static exprt negation_of_not_contains_constraint(
|
||||||
const string_not_contains_constraintt &axiom,
|
const string_not_contains_constraintt &axiom,
|
||||||
const exprt &val,
|
|
||||||
const symbol_exprt &univ_var)
|
const symbol_exprt &univ_var)
|
||||||
{
|
{
|
||||||
|
// If the for all is vacuously true, the negation is false.
|
||||||
exprt lbu=axiom.univ_lower_bound();
|
exprt lbu=axiom.univ_lower_bound();
|
||||||
exprt ubu=axiom.univ_upper_bound();
|
exprt ubu=axiom.univ_upper_bound();
|
||||||
if(lbu.id()==ID_constant && ubu.id()==ID_constant)
|
if(lbu.id()==ID_constant && ubu.id()==ID_constant)
|
||||||
|
@ -1039,51 +1043,53 @@ exprt string_refinementt::negation_of_not_contains_constraint(
|
||||||
to_integer(to_constant_expr(lbu), lb_int);
|
to_integer(to_constant_expr(lbu), lb_int);
|
||||||
to_integer(to_constant_expr(ubu), ub_int);
|
to_integer(to_constant_expr(ubu), ub_int);
|
||||||
if(ub_int<=lb_int)
|
if(ub_int<=lb_int)
|
||||||
{
|
|
||||||
debug() << "empty constraint with current model" << eom;
|
|
||||||
return false_exprt();
|
return false_exprt();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exprt lbe=axiom.exists_lower_bound();
|
exprt lbe=axiom.exists_lower_bound();
|
||||||
exprt ube=axiom.exists_upper_bound();
|
exprt ube=axiom.exists_upper_bound();
|
||||||
|
|
||||||
if(axiom.premise()==false_exprt())
|
mp_integer lbe_int, ube_int;
|
||||||
{
|
to_integer(to_constant_expr(lbe), lbe_int);
|
||||||
debug() << "(string_refinement::check_axioms) adding false" << eom;
|
to_integer(to_constant_expr(ube), ube_int);
|
||||||
return false_exprt();
|
|
||||||
}
|
// If the premise is false, the implication is trivially true, so the
|
||||||
|
// negation is false.
|
||||||
|
if(axiom.premise()==false_exprt())
|
||||||
|
return false_exprt();
|
||||||
|
|
||||||
// Witness is the Skolem function for the existential, which we evaluate at
|
|
||||||
// univ_var.
|
|
||||||
and_exprt univ_bounds(
|
and_exprt univ_bounds(
|
||||||
binary_relation_exprt(lbu, ID_le, univ_var),
|
binary_relation_exprt(lbu, ID_le, univ_var),
|
||||||
binary_relation_exprt(ubu, ID_gt, univ_var));
|
binary_relation_exprt(ubu, ID_gt, univ_var));
|
||||||
and_exprt exists_bounds(
|
|
||||||
binary_relation_exprt(lbe, ID_le, val),
|
|
||||||
binary_relation_exprt(ube, ID_gt, val));
|
|
||||||
equal_exprt equal_chars(
|
|
||||||
axiom.s0()[plus_exprt(univ_var, val)],
|
|
||||||
axiom.s1()[val]);
|
|
||||||
and_exprt negaxiom(univ_bounds, axiom.premise(), exists_bounds, equal_chars);
|
|
||||||
|
|
||||||
debug() << "(sr::check_axioms) negated not_contains axiom: "
|
// The negated existential becomes an universal, and this is the unrolling of
|
||||||
<< from_expr(ns, "", negaxiom) << eom;
|
// that universal quantifier.
|
||||||
substitute_array_access(negaxiom);
|
std::vector<exprt> conjuncts;
|
||||||
|
for(mp_integer i=lbe_int; i<ube_int; ++i)
|
||||||
|
{
|
||||||
|
const constant_exprt i_exprt=from_integer(i, univ_var.type());
|
||||||
|
const equal_exprt equal_chars(
|
||||||
|
axiom.s0()[plus_exprt(univ_var, i_exprt)],
|
||||||
|
axiom.s1()[i_exprt]);
|
||||||
|
conjuncts.push_back(equal_chars);
|
||||||
|
}
|
||||||
|
exprt equal_strings=conjunction(conjuncts);
|
||||||
|
and_exprt negaxiom(univ_bounds, axiom.premise(), equal_strings);
|
||||||
|
|
||||||
return negaxiom;
|
return negaxiom;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Negates the constraint to be fed to a solver. The intended usage is to find
|
/// Negates the constraint to be fed to a solver. The intended usage is to find
|
||||||
/// an assignement of the universal variable that would violate the axiom. To
|
/// an assignment of the universal variable that would violate the axiom. To
|
||||||
/// avoid false positives, the symbols other than the universal variable should
|
/// avoid false positives, the symbols other than the universal variable should
|
||||||
/// have been replaced by their valuation in the current model.
|
/// have been replaced by their valuation in the current model.
|
||||||
/// \pre Symbols other than the universal variable should have been replaced by
|
/// \pre Symbols other than the universal variable should have been replaced by
|
||||||
/// their valuation in the current model.
|
/// their valuation in the current model.
|
||||||
/// \param axiom: the not_contains constraint to add the negation of
|
/// \param axiom: the not_contains constraint to add the negation of
|
||||||
/// \return: the negation of the axiom under the current evaluation
|
/// \return: the negation of the axiom under the current evaluation
|
||||||
exprt string_refinementt::negation_of_constraint(
|
static exprt negation_of_constraint(const string_constraintt &axiom)
|
||||||
const string_constraintt &axiom)
|
|
||||||
{
|
{
|
||||||
|
// If the for all is vacuously true, the negation is false.
|
||||||
exprt lb=axiom.lower_bound();
|
exprt lb=axiom.lower_bound();
|
||||||
exprt ub=axiom.upper_bound();
|
exprt ub=axiom.upper_bound();
|
||||||
if(lb.id()==ID_constant && ub.id()==ID_constant)
|
if(lb.id()==ID_constant && ub.id()==ID_constant)
|
||||||
|
@ -1092,24 +1098,17 @@ exprt string_refinementt::negation_of_constraint(
|
||||||
to_integer(to_constant_expr(lb), lb_int);
|
to_integer(to_constant_expr(lb), lb_int);
|
||||||
to_integer(to_constant_expr(ub), ub_int);
|
to_integer(to_constant_expr(ub), ub_int);
|
||||||
if(ub_int<=lb_int)
|
if(ub_int<=lb_int)
|
||||||
{
|
|
||||||
debug() << "empty constraint with current model" << eom;
|
|
||||||
return false_exprt();
|
return false_exprt();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the premise is false, the implication is trivially true, so the
|
||||||
|
// negation is false.
|
||||||
if(axiom.premise()==false_exprt())
|
if(axiom.premise()==false_exprt())
|
||||||
{
|
|
||||||
debug() << "(string_refinement::check_axioms) adding false" << eom;
|
|
||||||
return false_exprt();
|
return false_exprt();
|
||||||
}
|
|
||||||
|
|
||||||
and_exprt premise(axiom.premise(), axiom.univ_within_bounds());
|
and_exprt premise(axiom.premise(), axiom.univ_within_bounds());
|
||||||
and_exprt negaxiom(premise, not_exprt(axiom.body()));
|
and_exprt negaxiom(premise, not_exprt(axiom.body()));
|
||||||
|
|
||||||
debug() << "(sr::check_axioms) negated axiom: "
|
|
||||||
<< from_expr(ns, "", negaxiom) << eom;
|
|
||||||
substitute_array_access(negaxiom);
|
|
||||||
return negaxiom;
|
return negaxiom;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1140,7 +1139,10 @@ bool string_refinementt::check_axioms()
|
||||||
const string_constraintt axiom_in_model(
|
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(prem), get(body));
|
||||||
|
|
||||||
const exprt negaxiom=negation_of_constraint(axiom_in_model);
|
exprt negaxiom=negation_of_constraint(axiom_in_model);
|
||||||
|
debug() << "(string_refinementt::check_axioms) Adding negated constraint: "
|
||||||
|
<< from_expr(ns, "", negaxiom) << eom;
|
||||||
|
substitute_array_access(negaxiom);
|
||||||
exprt witness;
|
exprt witness;
|
||||||
|
|
||||||
bool is_sat=is_axiom_sat(negaxiom, univ_var, witness);
|
bool is_sat=is_axiom_sat(negaxiom, univ_var, witness);
|
||||||
|
@ -1172,8 +1174,6 @@ bool string_refinementt::check_axioms()
|
||||||
|
|
||||||
symbol_exprt univ_var=generator.fresh_univ_index(
|
symbol_exprt univ_var=generator.fresh_univ_index(
|
||||||
"not_contains_univ_var", nc_axiom.s0().length().type());
|
"not_contains_univ_var", nc_axiom.s0().length().type());
|
||||||
exprt wit=generator.get_witness_of(nc_axiom, univ_var);
|
|
||||||
exprt val=get(wit);
|
|
||||||
const string_not_contains_constraintt nc_axiom_in_model(
|
const string_not_contains_constraintt nc_axiom_in_model(
|
||||||
get(univ_bound_inf),
|
get(univ_bound_inf),
|
||||||
get(univ_bound_sup),
|
get(univ_bound_sup),
|
||||||
|
@ -1183,8 +1183,11 @@ bool string_refinementt::check_axioms()
|
||||||
to_string_expr(get(s0)),
|
to_string_expr(get(s0)),
|
||||||
to_string_expr(get(s1)));
|
to_string_expr(get(s1)));
|
||||||
|
|
||||||
const exprt negaxiom=negation_of_not_contains_constraint(
|
exprt negaxiom=negation_of_not_contains_constraint(
|
||||||
nc_axiom_in_model, val, univ_var);
|
nc_axiom_in_model, univ_var);
|
||||||
|
debug() << "(string_refinementt::check_axioms) Adding negated constraint: "
|
||||||
|
<< from_expr(ns, "", negaxiom) << eom;
|
||||||
|
substitute_array_access(negaxiom);
|
||||||
exprt witness;
|
exprt witness;
|
||||||
|
|
||||||
bool is_sat=is_axiom_sat(negaxiom, univ_var, witness);
|
bool is_sat=is_axiom_sat(negaxiom, univ_var, witness);
|
||||||
|
@ -1235,7 +1238,7 @@ bool string_refinementt::check_axioms()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \par parameters: an expression with only addition and substraction
|
/// \par parameters: an expression with only addition and subtraction
|
||||||
/// \return a map where each leaf of the input is mapped to the number of times
|
/// \return a map where each leaf of the input is mapped to the number of times
|
||||||
/// it is added. For instance, expression $x + x - y$ would give the map x ->
|
/// it is added. For instance, expression $x + x - y$ would give the map x ->
|
||||||
/// 2, y -> -1.
|
/// 2, y -> -1.
|
||||||
|
@ -1352,7 +1355,7 @@ exprt string_refinementt::sum_over_map(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \par parameters: an expression with only plus and minus expr
|
/// \par parameters: an expression with only plus and minus expr
|
||||||
/// \return an equivalent expression in a cannonical form
|
/// \return an equivalent expression in a canonical form
|
||||||
exprt string_refinementt::simplify_sum(const exprt &f) const
|
exprt string_refinementt::simplify_sum(const exprt &f) const
|
||||||
{
|
{
|
||||||
std::map<exprt, int> map=map_representation_of_sum(f);
|
std::map<exprt, int> map=map_representation_of_sum(f);
|
||||||
|
@ -1372,7 +1375,7 @@ exprt string_refinementt::compute_inverse_function(
|
||||||
exprt positive, negative;
|
exprt positive, negative;
|
||||||
// number of time the element should be added (can be negative)
|
// number of time the element should be added (can be negative)
|
||||||
// qvar has to be equal to val - f(0) if it appears positively in f
|
// qvar has to be equal to val - f(0) if it appears positively in f
|
||||||
// (ie if f(qvar)=f(0) + qvar) and f(0) - val if it appears negatively
|
// (i.e. if f(qvar)=f(0) + qvar) and f(0) - val if it appears negatively
|
||||||
// in f. So we start by computing val - f(0).
|
// in f. So we start by computing val - f(0).
|
||||||
std::map<exprt, int> elems=map_representation_of_sum(minus_exprt(val, f));
|
std::map<exprt, int> elems=map_representation_of_sum(minus_exprt(val, f));
|
||||||
|
|
||||||
|
@ -1608,7 +1611,7 @@ exprt find_index(const exprt &expr, const exprt &str, const symbol_exprt &qvar)
|
||||||
/// \return substitute `qvar` the universally quantified variable of `axiom`, by
|
/// \return substitute `qvar` the universally quantified variable of `axiom`, by
|
||||||
/// an index `val`, in `axiom`, so that the index used for `str` equals `val`.
|
/// an index `val`, in `axiom`, so that the index used for `str` equals `val`.
|
||||||
/// For instance, if `axiom` corresponds to $\forall q. s[q+x]='a' &&
|
/// For instance, if `axiom` corresponds to $\forall q. s[q+x]='a' &&
|
||||||
/// t[q]='b'$, `instantiate(axom,s,v)` would return an expression for
|
/// t[q]='b'$, `instantiate(axiom,s,v)` would return an expression for
|
||||||
/// $s[v]='a' && t[v-x]='b'$.
|
/// $s[v]='a' && t[v-x]='b'$.
|
||||||
exprt string_refinementt::instantiate(
|
exprt string_refinementt::instantiate(
|
||||||
const string_constraintt &axiom, const exprt &str, const exprt &val)
|
const string_constraintt &axiom, const exprt &str, const exprt &val)
|
||||||
|
@ -1687,7 +1690,7 @@ void string_refinementt::instantiate_not_contains(
|
||||||
|
|
||||||
/// replace array-lists by 'with' expressions
|
/// replace array-lists by 'with' expressions
|
||||||
/// \par parameters: an expression containing array-list expressions
|
/// \par parameters: an expression containing array-list expressions
|
||||||
/// \return an epression containing no array-list
|
/// \return an expression containing no array-list
|
||||||
exprt string_refinementt::substitute_array_lists(exprt expr) const
|
exprt string_refinementt::substitute_array_lists(exprt expr) const
|
||||||
{
|
{
|
||||||
for(size_t i=0; i<expr.operands().size(); ++i)
|
for(size_t i=0; i<expr.operands().size(); ++i)
|
||||||
|
@ -1741,6 +1744,16 @@ exprt string_refinementt::get(const exprt &expr) const
|
||||||
if(it!=found_length.end())
|
if(it!=found_length.end())
|
||||||
return get_array(ecopy, it->second);
|
return get_array(ecopy, it->second);
|
||||||
}
|
}
|
||||||
|
else if(refined_string_typet::is_refined_string_type(ecopy.type()) &&
|
||||||
|
ecopy.id()==ID_struct)
|
||||||
|
{
|
||||||
|
const string_exprt &string=to_string_expr(ecopy);
|
||||||
|
const exprt &content=string.content();
|
||||||
|
const exprt &length=string.length();
|
||||||
|
|
||||||
|
const exprt arr=get_array(content, length);
|
||||||
|
ecopy=string_exprt(length, arr, string.type());
|
||||||
|
}
|
||||||
|
|
||||||
ecopy=supert::get(ecopy);
|
ecopy=supert::get(ecopy);
|
||||||
|
|
||||||
|
@ -1748,7 +1761,7 @@ exprt string_refinementt::get(const exprt &expr) const
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a solver with `axiom` as the only formula added and runs it. If it
|
/// Creates a solver with `axiom` as the only formula added and runs it. If it
|
||||||
/// is SAT, then true is returned and the given evalutation of `var` is stored
|
/// is SAT, then true is returned and the given evaluation of `var` is stored
|
||||||
/// in `witness`. If UNSAT, then what witness is is undefined.
|
/// in `witness`. If UNSAT, then what witness is is undefined.
|
||||||
/// \param [in] axiom: the axiom to be checked
|
/// \param [in] axiom: the axiom to be checked
|
||||||
/// \param [in] var: the variable whose evaluation will be stored in witness
|
/// \param [in] var: the variable whose evaluation will be stored in witness
|
||||||
|
@ -1772,9 +1785,172 @@ bool string_refinementt::is_axiom_sat(
|
||||||
}
|
}
|
||||||
case decision_proceduret::resultt::D_UNSATISFIABLE:
|
case decision_proceduret::resultt::D_UNSATISFIABLE:
|
||||||
return false;
|
return false;
|
||||||
|
case decision_proceduret::resultt::D_ERROR:
|
||||||
default:
|
default:
|
||||||
INVARIANT(false, string_refinement_invariantt("failure in checking axiom"));
|
INVARIANT(false, string_refinement_invariantt("failure in checking axiom"));
|
||||||
// To tell the compiler that the previous line bails
|
// To tell the compiler that the previous line bails
|
||||||
throw 0;
|
throw 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// \related string_constraintt
|
||||||
|
typedef std::map<exprt, std::vector<exprt>> array_index_mapt;
|
||||||
|
|
||||||
|
/// \related string_constraintt
|
||||||
|
class gather_indices_visitort: public const_expr_visitort
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
array_index_mapt indices;
|
||||||
|
|
||||||
|
gather_indices_visitort(): indices() {}
|
||||||
|
|
||||||
|
void operator()(const exprt &expr) override
|
||||||
|
{
|
||||||
|
if(expr.id()==ID_index)
|
||||||
|
{
|
||||||
|
const index_exprt index=to_index_expr(expr);
|
||||||
|
const exprt s(index.array());
|
||||||
|
const exprt i(index.index());
|
||||||
|
indices[s].push_back(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \related string_constraintt
|
||||||
|
static array_index_mapt gather_indices(const exprt &expr)
|
||||||
|
{
|
||||||
|
gather_indices_visitort v;
|
||||||
|
expr.visit(v);
|
||||||
|
return v.indices;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// \related string_constraintt
|
||||||
|
class is_linear_arithmetic_expr_visitort: public const_expr_visitort
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bool correct;
|
||||||
|
|
||||||
|
is_linear_arithmetic_expr_visitort(): correct(true) {}
|
||||||
|
|
||||||
|
void operator()(const exprt &expr) override
|
||||||
|
{
|
||||||
|
if(expr.id()!=ID_plus && expr.id()!=ID_minus && expr.id()!=ID_unary_minus)
|
||||||
|
{
|
||||||
|
// This represents that the expr is a valid leaf, may not be future proof
|
||||||
|
// or 100% enforced, but is correct prescriptively. All non-sum exprs must
|
||||||
|
// be leaves.
|
||||||
|
correct&=expr.operands().empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// \related string_constraintt
|
||||||
|
static bool is_linear_arithmetic_expr(const exprt &expr)
|
||||||
|
{
|
||||||
|
is_linear_arithmetic_expr_visitort v;
|
||||||
|
expr.visit(v);
|
||||||
|
return v.correct;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The universally quantified variable is only allowed to occur in index
|
||||||
|
/// expressions in the body of a string constraint. This function returns true
|
||||||
|
/// if this is the case and false otherwise.
|
||||||
|
/// \related string_constraintt
|
||||||
|
/// \param [in] expr: The string constraint to check
|
||||||
|
/// \return true if the universal variable only occurs in index expressions,
|
||||||
|
/// false otherwise.
|
||||||
|
static bool universal_only_in_index(const string_constraintt &expr)
|
||||||
|
{
|
||||||
|
// For efficiency, we do a depth-first search of the
|
||||||
|
// body. The exprt visitors do a BFS and hide the stack/queue, so we would
|
||||||
|
// need to store a map from child to parent.
|
||||||
|
|
||||||
|
// The unsigned int represents index depth we are. For example, if we are
|
||||||
|
// considering the fragment `a[b[x]]` (not inside an index expression), then
|
||||||
|
// the stack would look something like `[..., (a, 0), (b, 1), (x, 2)]`.
|
||||||
|
typedef std::pair<exprt, unsigned> valuet;
|
||||||
|
std::stack<valuet> stack;
|
||||||
|
// We start at 0 since expr is not an index expression, so expr.body() is not
|
||||||
|
// in an index expression.
|
||||||
|
stack.push(valuet(expr.body(), 0));
|
||||||
|
while(!stack.empty())
|
||||||
|
{
|
||||||
|
// Inspect current value
|
||||||
|
const valuet cur=stack.top();
|
||||||
|
stack.pop();
|
||||||
|
const exprt e=cur.first;
|
||||||
|
const unsigned index_depth=cur.second;
|
||||||
|
const unsigned child_index_depth=index_depth+(e.id()==ID_index?0:1);
|
||||||
|
|
||||||
|
// If we found the universal variable not in an index_exprt, fail
|
||||||
|
if(e==expr.univ_var() && index_depth==0)
|
||||||
|
return false;
|
||||||
|
else
|
||||||
|
forall_operands(it, e)
|
||||||
|
stack.push(valuet(*it, child_index_depth));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks the data invariant for \link string_constraintt
|
||||||
|
/// \related string_constraintt
|
||||||
|
/// \param [in] expr: the string constraint to check
|
||||||
|
/// \return whether the constraint satisfies the invariant
|
||||||
|
bool string_refinementt::is_valid_string_constraint(
|
||||||
|
const string_constraintt &expr)
|
||||||
|
{
|
||||||
|
// Condition 1: The premise cannot contain any string indices
|
||||||
|
const array_index_mapt premise_indices=gather_indices(expr.premise());
|
||||||
|
if(!premise_indices.empty())
|
||||||
|
{
|
||||||
|
error() << "Premise has indices: " << from_expr(ns, "", expr) << ", map: {";
|
||||||
|
for(const auto &pair : premise_indices)
|
||||||
|
{
|
||||||
|
error() << from_expr(ns, "", pair.first) << ": {";
|
||||||
|
for(const auto &i : pair.second)
|
||||||
|
error() << from_expr(ns, "", i) << ", ";
|
||||||
|
}
|
||||||
|
error() << "}}" << eom;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const array_index_mapt body_indices=gather_indices(expr.body());
|
||||||
|
// Must validate for each string. Note that we have an invariant that the
|
||||||
|
// second value in the pair is non-empty.
|
||||||
|
for(const auto &pair : body_indices)
|
||||||
|
{
|
||||||
|
// Condition 2: All indices of the same string must be the of the same form
|
||||||
|
const exprt rep=pair.second.back();
|
||||||
|
for(size_t j=0; j<pair.second.size()-1; j++)
|
||||||
|
{
|
||||||
|
const exprt i=pair.second[j];
|
||||||
|
const equal_exprt equals(rep, i);
|
||||||
|
const exprt result=simplify_expr(equals, ns);
|
||||||
|
if(result.is_false())
|
||||||
|
{
|
||||||
|
error() << "Indices not equal: " << from_expr(ns, "", expr) << ", str: "
|
||||||
|
<< from_expr(ns, "", pair.first) << eom;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Condition 3: f must be linear
|
||||||
|
if(!is_linear_arithmetic_expr(rep))
|
||||||
|
{
|
||||||
|
error() << "f is not linear: " << from_expr(ns, "", expr) << ", str: "
|
||||||
|
<< from_expr(ns, "", pair.first) << eom;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Condition 4: the quantified variable can only occur in indices in the
|
||||||
|
// body
|
||||||
|
if(!universal_only_in_index(expr))
|
||||||
|
{
|
||||||
|
error() << "Universal variable outside of index:"
|
||||||
|
<< from_expr(ns, "", expr) << eom;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
|
@ -118,11 +118,6 @@ private:
|
||||||
void set_to(const exprt &expr, bool value) override;
|
void set_to(const exprt &expr, bool value) override;
|
||||||
|
|
||||||
void add_instantiations();
|
void add_instantiations();
|
||||||
exprt negation_of_not_contains_constraint(
|
|
||||||
const string_not_contains_constraintt &axiom,
|
|
||||||
const exprt &val,
|
|
||||||
const symbol_exprt &univ_var);
|
|
||||||
exprt negation_of_constraint(const string_constraintt &axiom);
|
|
||||||
void debug_model();
|
void debug_model();
|
||||||
bool check_axioms();
|
bool check_axioms();
|
||||||
bool is_axiom_sat(
|
bool is_axiom_sat(
|
||||||
|
@ -151,6 +146,8 @@ private:
|
||||||
exprt sum_over_map(
|
exprt sum_over_map(
|
||||||
std::map<exprt, int> &m, const typet &type, bool negated=false) const;
|
std::map<exprt, int> &m, const typet &type, bool negated=false) const;
|
||||||
|
|
||||||
|
bool is_valid_string_constraint(const string_constraintt &expr);
|
||||||
|
|
||||||
exprt simplify_sum(const exprt &f) const;
|
exprt simplify_sum(const exprt &f) const;
|
||||||
template <typename T1, typename T2>
|
template <typename T1, typename T2>
|
||||||
void pad_vector(
|
void pad_vector(
|
||||||
|
|
Loading…
Reference in New Issue