mirror of
https://github.com/Z3Prover/z3
synced 2026-07-12 10:06:23 +00:00
Give up with we have nasty replace_all or friends (otw. we would be unsound due to Nielsen saturation)
This commit is contained in:
parent
ff7cbe9406
commit
63a668a71f
5 changed files with 68 additions and 0 deletions
|
|
@ -1833,6 +1833,26 @@ namespace seq {
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool snode_has_rigid(euf::snode const* s) {
|
||||
for (euf::snode const* t : s->collect_tokens())
|
||||
if (t->is_rigid())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool nielsen_node::references_rigid() const {
|
||||
for (str_eq const& eq : m_str_eq)
|
||||
if (snode_has_rigid(eq.m_lhs) || snode_has_rigid(eq.m_rhs))
|
||||
return true;
|
||||
for (str_deq const& dq : m_str_deq)
|
||||
if (snode_has_rigid(dq.m_lhs) || snode_has_rigid(dq.m_rhs))
|
||||
return true;
|
||||
for (str_mem const& mem : m_str_mem)
|
||||
if (snode_has_rigid(mem.m_str) || snode_has_rigid(mem.m_regex))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
euf::snode const* nielsen_graph::mk_rewrite(expr* e) const {
|
||||
expr_ref er(e, m);
|
||||
th_rewriter rw(m);
|
||||
|
|
@ -1945,6 +1965,7 @@ namespace seq {
|
|||
if (r == search_result::unsat) {
|
||||
++m_stats.m_num_unsat;
|
||||
const auto deps = collect_conflict_deps();
|
||||
m_conflict_sources.reset();
|
||||
m_dep_mgr.linearize(deps, m_conflict_sources);
|
||||
TRACE(seq, display(tout, m_root));
|
||||
return r;
|
||||
|
|
|
|||
|
|
@ -654,6 +654,14 @@ namespace seq {
|
|||
// true if all str_eqs are trivial and there are no str_mems
|
||||
bool is_satisfied() const;
|
||||
|
||||
// true if ANY equality/disequality/membership references a rigid (defined) op
|
||||
// snode (str.replace, str.replace_all, str.replace_re*). Used to defer to the
|
||||
// axiom layer (FC_GIVEUP) before searching: these terms are not free variables
|
||||
// but are pinned by the recfun/axiom layer, and the Nielsen modifiers would
|
||||
// substitute/unify them as if free, discarding their definition and producing
|
||||
// invalid models.
|
||||
bool references_rigid() const;
|
||||
|
||||
// render constraint set as an HTML fragment for DOT node labels.
|
||||
// mirrors ZIPT's NielsenNode.ToHtmlString()
|
||||
std::ostream& to_html(std::ostream& out, obj_map<expr, std::string>& names, uint64_t& next_id, ast_manager& m) const;
|
||||
|
|
|
|||
|
|
@ -922,6 +922,22 @@ namespace smt {
|
|||
|
||||
SASSERT(!m_nielsen.root()->is_currently_conflict());
|
||||
|
||||
// nseq cannot soundly reason about defined sequence operations (str.replace,
|
||||
// str.replace_all, str.replace_re*) inside the Nielsen graph: they are not free
|
||||
// variables but are pinned by the recfun/axiom layer. The modifiers (and the
|
||||
// regex pre-check) would treat them as free (e.g. unifying two distinct
|
||||
// replace_all applications), silently discarding their definition and yielding
|
||||
// invalid models. When such a rigid term participates in the constraints, defer
|
||||
// to the axiom layer and give up. (Concrete replace_all etc. are folded to
|
||||
// literals by seq_rewriter before reaching the sgraph, so only genuinely
|
||||
// symbolic occurrences are affected.) This check precedes the regex pre-check
|
||||
// so a rigid term as a membership subject cannot yield a bogus SAT either.
|
||||
if (m_nielsen.root()->references_rigid()) {
|
||||
IF_VERBOSE(1, verbose_stream() << "nseq final_check: rigid defined op present, FC_GIVEUP\n";);
|
||||
TRACE(seq, tout << "nseq final_check: rigid defined op present, FC_GIVEUP\n");
|
||||
return FC_GIVEUP;
|
||||
}
|
||||
|
||||
// Regex membership pre-check: before running DFS, check intersection
|
||||
// emptiness for each variable's regex constraints. This handles
|
||||
// regex-only problems that the DFS cannot efficiently solve.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue