mirror of
https://github.com/Z3Prover/z3
synced 2025-08-08 20:21:23 +00:00
z3str3: move bitvector model construction to theory_str_mc
This commit is contained in:
parent
faf3934749
commit
5a9a173c5f
2 changed files with 684 additions and 683 deletions
|
@ -9672,689 +9672,6 @@ namespace smt {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Use the current model in the arithmetic solver to get the length of a term.
|
|
||||||
* Returns true if this could be done, placing result in 'termLen', or false otherwise.
|
|
||||||
* Works like get_len_value() except uses arithmetic solver model instead of EQCs.
|
|
||||||
*/
|
|
||||||
bool theory_str::fixed_length_get_len_value(expr * e, rational & val) {
|
|
||||||
ast_manager & m = get_manager();
|
|
||||||
|
|
||||||
rational val1;
|
|
||||||
expr_ref len(m), len_val(m);
|
|
||||||
expr* e1, *e2;
|
|
||||||
expr_ref_vector todo(m);
|
|
||||||
todo.push_back(e);
|
|
||||||
val.reset();
|
|
||||||
while (!todo.empty()) {
|
|
||||||
expr* c = todo.back();
|
|
||||||
todo.pop_back();
|
|
||||||
if (u.str.is_concat(to_app(c))) {
|
|
||||||
e1 = to_app(c)->get_arg(0);
|
|
||||||
e2 = to_app(c)->get_arg(1);
|
|
||||||
todo.push_back(e1);
|
|
||||||
todo.push_back(e2);
|
|
||||||
}
|
|
||||||
else if (u.str.is_string(to_app(c))) {
|
|
||||||
zstring tmp;
|
|
||||||
u.str.is_string(to_app(c), tmp);
|
|
||||||
unsigned int sl = tmp.length();
|
|
||||||
val += rational(sl);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
len = mk_strlen(c);
|
|
||||||
arith_value v(get_manager());
|
|
||||||
v.init(&get_context());
|
|
||||||
if (v.get_value(len, val1)) {
|
|
||||||
val += val1;
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return val.is_int();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool theory_str::fixed_length_reduce_suffix(smt::kernel & subsolver, expr_ref f, expr_ref & cex) {
|
|
||||||
ast_manager & m = get_manager();
|
|
||||||
context & ctx = get_context();
|
|
||||||
|
|
||||||
ast_manager & sub_m = subsolver.m();
|
|
||||||
context & sub_ctx = subsolver.get_context();
|
|
||||||
|
|
||||||
expr * full;
|
|
||||||
expr * suff;
|
|
||||||
u.str.is_suffix(f, suff, full);
|
|
||||||
|
|
||||||
expr_ref haystack(full, m);
|
|
||||||
expr_ref needle(suff, m);
|
|
||||||
|
|
||||||
ptr_vector<expr> full_chars(fixed_length_reduce_string_term(subsolver, haystack));
|
|
||||||
ptr_vector<expr> suff_chars(fixed_length_reduce_string_term(subsolver, needle));
|
|
||||||
|
|
||||||
if (suff_chars.size() == 0) {
|
|
||||||
// all strings endwith the empty one
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (full_chars.size() == 0 && suff_chars.size() > 0) {
|
|
||||||
// the empty string doesn't "endwith" any non-empty string
|
|
||||||
cex = m.mk_or(m.mk_not(f), ctx.mk_eq_atom(mk_strlen(suff), mk_int(0)),
|
|
||||||
m_autil.mk_ge(mk_strlen(full), mk_int(0)));
|
|
||||||
th_rewriter m_rw(m);
|
|
||||||
m_rw(cex);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (full_chars.size() < suff_chars.size()) {
|
|
||||||
// a string can't endwith a longer one
|
|
||||||
// X startswith Y -> len(X) >= len(Y)
|
|
||||||
expr_ref minus_one(m_autil.mk_numeral(rational::minus_one(), true), m);
|
|
||||||
expr_ref zero(m_autil.mk_numeral(rational::zero(), true), m);
|
|
||||||
expr_ref lens(m_autil.mk_add(mk_strlen(full), m_autil.mk_mul(minus_one, mk_strlen(suff))), m);
|
|
||||||
cex = m.mk_or(m.mk_not(f), m_autil.mk_ge(lens, zero));
|
|
||||||
th_rewriter m_rw(m);
|
|
||||||
m_rw(cex);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
expr_ref_vector branch(sub_m);
|
|
||||||
for (unsigned j = 0; j < suff_chars.size(); ++j) {
|
|
||||||
// full[j] == suff[j]
|
|
||||||
expr_ref cLHS(full_chars.get(full_chars.size() - j - 1), sub_m);
|
|
||||||
expr_ref cRHS(suff_chars.get(suff_chars.size() - j - 1), sub_m);
|
|
||||||
expr_ref _e(sub_ctx.mk_eq_atom(cLHS, cRHS), sub_m);
|
|
||||||
branch.push_back(_e);
|
|
||||||
}
|
|
||||||
|
|
||||||
expr_ref final_diseq(mk_and(branch), sub_m);
|
|
||||||
fixed_length_assumptions.push_back(final_diseq);
|
|
||||||
fixed_length_lesson.insert(final_diseq, std::make_tuple(rational(-2), f, f));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool theory_str::fixed_length_reduce_negative_suffix(smt::kernel & subsolver, expr_ref f, expr_ref & cex) {
|
|
||||||
ast_manager & m = get_manager();
|
|
||||||
context & ctx = get_context();
|
|
||||||
|
|
||||||
ast_manager & sub_m = subsolver.m();
|
|
||||||
context & sub_ctx = subsolver.get_context();
|
|
||||||
|
|
||||||
expr * full;
|
|
||||||
expr * suff;
|
|
||||||
u.str.is_suffix(f, suff, full);
|
|
||||||
|
|
||||||
expr_ref haystack(full, m);
|
|
||||||
expr_ref needle(suff, m);
|
|
||||||
|
|
||||||
ptr_vector<expr> full_chars(fixed_length_reduce_string_term(subsolver, haystack));
|
|
||||||
ptr_vector<expr> suff_chars(fixed_length_reduce_string_term(subsolver, needle));
|
|
||||||
|
|
||||||
if (suff_chars.size() == 0) {
|
|
||||||
// all strings endwith the empty one
|
|
||||||
cex = m.mk_or(m.mk_not(f), m.mk_not(ctx.mk_eq_atom(mk_strlen(suff), mk_int(0))));
|
|
||||||
th_rewriter m_rw(m);
|
|
||||||
m_rw(cex);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (full_chars.size() == 0 && suff_chars.size() > 0) {
|
|
||||||
// the empty string doesn't "endwith" any non-empty string
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (full_chars.size() < suff_chars.size()) {
|
|
||||||
// a string can't endwith a longer one
|
|
||||||
// X startswith Y -> len(X) >= len(Y)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
expr_ref_vector branch(sub_m);
|
|
||||||
for (unsigned j = 0; j < suff_chars.size(); ++j) {
|
|
||||||
// full[j] == suff[j]
|
|
||||||
expr_ref cLHS(full_chars.get(full_chars.size() - j - 1), sub_m);
|
|
||||||
expr_ref cRHS(suff_chars.get(suff_chars.size() - j - 1), sub_m);
|
|
||||||
expr_ref _e(sub_ctx.mk_eq_atom(cLHS, cRHS), sub_m);
|
|
||||||
branch.push_back(_e);
|
|
||||||
}
|
|
||||||
|
|
||||||
expr_ref final_diseq(mk_not(sub_m, mk_and(branch)), sub_m);
|
|
||||||
fixed_length_assumptions.push_back(final_diseq);
|
|
||||||
fixed_length_lesson.insert(final_diseq, std::make_tuple(rational(-3), f, f));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool theory_str::fixed_length_reduce_prefix(smt::kernel & subsolver, expr_ref f, expr_ref & cex) {
|
|
||||||
ast_manager & m = get_manager();
|
|
||||||
context & ctx = get_context();
|
|
||||||
|
|
||||||
ast_manager & sub_m = subsolver.m();
|
|
||||||
context & sub_ctx = subsolver.get_context();
|
|
||||||
|
|
||||||
expr * full;
|
|
||||||
expr * pref;
|
|
||||||
u.str.is_prefix(f, pref, full);
|
|
||||||
|
|
||||||
expr_ref haystack(full, m);
|
|
||||||
expr_ref needle(pref, m);
|
|
||||||
|
|
||||||
ptr_vector<expr> full_chars(fixed_length_reduce_string_term(subsolver, haystack));
|
|
||||||
ptr_vector<expr> pref_chars(fixed_length_reduce_string_term(subsolver, needle));
|
|
||||||
|
|
||||||
|
|
||||||
if (pref_chars.size() == 0) {
|
|
||||||
// all strings startwith the empty one
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (full_chars.size() == 0 && pref_chars.size() > 0) {
|
|
||||||
// the empty string doesn't "stratwith" any non-empty string
|
|
||||||
cex = m.mk_or(m.mk_not(f), ctx.mk_eq_atom(mk_strlen(pref), mk_int(0)),
|
|
||||||
m_autil.mk_ge(mk_strlen(full), mk_int(0)));
|
|
||||||
th_rewriter m_rw(m);
|
|
||||||
m_rw(cex);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (full_chars.size() < pref_chars.size()) {
|
|
||||||
// a string can't startwith a longer one
|
|
||||||
// X startswith Y -> len(X) >= len(Y)
|
|
||||||
expr_ref minus_one(m_autil.mk_numeral(rational::minus_one(), true), m);
|
|
||||||
expr_ref zero(m_autil.mk_numeral(rational::zero(), true), m);
|
|
||||||
expr_ref lens(m_autil.mk_add(mk_strlen(full), m_autil.mk_mul(minus_one, mk_strlen(pref))), m);
|
|
||||||
cex = m.mk_or(m.mk_not(f), m_autil.mk_ge(lens, zero));
|
|
||||||
th_rewriter m_rw(m);
|
|
||||||
m_rw(cex);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
expr_ref_vector branch(m);
|
|
||||||
for (unsigned j = 0; j < pref_chars.size(); ++j) {
|
|
||||||
// full[j] == pref[j]
|
|
||||||
expr_ref cLHS(full_chars.get(j), sub_m);
|
|
||||||
expr_ref cRHS(pref_chars.get(j), sub_m);
|
|
||||||
expr_ref _e(sub_ctx.mk_eq_atom(cLHS, cRHS), sub_m);
|
|
||||||
branch.push_back(_e);
|
|
||||||
}
|
|
||||||
|
|
||||||
expr_ref final_diseq(mk_and(branch), sub_m);
|
|
||||||
fixed_length_assumptions.push_back(final_diseq);
|
|
||||||
fixed_length_lesson.insert(final_diseq, std::make_tuple(rational(-2), f, f));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool theory_str::fixed_length_reduce_negative_prefix(smt::kernel & subsolver, expr_ref f, expr_ref & cex) {
|
|
||||||
ast_manager & m = get_manager();
|
|
||||||
context & ctx = get_context();
|
|
||||||
|
|
||||||
ast_manager & sub_m = subsolver.m();
|
|
||||||
context & sub_ctx = subsolver.get_context();
|
|
||||||
|
|
||||||
expr * full;
|
|
||||||
expr * pref;
|
|
||||||
u.str.is_prefix(f, pref, full);
|
|
||||||
|
|
||||||
expr_ref haystack(full, m);
|
|
||||||
expr_ref needle(pref, m);
|
|
||||||
|
|
||||||
ptr_vector<expr> full_chars(fixed_length_reduce_string_term(subsolver, haystack));
|
|
||||||
ptr_vector<expr> pref_chars(fixed_length_reduce_string_term(subsolver, needle));
|
|
||||||
|
|
||||||
if (pref_chars.size() == 0) {
|
|
||||||
// all strings startwith the empty one
|
|
||||||
cex = m.mk_or(m.mk_not(f), m.mk_not(ctx.mk_eq_atom(mk_strlen(pref), mk_int(0))));
|
|
||||||
th_rewriter m_rw(m);
|
|
||||||
m_rw(cex);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (full_chars.size() == 0 && pref_chars.size() > 0) {
|
|
||||||
// the empty string doesn't "stratwith" any non-empty string
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (full_chars.size() < pref_chars.size()) {
|
|
||||||
// a string can't startwith a longer one
|
|
||||||
// X startswith Y -> len(X) >= len(Y)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
expr_ref_vector branch(m);
|
|
||||||
for (unsigned j = 0; j < pref_chars.size(); ++j) {
|
|
||||||
// full[j] == pref[j]
|
|
||||||
expr_ref cLHS(full_chars.get(j), sub_m);
|
|
||||||
expr_ref cRHS(pref_chars.get(j), sub_m);
|
|
||||||
expr_ref _e(sub_ctx.mk_eq_atom(cLHS, cRHS), sub_m);
|
|
||||||
branch.push_back(_e);
|
|
||||||
}
|
|
||||||
|
|
||||||
expr_ref final_diseq(mk_not(sub_m, mk_and(branch)), sub_m);
|
|
||||||
fixed_length_assumptions.push_back(final_diseq);
|
|
||||||
fixed_length_lesson.insert(final_diseq, std::make_tuple(rational(-3), f, f));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool theory_str::fixed_length_reduce_contains(smt::kernel & subsolver, expr_ref f, expr_ref & cex) {
|
|
||||||
ast_manager & m = get_manager();
|
|
||||||
context & ctx = get_context();
|
|
||||||
|
|
||||||
ast_manager & sub_m = subsolver.m();
|
|
||||||
context & sub_ctx = subsolver.get_context();
|
|
||||||
|
|
||||||
expr * full;
|
|
||||||
expr * small;
|
|
||||||
u.str.is_contains(f, full, small);
|
|
||||||
|
|
||||||
expr_ref haystack(full, m);
|
|
||||||
expr_ref needle(small, m);
|
|
||||||
|
|
||||||
ptr_vector<expr> haystack_chars(fixed_length_reduce_string_term(subsolver, haystack));
|
|
||||||
ptr_vector<expr> needle_chars(fixed_length_reduce_string_term(subsolver, needle));
|
|
||||||
|
|
||||||
if (needle_chars.size() == 0) {
|
|
||||||
// all strings "contain" the empty one
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (haystack_chars.size() == 0 && needle_chars.size() > 0) {
|
|
||||||
// the empty string doesn't "contain" any non-empty string
|
|
||||||
cex = m.mk_or(m.mk_not(f), ctx.mk_eq_atom(mk_strlen(needle), mk_int(0)),
|
|
||||||
m_autil.mk_ge(mk_strlen(haystack), mk_int(0)));
|
|
||||||
th_rewriter m_rw(m);
|
|
||||||
m_rw(cex);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (needle_chars.size() > haystack_chars.size()) {
|
|
||||||
// a string can't contain a longer one
|
|
||||||
// X contains Y -> len(X) >= len(Y)
|
|
||||||
expr_ref minus_one(m_autil.mk_numeral(rational::minus_one(), true), m);
|
|
||||||
expr_ref zero(m_autil.mk_numeral(rational::zero(), true), m);
|
|
||||||
expr_ref lens(m_autil.mk_add(mk_strlen(haystack), m_autil.mk_mul(minus_one, mk_strlen(needle))), m);
|
|
||||||
cex = m.mk_or(m.mk_not(f), m_autil.mk_ge(lens, zero));
|
|
||||||
th_rewriter m_rw(m);
|
|
||||||
m_rw(cex);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// find all positions at which `needle` could occur in `haystack`
|
|
||||||
expr_ref_vector branches(m);
|
|
||||||
for (unsigned i = 0; i <= (haystack_chars.size() - needle_chars.size()); ++i) {
|
|
||||||
// i defines the offset into haystack_chars
|
|
||||||
expr_ref_vector branch(m);
|
|
||||||
for (unsigned j = 0; j < needle_chars.size(); ++j) {
|
|
||||||
// needle[j] == haystack[i+j]
|
|
||||||
ENSURE(i+j < haystack_chars.size());
|
|
||||||
expr_ref cLHS(needle_chars.get(j), sub_m);
|
|
||||||
expr_ref cRHS(haystack_chars.get(j), sub_m);
|
|
||||||
expr_ref _e(sub_ctx.mk_eq_atom(cLHS, cRHS), sub_m);
|
|
||||||
branch.push_back(_e);
|
|
||||||
}
|
|
||||||
branches.push_back(mk_and(branch));
|
|
||||||
}
|
|
||||||
|
|
||||||
expr_ref final_diseq(mk_or(branches), sub_m);
|
|
||||||
fixed_length_assumptions.push_back(final_diseq);
|
|
||||||
fixed_length_lesson.insert(final_diseq, std::make_tuple(rational(-2), f, f));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool theory_str::fixed_length_reduce_negative_contains(smt::kernel & subsolver, expr_ref f, expr_ref & cex) {
|
|
||||||
ast_manager & m = get_manager();
|
|
||||||
context & ctx = get_context();
|
|
||||||
|
|
||||||
ast_manager & sub_m = subsolver.m();
|
|
||||||
context & sub_ctx = subsolver.get_context();
|
|
||||||
|
|
||||||
expr * full;
|
|
||||||
expr * small;
|
|
||||||
u.str.is_contains(f, full, small);
|
|
||||||
|
|
||||||
expr_ref haystack(full, m);
|
|
||||||
expr_ref needle(small, m);
|
|
||||||
|
|
||||||
ptr_vector<expr> haystack_chars(fixed_length_reduce_string_term(subsolver, haystack));
|
|
||||||
ptr_vector<expr> needle_chars(fixed_length_reduce_string_term(subsolver, needle));
|
|
||||||
|
|
||||||
if (needle_chars.size() == 0) {
|
|
||||||
// all strings "contain" the empty one
|
|
||||||
cex = m.mk_or(m.mk_not(f), m.mk_not(ctx.mk_eq_atom(mk_strlen(needle), mk_int(0))));
|
|
||||||
th_rewriter m_rw(m);
|
|
||||||
m_rw(cex);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (haystack_chars.size() == 0 && needle_chars.size() > 0) {
|
|
||||||
// the empty string doesn't "contain" any non-empty string
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (needle_chars.size() > haystack_chars.size()) {
|
|
||||||
// a string can't contain a longer one
|
|
||||||
// X contains Y -> len(X) >= len(Y)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// find all positions at which `needle` could occur in `haystack`
|
|
||||||
expr_ref_vector branches(m);
|
|
||||||
for (unsigned i = 0; i <= (haystack_chars.size() - needle_chars.size()); ++i) {
|
|
||||||
// i defines the offset into haystack_chars
|
|
||||||
expr_ref_vector branch(m);
|
|
||||||
for (unsigned j = 0; j < needle_chars.size(); ++j) {
|
|
||||||
// needle[j] == haystack[i+j]
|
|
||||||
ENSURE(i+j < haystack_chars.size());
|
|
||||||
expr_ref cLHS(needle_chars.get(j), sub_m);
|
|
||||||
expr_ref cRHS(haystack_chars.get(j), sub_m);
|
|
||||||
expr_ref _e(sub_ctx.mk_eq_atom(cLHS, cRHS), sub_m);
|
|
||||||
branch.push_back(_e);
|
|
||||||
}
|
|
||||||
branches.push_back(mk_and(branch));
|
|
||||||
}
|
|
||||||
|
|
||||||
expr_ref final_diseq(mk_not(sub_m, mk_and(branches)), sub_m);
|
|
||||||
fixed_length_assumptions.push_back(final_diseq);
|
|
||||||
fixed_length_lesson.insert(final_diseq, std::make_tuple(rational(-3), f, f));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
lbool theory_str::fixed_length_model_construction(expr_ref_vector formulas, expr_ref_vector &precondition,
|
|
||||||
obj_map<expr, zstring> &model, expr_ref_vector &cex) {
|
|
||||||
|
|
||||||
ast_manager & m = get_manager();
|
|
||||||
|
|
||||||
if (bitvector_character_constants.empty()) {
|
|
||||||
bv_util bv(m);
|
|
||||||
sort * bv8_sort = bv.mk_sort(8);
|
|
||||||
for (unsigned i = 0; i < 256; ++i) {
|
|
||||||
rational ch(i);
|
|
||||||
expr_ref chTerm(bv.mk_numeral(ch, bv8_sort), m);
|
|
||||||
bitvector_character_constants.push_back(chTerm);
|
|
||||||
fixed_length_subterm_trail.push_back(chTerm);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_trace_enabled("str")) {
|
|
||||||
TRACE_CODE(
|
|
||||||
ast_manager & m = get_manager();
|
|
||||||
context & ctx = get_context();
|
|
||||||
tout << "dumping all formulas:" << std::endl;
|
|
||||||
for (expr_ref_vector::iterator i = formulas.begin(); i != formulas.end(); ++i) {
|
|
||||||
expr * ex = *i;
|
|
||||||
tout << mk_pp(ex, m) << (ctx.is_relevant(ex) ? "" : " (NOT REL)") << std::endl;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
fixed_length_subterm_trail.reset();
|
|
||||||
fixed_length_used_len_terms.reset();
|
|
||||||
fixed_length_assumptions.reset();
|
|
||||||
var_to_char_subterm_map.reset();
|
|
||||||
uninterpreted_to_char_subterm_map.reset();
|
|
||||||
fixed_length_lesson.reset();
|
|
||||||
|
|
||||||
// Boolean formulas on which to apply abstraction refinement.
|
|
||||||
expr_ref_vector abstracted_boolean_formulas(m);
|
|
||||||
|
|
||||||
smt_params subsolver_params;
|
|
||||||
smt::kernel subsolver(m, subsolver_params);
|
|
||||||
subsolver.set_logic(symbol("QF_BV"));
|
|
||||||
|
|
||||||
sort * str_sort = u.str.mk_string_sort();
|
|
||||||
sort * bool_sort = m.mk_bool_sort();
|
|
||||||
|
|
||||||
for (expr * f : formulas) {
|
|
||||||
// reduce string formulas only. ignore others
|
|
||||||
sort * fSort = m.get_sort(f);
|
|
||||||
if (fSort == bool_sort && !is_quantifier(f)) {
|
|
||||||
// extracted terms
|
|
||||||
expr * subterm;
|
|
||||||
expr * lhs;
|
|
||||||
expr * rhs;
|
|
||||||
if (m.is_eq(f, lhs, rhs)) {
|
|
||||||
sort * lhs_sort = m.get_sort(lhs);
|
|
||||||
if (lhs_sort == str_sort) {
|
|
||||||
TRACE("str_fl", tout << "reduce string equality: " << mk_pp(lhs, m) << " == " << mk_pp(rhs, m) << std::endl;);
|
|
||||||
expr_ref cex(m);
|
|
||||||
expr_ref left(lhs, m);
|
|
||||||
expr_ref right(rhs, m);
|
|
||||||
if (!fixed_length_reduce_eq(subsolver, left, right, cex)) {
|
|
||||||
// missing a side condition. assert it and return unknown
|
|
||||||
assert_axiom(cex);
|
|
||||||
add_persisted_axiom(cex);
|
|
||||||
return l_undef;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
TRACE("str_fl", tout << "skip reducing formula " << mk_pp(f, m) << ", not an equality over strings" << std::endl;);
|
|
||||||
}
|
|
||||||
} else if (u.str.is_in_re(f)) {
|
|
||||||
TRACE("str_fl", tout << "WARNING: regex constraints not yet implemented in fixed-length model construction!" << std::endl;);
|
|
||||||
return l_undef;
|
|
||||||
} else if (u.str.is_contains(f)) {
|
|
||||||
// TODO in some cases (e.g. len(haystack) is only slightly greater than len(needle))
|
|
||||||
// we might be okay to assert the full disjunction because there are very few disjuncts
|
|
||||||
if (m_params.m_FixedLengthRefinement) {
|
|
||||||
TRACE("str_fl", tout << "abstracting out positive contains: " << mk_pp(f, m) << std::endl;);
|
|
||||||
abstracted_boolean_formulas.push_back(f);
|
|
||||||
} else {
|
|
||||||
TRACE("str_fl", tout << "reduce positive contains: " << mk_pp(f, m) << std::endl;);
|
|
||||||
expr_ref cex(m);
|
|
||||||
expr_ref cont(f, m);
|
|
||||||
if (!fixed_length_reduce_contains(subsolver, cont, cex)) {
|
|
||||||
assert_axiom(cex);
|
|
||||||
add_persisted_axiom(cex);
|
|
||||||
return l_undef;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (u.str.is_prefix(f)) {
|
|
||||||
TRACE("str_fl", tout << "reduce positive prefix: " << mk_pp(f, m) << std::endl;);
|
|
||||||
expr_ref cex(m);
|
|
||||||
expr_ref pref(f, m);
|
|
||||||
if (!fixed_length_reduce_prefix(subsolver, pref, cex)) {
|
|
||||||
assert_axiom(cex);
|
|
||||||
add_persisted_axiom(cex);
|
|
||||||
return l_undef;
|
|
||||||
}
|
|
||||||
} else if (u.str.is_suffix(f)) {
|
|
||||||
TRACE("str_fl", tout << "reduce positive suffix: " << mk_pp(f, m) << std::endl;);
|
|
||||||
expr_ref cex(m);
|
|
||||||
expr_ref suf(f, m);
|
|
||||||
if (!fixed_length_reduce_suffix(subsolver, suf, cex)) {
|
|
||||||
assert_axiom(cex);
|
|
||||||
add_persisted_axiom(cex);
|
|
||||||
return l_undef;
|
|
||||||
}
|
|
||||||
}else if (m.is_not(f, subterm)) {
|
|
||||||
// if subterm is a string formula such as an equality, reduce it as a disequality
|
|
||||||
if (m.is_eq(subterm, lhs, rhs)) {
|
|
||||||
sort * lhs_sort = m.get_sort(lhs);
|
|
||||||
if (lhs_sort == str_sort) {
|
|
||||||
TRACE("str_fl", tout << "reduce string disequality: " << mk_pp(lhs, m) << " != " << mk_pp(rhs, m) << std::endl;);
|
|
||||||
expr_ref cex(m);
|
|
||||||
expr_ref left(lhs, m);
|
|
||||||
expr_ref right(rhs, m);
|
|
||||||
if (!fixed_length_reduce_diseq(subsolver, left, right, cex)) {
|
|
||||||
// missing a side condition. assert it and return unknown
|
|
||||||
assert_axiom(cex);
|
|
||||||
add_persisted_axiom(cex);
|
|
||||||
return l_undef;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (u.str.is_in_re(subterm)) {
|
|
||||||
TRACE("str_fl", tout << "WARNING: negative regex constraints not yet implemented in fixed-length model construction!" << std::endl;);
|
|
||||||
return l_undef;
|
|
||||||
} else if (u.str.is_contains(subterm)) {
|
|
||||||
TRACE("str_fl", tout << "reduce negative contains: " << mk_pp(subterm, m) << std::endl;);
|
|
||||||
expr_ref cex(m);
|
|
||||||
expr_ref cont(subterm, m);
|
|
||||||
if (!fixed_length_reduce_negative_contains(subsolver, cont, cex)) {
|
|
||||||
assert_axiom(cex);
|
|
||||||
add_persisted_axiom(cex);
|
|
||||||
return l_undef;
|
|
||||||
}
|
|
||||||
} else if (u.str.is_prefix(subterm)) {
|
|
||||||
TRACE("str_fl", tout << "reduce negative prefix: " << mk_pp(subterm, m) << std::endl;);
|
|
||||||
expr_ref cex(m);
|
|
||||||
expr_ref pref(subterm, m);
|
|
||||||
if (!fixed_length_reduce_negative_prefix(subsolver, pref, cex)) {
|
|
||||||
assert_axiom(cex);
|
|
||||||
add_persisted_axiom(cex);
|
|
||||||
return l_undef;
|
|
||||||
}
|
|
||||||
} else if (u.str.is_suffix(subterm)) {
|
|
||||||
TRACE("str_fl", tout << "reduce negative suffix: " << mk_pp(subterm, m) << std::endl;);
|
|
||||||
expr_ref cex(m);
|
|
||||||
expr_ref suf(subterm, m);
|
|
||||||
if (!fixed_length_reduce_negative_suffix(subsolver, suf, cex)) {
|
|
||||||
assert_axiom(cex);
|
|
||||||
add_persisted_axiom(cex);
|
|
||||||
return l_undef;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
TRACE("str_fl", tout << "skip reducing formula " << mk_pp(f, m) << ", not a boolean formula we handle" << std::endl;);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
TRACE("str_fl", tout << "skip reducing formula " << mk_pp(f, m) << ", not a boolean formula we handle" << std::endl;);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
TRACE("str_fl", tout << "skip reducing formula " << mk_pp(f, m) << ", not relevant to strings" << std::endl;);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto e : fixed_length_used_len_terms) {
|
|
||||||
expr * var = &e.get_key();
|
|
||||||
precondition.push_back(m.mk_eq(u.str.mk_length(var), mk_int(e.get_value())));
|
|
||||||
}
|
|
||||||
|
|
||||||
TRACE("str_fl", tout << "calling subsolver" << std::endl;);
|
|
||||||
|
|
||||||
lbool subproblem_status = subsolver.check(fixed_length_assumptions);
|
|
||||||
|
|
||||||
if (subproblem_status == l_true) {
|
|
||||||
bv_util bv(m);
|
|
||||||
TRACE("str_fl", tout << "subsolver found SAT; reconstructing model" << std::endl;);
|
|
||||||
model_ref subModel;
|
|
||||||
subsolver.get_model(subModel);
|
|
||||||
|
|
||||||
expr_substitution subst(m);
|
|
||||||
|
|
||||||
// model_smt2_pp(std::cout, m, *subModel, 2);
|
|
||||||
for (auto entry : var_to_char_subterm_map) {
|
|
||||||
svector<unsigned> assignment;
|
|
||||||
expr * var = entry.m_key;
|
|
||||||
ptr_vector<expr> char_subterms(entry.m_value);
|
|
||||||
for (expr * chExpr : char_subterms) {
|
|
||||||
expr_ref chAssignment(subModel->get_const_interp(to_app(chExpr)->get_decl()), m);
|
|
||||||
rational n;
|
|
||||||
if (chAssignment != nullptr && bv.is_numeral(chAssignment, n)) {
|
|
||||||
assignment.push_back(n.get_unsigned());
|
|
||||||
} else {
|
|
||||||
assignment.push_back((unsigned)'?');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
zstring strValue(assignment.size(), assignment.c_ptr());
|
|
||||||
model.insert(var, strValue);
|
|
||||||
subst.insert(var, mk_string(strValue));
|
|
||||||
}
|
|
||||||
for (auto entry : uninterpreted_to_char_subterm_map) {
|
|
||||||
svector<unsigned> assignment;
|
|
||||||
expr * var = entry.m_key;
|
|
||||||
ptr_vector<expr> char_subterms(entry.m_value);
|
|
||||||
for (expr * chExpr : char_subterms) {
|
|
||||||
expr_ref chAssignment(subModel->get_const_interp(to_app(chExpr)->get_decl()), m);
|
|
||||||
rational n;
|
|
||||||
if (chAssignment != nullptr && bv.is_numeral(chAssignment, n)) {
|
|
||||||
assignment.push_back(n.get_unsigned());
|
|
||||||
} else {
|
|
||||||
assignment.push_back((unsigned)'?');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
zstring strValue(assignment.size(), assignment.c_ptr());
|
|
||||||
model.insert(var, strValue);
|
|
||||||
subst.insert(var, mk_string(strValue));
|
|
||||||
}
|
|
||||||
// TODO insert length values into substitution table as well?
|
|
||||||
if (m_params.m_FixedLengthRefinement) {
|
|
||||||
scoped_ptr<expr_replacer> replacer = mk_default_expr_replacer(m);
|
|
||||||
replacer->set_substitution(&subst);
|
|
||||||
th_rewriter rw(m);
|
|
||||||
if (!abstracted_boolean_formulas.empty()) {
|
|
||||||
for (auto f : abstracted_boolean_formulas) {
|
|
||||||
TRACE("str_fl", tout << "refinement of boolean formula: " << mk_pp(f, m) << std::endl;);
|
|
||||||
expr_ref f_new(m);
|
|
||||||
(*replacer)(f, f_new);
|
|
||||||
rw(f_new);
|
|
||||||
TRACE("str_fl", tout << "after substitution and simplification, evaluates to: " << mk_pp(f_new, m) << std::endl;);
|
|
||||||
// now there are three cases, depending on what f_new evaluates to:
|
|
||||||
// true -> OK, do nothing
|
|
||||||
// false -> refine abstraction by generating conflict clause
|
|
||||||
// anything else -> error, probably our substitution was incomplete
|
|
||||||
if (m.is_true(f_new)) {
|
|
||||||
// do nothing
|
|
||||||
} else if (m.is_false(f_new)) {
|
|
||||||
context & ctx = get_context();
|
|
||||||
if (u.str.is_contains(f)) {
|
|
||||||
expr * haystack;
|
|
||||||
expr * needle;
|
|
||||||
u.str.is_contains(f, haystack, needle);
|
|
||||||
expr_ref haystack_assignment(m);
|
|
||||||
expr_ref needle_assignment(m);
|
|
||||||
(*replacer)(haystack, haystack_assignment);
|
|
||||||
(*replacer)(needle, needle_assignment);
|
|
||||||
cex.push_back(f);
|
|
||||||
cex.push_back(ctx.mk_eq_atom(haystack, haystack_assignment));
|
|
||||||
cex.push_back(ctx.mk_eq_atom(needle, needle_assignment));
|
|
||||||
return l_false;
|
|
||||||
} else {
|
|
||||||
TRACE("str_fl", tout << "error: unhandled refinement term " << mk_pp(f, m) << std::endl;);
|
|
||||||
NOT_IMPLEMENTED_YET();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
NOT_IMPLEMENTED_YET();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return l_true;
|
|
||||||
} else if (subproblem_status == l_false) {
|
|
||||||
// TODO replace this with something simpler for now
|
|
||||||
NOT_IMPLEMENTED_YET();
|
|
||||||
TRACE("str_fl", tout << "subsolver found UNSAT; reconstructing unsat core" << std::endl;);
|
|
||||||
TRACE("str_fl", tout << "unsat core has size " << subsolver.get_unsat_core_size() << std::endl;);
|
|
||||||
bool negate_pre = false;
|
|
||||||
for (unsigned i = 0; i < subsolver.get_unsat_core_size(); ++i) {
|
|
||||||
TRACE("str", tout << "entry " << i << " = " << mk_pp(subsolver.get_unsat_core_expr(i), m) << std::endl;);
|
|
||||||
rational index;
|
|
||||||
expr* lhs;
|
|
||||||
expr* rhs;
|
|
||||||
std::tie(index, lhs, rhs) = fixed_length_lesson.find(subsolver.get_unsat_core_expr(i));
|
|
||||||
TRACE("str_fl", tout << "lesson: " << mk_pp(lhs, m) << " == " << mk_pp(rhs, m) << " at index " << index << std::endl;);
|
|
||||||
cex.push_back(refine(lhs, rhs, index));
|
|
||||||
if (index < rational(0)) {
|
|
||||||
negate_pre = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (negate_pre){
|
|
||||||
for (auto ex : precondition) {
|
|
||||||
cex.push_back(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return l_false;
|
|
||||||
} else { // l_undef
|
|
||||||
TRACE("str_fl", tout << "WARNING: subsolver found UNKNOWN" << std::endl;);
|
|
||||||
return l_undef;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void theory_str::get_concats_in_eqc(expr * n, std::set<expr*> & concats) {
|
void theory_str::get_concats_in_eqc(expr * n, std::set<expr*> & concats) {
|
||||||
|
|
||||||
expr * eqcNode = n;
|
expr * eqcNode = n;
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include "smt/theory_arith.h"
|
#include "smt/theory_arith.h"
|
||||||
#include "ast/ast_util.h"
|
#include "ast/ast_util.h"
|
||||||
#include "ast/rewriter/seq_rewriter.h"
|
#include "ast/rewriter/seq_rewriter.h"
|
||||||
|
#include "ast/rewriter/expr_replacer.h"
|
||||||
#include "smt_kernel.h"
|
#include "smt_kernel.h"
|
||||||
|
|
||||||
namespace smt {
|
namespace smt {
|
||||||
|
@ -43,6 +44,689 @@ namespace smt {
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use the current model in the arithmetic solver to get the length of a term.
|
||||||
|
* Returns true if this could be done, placing result in 'termLen', or false otherwise.
|
||||||
|
* Works like get_len_value() except uses arithmetic solver model instead of EQCs.
|
||||||
|
*/
|
||||||
|
bool theory_str::fixed_length_get_len_value(expr * e, rational & val) {
|
||||||
|
ast_manager & m = get_manager();
|
||||||
|
|
||||||
|
rational val1;
|
||||||
|
expr_ref len(m), len_val(m);
|
||||||
|
expr* e1, *e2;
|
||||||
|
expr_ref_vector todo(m);
|
||||||
|
todo.push_back(e);
|
||||||
|
val.reset();
|
||||||
|
while (!todo.empty()) {
|
||||||
|
expr* c = todo.back();
|
||||||
|
todo.pop_back();
|
||||||
|
if (u.str.is_concat(to_app(c))) {
|
||||||
|
e1 = to_app(c)->get_arg(0);
|
||||||
|
e2 = to_app(c)->get_arg(1);
|
||||||
|
todo.push_back(e1);
|
||||||
|
todo.push_back(e2);
|
||||||
|
}
|
||||||
|
else if (u.str.is_string(to_app(c))) {
|
||||||
|
zstring tmp;
|
||||||
|
u.str.is_string(to_app(c), tmp);
|
||||||
|
unsigned int sl = tmp.length();
|
||||||
|
val += rational(sl);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
len = mk_strlen(c);
|
||||||
|
arith_value v(get_manager());
|
||||||
|
v.init(&get_context());
|
||||||
|
if (v.get_value(len, val1)) {
|
||||||
|
val += val1;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return val.is_int();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool theory_str::fixed_length_reduce_suffix(smt::kernel & subsolver, expr_ref f, expr_ref & cex) {
|
||||||
|
ast_manager & m = get_manager();
|
||||||
|
context & ctx = get_context();
|
||||||
|
|
||||||
|
ast_manager & sub_m = subsolver.m();
|
||||||
|
context & sub_ctx = subsolver.get_context();
|
||||||
|
|
||||||
|
expr * full;
|
||||||
|
expr * suff;
|
||||||
|
u.str.is_suffix(f, suff, full);
|
||||||
|
|
||||||
|
expr_ref haystack(full, m);
|
||||||
|
expr_ref needle(suff, m);
|
||||||
|
|
||||||
|
ptr_vector<expr> full_chars(fixed_length_reduce_string_term(subsolver, haystack));
|
||||||
|
ptr_vector<expr> suff_chars(fixed_length_reduce_string_term(subsolver, needle));
|
||||||
|
|
||||||
|
if (suff_chars.size() == 0) {
|
||||||
|
// all strings endwith the empty one
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (full_chars.size() == 0 && suff_chars.size() > 0) {
|
||||||
|
// the empty string doesn't "endwith" any non-empty string
|
||||||
|
cex = m.mk_or(m.mk_not(f), ctx.mk_eq_atom(mk_strlen(suff), mk_int(0)),
|
||||||
|
m_autil.mk_ge(mk_strlen(full), mk_int(0)));
|
||||||
|
th_rewriter m_rw(m);
|
||||||
|
m_rw(cex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (full_chars.size() < suff_chars.size()) {
|
||||||
|
// a string can't endwith a longer one
|
||||||
|
// X startswith Y -> len(X) >= len(Y)
|
||||||
|
expr_ref minus_one(m_autil.mk_numeral(rational::minus_one(), true), m);
|
||||||
|
expr_ref zero(m_autil.mk_numeral(rational::zero(), true), m);
|
||||||
|
expr_ref lens(m_autil.mk_add(mk_strlen(full), m_autil.mk_mul(minus_one, mk_strlen(suff))), m);
|
||||||
|
cex = m.mk_or(m.mk_not(f), m_autil.mk_ge(lens, zero));
|
||||||
|
th_rewriter m_rw(m);
|
||||||
|
m_rw(cex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
expr_ref_vector branch(sub_m);
|
||||||
|
for (unsigned j = 0; j < suff_chars.size(); ++j) {
|
||||||
|
// full[j] == suff[j]
|
||||||
|
expr_ref cLHS(full_chars.get(full_chars.size() - j - 1), sub_m);
|
||||||
|
expr_ref cRHS(suff_chars.get(suff_chars.size() - j - 1), sub_m);
|
||||||
|
expr_ref _e(sub_ctx.mk_eq_atom(cLHS, cRHS), sub_m);
|
||||||
|
branch.push_back(_e);
|
||||||
|
}
|
||||||
|
|
||||||
|
expr_ref final_diseq(mk_and(branch), sub_m);
|
||||||
|
fixed_length_assumptions.push_back(final_diseq);
|
||||||
|
fixed_length_lesson.insert(final_diseq, std::make_tuple(rational(-2), f, f));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool theory_str::fixed_length_reduce_negative_suffix(smt::kernel & subsolver, expr_ref f, expr_ref & cex) {
|
||||||
|
ast_manager & m = get_manager();
|
||||||
|
context & ctx = get_context();
|
||||||
|
|
||||||
|
ast_manager & sub_m = subsolver.m();
|
||||||
|
context & sub_ctx = subsolver.get_context();
|
||||||
|
|
||||||
|
expr * full;
|
||||||
|
expr * suff;
|
||||||
|
u.str.is_suffix(f, suff, full);
|
||||||
|
|
||||||
|
expr_ref haystack(full, m);
|
||||||
|
expr_ref needle(suff, m);
|
||||||
|
|
||||||
|
ptr_vector<expr> full_chars(fixed_length_reduce_string_term(subsolver, haystack));
|
||||||
|
ptr_vector<expr> suff_chars(fixed_length_reduce_string_term(subsolver, needle));
|
||||||
|
|
||||||
|
if (suff_chars.size() == 0) {
|
||||||
|
// all strings endwith the empty one
|
||||||
|
cex = m.mk_or(m.mk_not(f), m.mk_not(ctx.mk_eq_atom(mk_strlen(suff), mk_int(0))));
|
||||||
|
th_rewriter m_rw(m);
|
||||||
|
m_rw(cex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (full_chars.size() == 0 && suff_chars.size() > 0) {
|
||||||
|
// the empty string doesn't "endwith" any non-empty string
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (full_chars.size() < suff_chars.size()) {
|
||||||
|
// a string can't endwith a longer one
|
||||||
|
// X startswith Y -> len(X) >= len(Y)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
expr_ref_vector branch(sub_m);
|
||||||
|
for (unsigned j = 0; j < suff_chars.size(); ++j) {
|
||||||
|
// full[j] == suff[j]
|
||||||
|
expr_ref cLHS(full_chars.get(full_chars.size() - j - 1), sub_m);
|
||||||
|
expr_ref cRHS(suff_chars.get(suff_chars.size() - j - 1), sub_m);
|
||||||
|
expr_ref _e(sub_ctx.mk_eq_atom(cLHS, cRHS), sub_m);
|
||||||
|
branch.push_back(_e);
|
||||||
|
}
|
||||||
|
|
||||||
|
expr_ref final_diseq(mk_not(sub_m, mk_and(branch)), sub_m);
|
||||||
|
fixed_length_assumptions.push_back(final_diseq);
|
||||||
|
fixed_length_lesson.insert(final_diseq, std::make_tuple(rational(-3), f, f));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool theory_str::fixed_length_reduce_prefix(smt::kernel & subsolver, expr_ref f, expr_ref & cex) {
|
||||||
|
ast_manager & m = get_manager();
|
||||||
|
context & ctx = get_context();
|
||||||
|
|
||||||
|
ast_manager & sub_m = subsolver.m();
|
||||||
|
context & sub_ctx = subsolver.get_context();
|
||||||
|
|
||||||
|
expr * full;
|
||||||
|
expr * pref;
|
||||||
|
u.str.is_prefix(f, pref, full);
|
||||||
|
|
||||||
|
expr_ref haystack(full, m);
|
||||||
|
expr_ref needle(pref, m);
|
||||||
|
|
||||||
|
ptr_vector<expr> full_chars(fixed_length_reduce_string_term(subsolver, haystack));
|
||||||
|
ptr_vector<expr> pref_chars(fixed_length_reduce_string_term(subsolver, needle));
|
||||||
|
|
||||||
|
|
||||||
|
if (pref_chars.size() == 0) {
|
||||||
|
// all strings startwith the empty one
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (full_chars.size() == 0 && pref_chars.size() > 0) {
|
||||||
|
// the empty string doesn't "stratwith" any non-empty string
|
||||||
|
cex = m.mk_or(m.mk_not(f), ctx.mk_eq_atom(mk_strlen(pref), mk_int(0)),
|
||||||
|
m_autil.mk_ge(mk_strlen(full), mk_int(0)));
|
||||||
|
th_rewriter m_rw(m);
|
||||||
|
m_rw(cex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (full_chars.size() < pref_chars.size()) {
|
||||||
|
// a string can't startwith a longer one
|
||||||
|
// X startswith Y -> len(X) >= len(Y)
|
||||||
|
expr_ref minus_one(m_autil.mk_numeral(rational::minus_one(), true), m);
|
||||||
|
expr_ref zero(m_autil.mk_numeral(rational::zero(), true), m);
|
||||||
|
expr_ref lens(m_autil.mk_add(mk_strlen(full), m_autil.mk_mul(minus_one, mk_strlen(pref))), m);
|
||||||
|
cex = m.mk_or(m.mk_not(f), m_autil.mk_ge(lens, zero));
|
||||||
|
th_rewriter m_rw(m);
|
||||||
|
m_rw(cex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
expr_ref_vector branch(m);
|
||||||
|
for (unsigned j = 0; j < pref_chars.size(); ++j) {
|
||||||
|
// full[j] == pref[j]
|
||||||
|
expr_ref cLHS(full_chars.get(j), sub_m);
|
||||||
|
expr_ref cRHS(pref_chars.get(j), sub_m);
|
||||||
|
expr_ref _e(sub_ctx.mk_eq_atom(cLHS, cRHS), sub_m);
|
||||||
|
branch.push_back(_e);
|
||||||
|
}
|
||||||
|
|
||||||
|
expr_ref final_diseq(mk_and(branch), sub_m);
|
||||||
|
fixed_length_assumptions.push_back(final_diseq);
|
||||||
|
fixed_length_lesson.insert(final_diseq, std::make_tuple(rational(-2), f, f));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool theory_str::fixed_length_reduce_negative_prefix(smt::kernel & subsolver, expr_ref f, expr_ref & cex) {
|
||||||
|
ast_manager & m = get_manager();
|
||||||
|
context & ctx = get_context();
|
||||||
|
|
||||||
|
ast_manager & sub_m = subsolver.m();
|
||||||
|
context & sub_ctx = subsolver.get_context();
|
||||||
|
|
||||||
|
expr * full;
|
||||||
|
expr * pref;
|
||||||
|
u.str.is_prefix(f, pref, full);
|
||||||
|
|
||||||
|
expr_ref haystack(full, m);
|
||||||
|
expr_ref needle(pref, m);
|
||||||
|
|
||||||
|
ptr_vector<expr> full_chars(fixed_length_reduce_string_term(subsolver, haystack));
|
||||||
|
ptr_vector<expr> pref_chars(fixed_length_reduce_string_term(subsolver, needle));
|
||||||
|
|
||||||
|
if (pref_chars.size() == 0) {
|
||||||
|
// all strings startwith the empty one
|
||||||
|
cex = m.mk_or(m.mk_not(f), m.mk_not(ctx.mk_eq_atom(mk_strlen(pref), mk_int(0))));
|
||||||
|
th_rewriter m_rw(m);
|
||||||
|
m_rw(cex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (full_chars.size() == 0 && pref_chars.size() > 0) {
|
||||||
|
// the empty string doesn't "stratwith" any non-empty string
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (full_chars.size() < pref_chars.size()) {
|
||||||
|
// a string can't startwith a longer one
|
||||||
|
// X startswith Y -> len(X) >= len(Y)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
expr_ref_vector branch(m);
|
||||||
|
for (unsigned j = 0; j < pref_chars.size(); ++j) {
|
||||||
|
// full[j] == pref[j]
|
||||||
|
expr_ref cLHS(full_chars.get(j), sub_m);
|
||||||
|
expr_ref cRHS(pref_chars.get(j), sub_m);
|
||||||
|
expr_ref _e(sub_ctx.mk_eq_atom(cLHS, cRHS), sub_m);
|
||||||
|
branch.push_back(_e);
|
||||||
|
}
|
||||||
|
|
||||||
|
expr_ref final_diseq(mk_not(sub_m, mk_and(branch)), sub_m);
|
||||||
|
fixed_length_assumptions.push_back(final_diseq);
|
||||||
|
fixed_length_lesson.insert(final_diseq, std::make_tuple(rational(-3), f, f));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool theory_str::fixed_length_reduce_contains(smt::kernel & subsolver, expr_ref f, expr_ref & cex) {
|
||||||
|
ast_manager & m = get_manager();
|
||||||
|
context & ctx = get_context();
|
||||||
|
|
||||||
|
ast_manager & sub_m = subsolver.m();
|
||||||
|
context & sub_ctx = subsolver.get_context();
|
||||||
|
|
||||||
|
expr * full;
|
||||||
|
expr * small;
|
||||||
|
u.str.is_contains(f, full, small);
|
||||||
|
|
||||||
|
expr_ref haystack(full, m);
|
||||||
|
expr_ref needle(small, m);
|
||||||
|
|
||||||
|
ptr_vector<expr> haystack_chars(fixed_length_reduce_string_term(subsolver, haystack));
|
||||||
|
ptr_vector<expr> needle_chars(fixed_length_reduce_string_term(subsolver, needle));
|
||||||
|
|
||||||
|
if (needle_chars.size() == 0) {
|
||||||
|
// all strings "contain" the empty one
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (haystack_chars.size() == 0 && needle_chars.size() > 0) {
|
||||||
|
// the empty string doesn't "contain" any non-empty string
|
||||||
|
cex = m.mk_or(m.mk_not(f), ctx.mk_eq_atom(mk_strlen(needle), mk_int(0)),
|
||||||
|
m_autil.mk_ge(mk_strlen(haystack), mk_int(0)));
|
||||||
|
th_rewriter m_rw(m);
|
||||||
|
m_rw(cex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needle_chars.size() > haystack_chars.size()) {
|
||||||
|
// a string can't contain a longer one
|
||||||
|
// X contains Y -> len(X) >= len(Y)
|
||||||
|
expr_ref minus_one(m_autil.mk_numeral(rational::minus_one(), true), m);
|
||||||
|
expr_ref zero(m_autil.mk_numeral(rational::zero(), true), m);
|
||||||
|
expr_ref lens(m_autil.mk_add(mk_strlen(haystack), m_autil.mk_mul(minus_one, mk_strlen(needle))), m);
|
||||||
|
cex = m.mk_or(m.mk_not(f), m_autil.mk_ge(lens, zero));
|
||||||
|
th_rewriter m_rw(m);
|
||||||
|
m_rw(cex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// find all positions at which `needle` could occur in `haystack`
|
||||||
|
expr_ref_vector branches(m);
|
||||||
|
for (unsigned i = 0; i <= (haystack_chars.size() - needle_chars.size()); ++i) {
|
||||||
|
// i defines the offset into haystack_chars
|
||||||
|
expr_ref_vector branch(m);
|
||||||
|
for (unsigned j = 0; j < needle_chars.size(); ++j) {
|
||||||
|
// needle[j] == haystack[i+j]
|
||||||
|
ENSURE(i+j < haystack_chars.size());
|
||||||
|
expr_ref cLHS(needle_chars.get(j), sub_m);
|
||||||
|
expr_ref cRHS(haystack_chars.get(j), sub_m);
|
||||||
|
expr_ref _e(sub_ctx.mk_eq_atom(cLHS, cRHS), sub_m);
|
||||||
|
branch.push_back(_e);
|
||||||
|
}
|
||||||
|
branches.push_back(mk_and(branch));
|
||||||
|
}
|
||||||
|
|
||||||
|
expr_ref final_diseq(mk_or(branches), sub_m);
|
||||||
|
fixed_length_assumptions.push_back(final_diseq);
|
||||||
|
fixed_length_lesson.insert(final_diseq, std::make_tuple(rational(-2), f, f));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool theory_str::fixed_length_reduce_negative_contains(smt::kernel & subsolver, expr_ref f, expr_ref & cex) {
|
||||||
|
ast_manager & m = get_manager();
|
||||||
|
context & ctx = get_context();
|
||||||
|
|
||||||
|
ast_manager & sub_m = subsolver.m();
|
||||||
|
context & sub_ctx = subsolver.get_context();
|
||||||
|
|
||||||
|
expr * full;
|
||||||
|
expr * small;
|
||||||
|
u.str.is_contains(f, full, small);
|
||||||
|
|
||||||
|
expr_ref haystack(full, m);
|
||||||
|
expr_ref needle(small, m);
|
||||||
|
|
||||||
|
ptr_vector<expr> haystack_chars(fixed_length_reduce_string_term(subsolver, haystack));
|
||||||
|
ptr_vector<expr> needle_chars(fixed_length_reduce_string_term(subsolver, needle));
|
||||||
|
|
||||||
|
if (needle_chars.size() == 0) {
|
||||||
|
// all strings "contain" the empty one
|
||||||
|
cex = m.mk_or(m.mk_not(f), m.mk_not(ctx.mk_eq_atom(mk_strlen(needle), mk_int(0))));
|
||||||
|
th_rewriter m_rw(m);
|
||||||
|
m_rw(cex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (haystack_chars.size() == 0 && needle_chars.size() > 0) {
|
||||||
|
// the empty string doesn't "contain" any non-empty string
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needle_chars.size() > haystack_chars.size()) {
|
||||||
|
// a string can't contain a longer one
|
||||||
|
// X contains Y -> len(X) >= len(Y)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// find all positions at which `needle` could occur in `haystack`
|
||||||
|
expr_ref_vector branches(m);
|
||||||
|
for (unsigned i = 0; i <= (haystack_chars.size() - needle_chars.size()); ++i) {
|
||||||
|
// i defines the offset into haystack_chars
|
||||||
|
expr_ref_vector branch(m);
|
||||||
|
for (unsigned j = 0; j < needle_chars.size(); ++j) {
|
||||||
|
// needle[j] == haystack[i+j]
|
||||||
|
ENSURE(i+j < haystack_chars.size());
|
||||||
|
expr_ref cLHS(needle_chars.get(j), sub_m);
|
||||||
|
expr_ref cRHS(haystack_chars.get(j), sub_m);
|
||||||
|
expr_ref _e(sub_ctx.mk_eq_atom(cLHS, cRHS), sub_m);
|
||||||
|
branch.push_back(_e);
|
||||||
|
}
|
||||||
|
branches.push_back(mk_and(branch));
|
||||||
|
}
|
||||||
|
|
||||||
|
expr_ref final_diseq(mk_not(sub_m, mk_and(branches)), sub_m);
|
||||||
|
fixed_length_assumptions.push_back(final_diseq);
|
||||||
|
fixed_length_lesson.insert(final_diseq, std::make_tuple(rational(-3), f, f));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
lbool theory_str::fixed_length_model_construction(expr_ref_vector formulas, expr_ref_vector &precondition,
|
||||||
|
obj_map<expr, zstring> &model, expr_ref_vector &cex) {
|
||||||
|
|
||||||
|
ast_manager & m = get_manager();
|
||||||
|
|
||||||
|
if (bitvector_character_constants.empty()) {
|
||||||
|
bv_util bv(m);
|
||||||
|
sort * bv8_sort = bv.mk_sort(8);
|
||||||
|
for (unsigned i = 0; i < 256; ++i) {
|
||||||
|
rational ch(i);
|
||||||
|
expr_ref chTerm(bv.mk_numeral(ch, bv8_sort), m);
|
||||||
|
bitvector_character_constants.push_back(chTerm);
|
||||||
|
fixed_length_subterm_trail.push_back(chTerm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_trace_enabled("str")) {
|
||||||
|
TRACE_CODE(
|
||||||
|
ast_manager & m = get_manager();
|
||||||
|
context & ctx = get_context();
|
||||||
|
tout << "dumping all formulas:" << std::endl;
|
||||||
|
for (expr_ref_vector::iterator i = formulas.begin(); i != formulas.end(); ++i) {
|
||||||
|
expr * ex = *i;
|
||||||
|
tout << mk_pp(ex, m) << (ctx.is_relevant(ex) ? "" : " (NOT REL)") << std::endl;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fixed_length_subterm_trail.reset();
|
||||||
|
fixed_length_used_len_terms.reset();
|
||||||
|
fixed_length_assumptions.reset();
|
||||||
|
var_to_char_subterm_map.reset();
|
||||||
|
uninterpreted_to_char_subterm_map.reset();
|
||||||
|
fixed_length_lesson.reset();
|
||||||
|
|
||||||
|
// Boolean formulas on which to apply abstraction refinement.
|
||||||
|
expr_ref_vector abstracted_boolean_formulas(m);
|
||||||
|
|
||||||
|
smt_params subsolver_params;
|
||||||
|
smt::kernel subsolver(m, subsolver_params);
|
||||||
|
subsolver.set_logic(symbol("QF_BV"));
|
||||||
|
|
||||||
|
sort * str_sort = u.str.mk_string_sort();
|
||||||
|
sort * bool_sort = m.mk_bool_sort();
|
||||||
|
|
||||||
|
for (expr * f : formulas) {
|
||||||
|
// reduce string formulas only. ignore others
|
||||||
|
sort * fSort = m.get_sort(f);
|
||||||
|
if (fSort == bool_sort && !is_quantifier(f)) {
|
||||||
|
// extracted terms
|
||||||
|
expr * subterm;
|
||||||
|
expr * lhs;
|
||||||
|
expr * rhs;
|
||||||
|
if (m.is_eq(f, lhs, rhs)) {
|
||||||
|
sort * lhs_sort = m.get_sort(lhs);
|
||||||
|
if (lhs_sort == str_sort) {
|
||||||
|
TRACE("str_fl", tout << "reduce string equality: " << mk_pp(lhs, m) << " == " << mk_pp(rhs, m) << std::endl;);
|
||||||
|
expr_ref cex(m);
|
||||||
|
expr_ref left(lhs, m);
|
||||||
|
expr_ref right(rhs, m);
|
||||||
|
if (!fixed_length_reduce_eq(subsolver, left, right, cex)) {
|
||||||
|
// missing a side condition. assert it and return unknown
|
||||||
|
assert_axiom(cex);
|
||||||
|
add_persisted_axiom(cex);
|
||||||
|
return l_undef;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TRACE("str_fl", tout << "skip reducing formula " << mk_pp(f, m) << ", not an equality over strings" << std::endl;);
|
||||||
|
}
|
||||||
|
} else if (u.str.is_in_re(f)) {
|
||||||
|
TRACE("str_fl", tout << "WARNING: regex constraints not yet implemented in fixed-length model construction!" << std::endl;);
|
||||||
|
return l_undef;
|
||||||
|
} else if (u.str.is_contains(f)) {
|
||||||
|
// TODO in some cases (e.g. len(haystack) is only slightly greater than len(needle))
|
||||||
|
// we might be okay to assert the full disjunction because there are very few disjuncts
|
||||||
|
if (m_params.m_FixedLengthRefinement) {
|
||||||
|
TRACE("str_fl", tout << "abstracting out positive contains: " << mk_pp(f, m) << std::endl;);
|
||||||
|
abstracted_boolean_formulas.push_back(f);
|
||||||
|
} else {
|
||||||
|
TRACE("str_fl", tout << "reduce positive contains: " << mk_pp(f, m) << std::endl;);
|
||||||
|
expr_ref cex(m);
|
||||||
|
expr_ref cont(f, m);
|
||||||
|
if (!fixed_length_reduce_contains(subsolver, cont, cex)) {
|
||||||
|
assert_axiom(cex);
|
||||||
|
add_persisted_axiom(cex);
|
||||||
|
return l_undef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (u.str.is_prefix(f)) {
|
||||||
|
TRACE("str_fl", tout << "reduce positive prefix: " << mk_pp(f, m) << std::endl;);
|
||||||
|
expr_ref cex(m);
|
||||||
|
expr_ref pref(f, m);
|
||||||
|
if (!fixed_length_reduce_prefix(subsolver, pref, cex)) {
|
||||||
|
assert_axiom(cex);
|
||||||
|
add_persisted_axiom(cex);
|
||||||
|
return l_undef;
|
||||||
|
}
|
||||||
|
} else if (u.str.is_suffix(f)) {
|
||||||
|
TRACE("str_fl", tout << "reduce positive suffix: " << mk_pp(f, m) << std::endl;);
|
||||||
|
expr_ref cex(m);
|
||||||
|
expr_ref suf(f, m);
|
||||||
|
if (!fixed_length_reduce_suffix(subsolver, suf, cex)) {
|
||||||
|
assert_axiom(cex);
|
||||||
|
add_persisted_axiom(cex);
|
||||||
|
return l_undef;
|
||||||
|
}
|
||||||
|
}else if (m.is_not(f, subterm)) {
|
||||||
|
// if subterm is a string formula such as an equality, reduce it as a disequality
|
||||||
|
if (m.is_eq(subterm, lhs, rhs)) {
|
||||||
|
sort * lhs_sort = m.get_sort(lhs);
|
||||||
|
if (lhs_sort == str_sort) {
|
||||||
|
TRACE("str_fl", tout << "reduce string disequality: " << mk_pp(lhs, m) << " != " << mk_pp(rhs, m) << std::endl;);
|
||||||
|
expr_ref cex(m);
|
||||||
|
expr_ref left(lhs, m);
|
||||||
|
expr_ref right(rhs, m);
|
||||||
|
if (!fixed_length_reduce_diseq(subsolver, left, right, cex)) {
|
||||||
|
// missing a side condition. assert it and return unknown
|
||||||
|
assert_axiom(cex);
|
||||||
|
add_persisted_axiom(cex);
|
||||||
|
return l_undef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (u.str.is_in_re(subterm)) {
|
||||||
|
TRACE("str_fl", tout << "WARNING: negative regex constraints not yet implemented in fixed-length model construction!" << std::endl;);
|
||||||
|
return l_undef;
|
||||||
|
} else if (u.str.is_contains(subterm)) {
|
||||||
|
TRACE("str_fl", tout << "reduce negative contains: " << mk_pp(subterm, m) << std::endl;);
|
||||||
|
expr_ref cex(m);
|
||||||
|
expr_ref cont(subterm, m);
|
||||||
|
if (!fixed_length_reduce_negative_contains(subsolver, cont, cex)) {
|
||||||
|
assert_axiom(cex);
|
||||||
|
add_persisted_axiom(cex);
|
||||||
|
return l_undef;
|
||||||
|
}
|
||||||
|
} else if (u.str.is_prefix(subterm)) {
|
||||||
|
TRACE("str_fl", tout << "reduce negative prefix: " << mk_pp(subterm, m) << std::endl;);
|
||||||
|
expr_ref cex(m);
|
||||||
|
expr_ref pref(subterm, m);
|
||||||
|
if (!fixed_length_reduce_negative_prefix(subsolver, pref, cex)) {
|
||||||
|
assert_axiom(cex);
|
||||||
|
add_persisted_axiom(cex);
|
||||||
|
return l_undef;
|
||||||
|
}
|
||||||
|
} else if (u.str.is_suffix(subterm)) {
|
||||||
|
TRACE("str_fl", tout << "reduce negative suffix: " << mk_pp(subterm, m) << std::endl;);
|
||||||
|
expr_ref cex(m);
|
||||||
|
expr_ref suf(subterm, m);
|
||||||
|
if (!fixed_length_reduce_negative_suffix(subsolver, suf, cex)) {
|
||||||
|
assert_axiom(cex);
|
||||||
|
add_persisted_axiom(cex);
|
||||||
|
return l_undef;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TRACE("str_fl", tout << "skip reducing formula " << mk_pp(f, m) << ", not a boolean formula we handle" << std::endl;);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TRACE("str_fl", tout << "skip reducing formula " << mk_pp(f, m) << ", not a boolean formula we handle" << std::endl;);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TRACE("str_fl", tout << "skip reducing formula " << mk_pp(f, m) << ", not relevant to strings" << std::endl;);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto e : fixed_length_used_len_terms) {
|
||||||
|
expr * var = &e.get_key();
|
||||||
|
precondition.push_back(m.mk_eq(u.str.mk_length(var), mk_int(e.get_value())));
|
||||||
|
}
|
||||||
|
|
||||||
|
TRACE("str_fl", tout << "calling subsolver" << std::endl;);
|
||||||
|
|
||||||
|
lbool subproblem_status = subsolver.check(fixed_length_assumptions);
|
||||||
|
|
||||||
|
if (subproblem_status == l_true) {
|
||||||
|
bv_util bv(m);
|
||||||
|
TRACE("str_fl", tout << "subsolver found SAT; reconstructing model" << std::endl;);
|
||||||
|
model_ref subModel;
|
||||||
|
subsolver.get_model(subModel);
|
||||||
|
|
||||||
|
expr_substitution subst(m);
|
||||||
|
|
||||||
|
// model_smt2_pp(std::cout, m, *subModel, 2);
|
||||||
|
for (auto entry : var_to_char_subterm_map) {
|
||||||
|
svector<unsigned> assignment;
|
||||||
|
expr * var = entry.m_key;
|
||||||
|
ptr_vector<expr> char_subterms(entry.m_value);
|
||||||
|
for (expr * chExpr : char_subterms) {
|
||||||
|
expr_ref chAssignment(subModel->get_const_interp(to_app(chExpr)->get_decl()), m);
|
||||||
|
rational n;
|
||||||
|
if (chAssignment != nullptr && bv.is_numeral(chAssignment, n)) {
|
||||||
|
assignment.push_back(n.get_unsigned());
|
||||||
|
} else {
|
||||||
|
assignment.push_back((unsigned)'?');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zstring strValue(assignment.size(), assignment.c_ptr());
|
||||||
|
model.insert(var, strValue);
|
||||||
|
subst.insert(var, mk_string(strValue));
|
||||||
|
}
|
||||||
|
for (auto entry : uninterpreted_to_char_subterm_map) {
|
||||||
|
svector<unsigned> assignment;
|
||||||
|
expr * var = entry.m_key;
|
||||||
|
ptr_vector<expr> char_subterms(entry.m_value);
|
||||||
|
for (expr * chExpr : char_subterms) {
|
||||||
|
expr_ref chAssignment(subModel->get_const_interp(to_app(chExpr)->get_decl()), m);
|
||||||
|
rational n;
|
||||||
|
if (chAssignment != nullptr && bv.is_numeral(chAssignment, n)) {
|
||||||
|
assignment.push_back(n.get_unsigned());
|
||||||
|
} else {
|
||||||
|
assignment.push_back((unsigned)'?');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
zstring strValue(assignment.size(), assignment.c_ptr());
|
||||||
|
model.insert(var, strValue);
|
||||||
|
subst.insert(var, mk_string(strValue));
|
||||||
|
}
|
||||||
|
// TODO insert length values into substitution table as well?
|
||||||
|
if (m_params.m_FixedLengthRefinement) {
|
||||||
|
scoped_ptr<expr_replacer> replacer = mk_default_expr_replacer(m);
|
||||||
|
replacer->set_substitution(&subst);
|
||||||
|
th_rewriter rw(m);
|
||||||
|
if (!abstracted_boolean_formulas.empty()) {
|
||||||
|
for (auto f : abstracted_boolean_formulas) {
|
||||||
|
TRACE("str_fl", tout << "refinement of boolean formula: " << mk_pp(f, m) << std::endl;);
|
||||||
|
expr_ref f_new(m);
|
||||||
|
(*replacer)(f, f_new);
|
||||||
|
rw(f_new);
|
||||||
|
TRACE("str_fl", tout << "after substitution and simplification, evaluates to: " << mk_pp(f_new, m) << std::endl;);
|
||||||
|
// now there are three cases, depending on what f_new evaluates to:
|
||||||
|
// true -> OK, do nothing
|
||||||
|
// false -> refine abstraction by generating conflict clause
|
||||||
|
// anything else -> error, probably our substitution was incomplete
|
||||||
|
if (m.is_true(f_new)) {
|
||||||
|
// do nothing
|
||||||
|
} else if (m.is_false(f_new)) {
|
||||||
|
context & ctx = get_context();
|
||||||
|
if (u.str.is_contains(f)) {
|
||||||
|
expr * haystack;
|
||||||
|
expr * needle;
|
||||||
|
u.str.is_contains(f, haystack, needle);
|
||||||
|
expr_ref haystack_assignment(m);
|
||||||
|
expr_ref needle_assignment(m);
|
||||||
|
(*replacer)(haystack, haystack_assignment);
|
||||||
|
(*replacer)(needle, needle_assignment);
|
||||||
|
cex.push_back(f);
|
||||||
|
cex.push_back(ctx.mk_eq_atom(haystack, haystack_assignment));
|
||||||
|
cex.push_back(ctx.mk_eq_atom(needle, needle_assignment));
|
||||||
|
return l_false;
|
||||||
|
} else {
|
||||||
|
TRACE("str_fl", tout << "error: unhandled refinement term " << mk_pp(f, m) << std::endl;);
|
||||||
|
NOT_IMPLEMENTED_YET();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
NOT_IMPLEMENTED_YET();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return l_true;
|
||||||
|
} else if (subproblem_status == l_false) {
|
||||||
|
// TODO replace this with something simpler for now
|
||||||
|
NOT_IMPLEMENTED_YET();
|
||||||
|
TRACE("str_fl", tout << "subsolver found UNSAT; reconstructing unsat core" << std::endl;);
|
||||||
|
TRACE("str_fl", tout << "unsat core has size " << subsolver.get_unsat_core_size() << std::endl;);
|
||||||
|
bool negate_pre = false;
|
||||||
|
for (unsigned i = 0; i < subsolver.get_unsat_core_size(); ++i) {
|
||||||
|
TRACE("str", tout << "entry " << i << " = " << mk_pp(subsolver.get_unsat_core_expr(i), m) << std::endl;);
|
||||||
|
rational index;
|
||||||
|
expr* lhs;
|
||||||
|
expr* rhs;
|
||||||
|
std::tie(index, lhs, rhs) = fixed_length_lesson.find(subsolver.get_unsat_core_expr(i));
|
||||||
|
TRACE("str_fl", tout << "lesson: " << mk_pp(lhs, m) << " == " << mk_pp(rhs, m) << " at index " << index << std::endl;);
|
||||||
|
cex.push_back(refine(lhs, rhs, index));
|
||||||
|
if (index < rational(0)) {
|
||||||
|
negate_pre = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (negate_pre){
|
||||||
|
for (auto ex : precondition) {
|
||||||
|
cex.push_back(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return l_false;
|
||||||
|
} else { // l_undef
|
||||||
|
TRACE("str_fl", tout << "WARNING: subsolver found UNKNOWN" << std::endl;);
|
||||||
|
return l_undef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
expr * theory_str::gen_len_test_options(expr * freeVar, expr * indicator, int tries) {
|
expr * theory_str::gen_len_test_options(expr * freeVar, expr * indicator, int tries) {
|
||||||
ast_manager & m = get_manager();
|
ast_manager & m = get_manager();
|
||||||
context & ctx = get_context();
|
context & ctx = get_context();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue