3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-22 16:45:31 +00:00

add theory_str::try_eval_concat to work around rewriter behaviour

this fixes a regression in concat-013.smt2
This commit is contained in:
Murphy Berzish 2016-09-14 16:18:03 -04:00
parent e46fc7b0b6
commit a294c145dc
2 changed files with 63 additions and 1 deletions

View file

@ -762,7 +762,7 @@ expr * theory_str::mk_concat(expr * n1, expr * n2) {
}
bool theory_str::can_propagate() {
return !m_basicstr_axiom_todo.empty() || !m_str_eq_todo.empty() || !m_concat_axiom_todo.empty()
return !m_basicstr_axiom_todo.empty() || !m_str_eq_todo.empty() || !m_concat_axiom_todo.empty() || !m_concat_eval_todo.empty()
|| !m_axiom_CharAt_todo.empty() || !m_axiom_StartsWith_todo.empty() || !m_axiom_EndsWith_todo.empty()
|| !m_axiom_Contains_todo.empty() || !m_axiom_Indexof_todo.empty() || !m_axiom_Indexof2_todo.empty() || !m_axiom_LastIndexof_todo.empty()
|| !m_axiom_Substr_todo.empty() || !m_axiom_Replace_todo.empty()
@ -794,6 +794,11 @@ void theory_str::propagate() {
}
m_concat_axiom_todo.reset();
for (unsigned i = 0; i < m_concat_eval_todo.size(); ++i) {
try_eval_concat(m_concat_eval_todo[i]);
}
m_concat_eval_todo.reset();
for (unsigned i = 0; i < m_axiom_CharAt_todo.size(); ++i) {
instantiate_axiom_CharAt(m_axiom_CharAt_todo[i]);
}
@ -853,6 +858,58 @@ void theory_str::propagate() {
}
}
/*
* Attempt to evaluate a concat over constant strings,
* and if this is possible, assert equality between the
* flattened string and the original term.
*/
void theory_str::try_eval_concat(enode * cat) {
SASSERT(is_concat(cat));
app * a_cat = cat->get_owner();
context & ctx = get_context();
ast_manager & m = get_manager();
TRACE("t_str_detail", tout << "attempting to flatten " << mk_pp(a_cat, m) << std::endl;);
std::stack<app*> worklist;
std::string flattenedString("");
bool constOK = true;
{
app * arg0 = to_app(a_cat->get_arg(0));
app * arg1 = to_app(a_cat->get_arg(1));
worklist.push(arg1);
worklist.push(arg0);
}
while (constOK && !worklist.empty()) {
app * evalArg = worklist.top(); worklist.pop();
if (m_strutil.is_string(evalArg)) {
std::string nextStr = m_strutil.get_string_constant_value(evalArg);
flattenedString.append(nextStr);
} else if (is_concat(evalArg)) {
app * arg0 = to_app(evalArg->get_arg(0));
app * arg1 = to_app(evalArg->get_arg(1));
worklist.push(arg1);
worklist.push(arg0);
} else {
TRACE("t_str_detail", tout << "non-constant term in concat -- giving up." << std::endl;);
constOK = false;
break;
}
}
if (constOK) {
TRACE("t_str_detail", tout << "flattened to \"" << flattenedString << "\"" << std::endl;);
expr_ref constStr(m_strutil.mk_string(flattenedString), m);
expr_ref axiom(ctx.mk_eq_atom(a_cat, constStr), m);
assert_axiom(axiom);
}
}
/*
* Instantiate an axiom of the following form:
* Length(Concat(x, y)) = Length(x) + Length(y)
@ -6240,6 +6297,9 @@ void theory_str::set_up_axioms(expr * ex) {
if (is_concat(ap)) {
// if ex is a concat, set up concat axioms later
m_concat_axiom_todo.push_back(n);
// we also want to check whether we can eval this concat,
// in case the rewriter did not totally finish with this term
m_concat_eval_todo.push_back(n);
} else if (is_strlen(ap)) {
// if the argument is a variable,
// keep track of this for later, we'll need it during model gen

View file

@ -184,6 +184,7 @@ namespace smt {
svector<std::pair<enode*,enode*> > m_str_eq_todo;
ptr_vector<enode> m_concat_axiom_todo;
ptr_vector<enode> m_string_constant_length_todo;
ptr_vector<enode> m_concat_eval_todo;
// enode lists for term-specific axioms
// TODO maybe refactor this into a generic "library_aware_axiom_todo" list
@ -332,6 +333,7 @@ namespace smt {
bool is_Unroll(enode const * n) const { return is_Unroll(n->get_owner()); }
void instantiate_concat_axiom(enode * cat);
void try_eval_concat(enode * cat);
void instantiate_basic_string_axioms(enode * str);
void instantiate_str_eq_length_axiom(enode * lhs, enode * rhs);