mirror of
https://github.com/Z3Prover/z3
synced 2026-08-02 20:23:27 +00:00
The array term-graph projection (mbp_qel) treats an array equality (= a b) as an implicit partial array equality and eliminates it by merging the array variable's congruence class. That rewrite is unsound when the array variable being eliminated occurs inside a select/store index position, because the index is a first-class term whose value must be preserved. For example, (select va (= v va)) was rewritten to (select v true) after merging va into v, turning a model-false literal into a model-true one and triggering the qe_mbp validation assertions (qe_mbp.cpp:412 for #7259, qe_mbp.cpp:622 for #7036), or a crash at qsat.cpp:579 in release builds. Detect when an array variable to be eliminated occurs inside a select or store index and fall back to the classic model-based projection (spacer_qe_lite) for such formulas. Both reproducers now return the correct unsat verdict with no assertion failure, and all unit tests pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 57b9b87e-950a-49ea-bbb3-ed585646a5a9
This commit is contained in:
parent
b0c15fd46c
commit
5bd9e6a009
1 changed files with 44 additions and 0 deletions
|
|
@ -420,6 +420,41 @@ public:
|
||||||
e = mk_and(fmls);
|
e = mk_and(fmls);
|
||||||
return any_of(subterms::all(e), [&](expr* c) { return seq.is_char(c) || seq.is_seq(c); });
|
return any_of(subterms::all(e), [&](expr* c) { return seq.is_char(c) || seq.is_seq(c); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The array term-graph projection (mbp_qel) treats an array equality
|
||||||
|
// (= a b) as an implicit partial array equality and eliminates it via
|
||||||
|
// class-merging. That rewrite is unsound when such an equality (or an
|
||||||
|
// array variable being eliminated) occurs inside a select/store *index*
|
||||||
|
// position, because the index is a first-class term whose value must be
|
||||||
|
// preserved. Detect that situation so we can fall back to the classic,
|
||||||
|
// model-based projection which handles it correctly (issues #7259, #7036).
|
||||||
|
bool has_array_var_in_index(app_ref_vector const& vars, expr* fml) {
|
||||||
|
array_util au(m);
|
||||||
|
ptr_vector<app> arr_vars;
|
||||||
|
for (app* v : vars)
|
||||||
|
if (au.is_array(v))
|
||||||
|
arr_vars.push_back(v);
|
||||||
|
if (arr_vars.empty())
|
||||||
|
return false;
|
||||||
|
for (expr* t : subterms::all(expr_ref(fml, m))) {
|
||||||
|
if (!is_app(t))
|
||||||
|
continue;
|
||||||
|
app* a = to_app(t);
|
||||||
|
bool is_sel = au.is_select(a);
|
||||||
|
bool is_st = au.is_store(a);
|
||||||
|
if (!is_sel && !is_st)
|
||||||
|
continue;
|
||||||
|
// args[0] is the array; the trailing arg of a store is the stored
|
||||||
|
// value; everything in between is an index argument.
|
||||||
|
unsigned n = a->get_num_args();
|
||||||
|
unsigned last = is_st ? n - 1 : n;
|
||||||
|
for (unsigned i = 1; i < last; ++i)
|
||||||
|
for (app* v : arr_vars)
|
||||||
|
if (occurs(v, a->get_arg(i)))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
void operator()(bool force_elim, app_ref_vector& vars, model& model, expr_ref_vector& fmls, vector<mbp::def>* defs = nullptr) {
|
void operator()(bool force_elim, app_ref_vector& vars, model& model, expr_ref_vector& fmls, vector<mbp::def>* defs = nullptr) {
|
||||||
//don't use mbp_qel on some theories where model evaluation is
|
//don't use mbp_qel on some theories where model evaluation is
|
||||||
//incomplete This is not a limitation of qel. Fix this either by
|
//incomplete This is not a limitation of qel. Fix this either by
|
||||||
|
|
@ -554,6 +589,15 @@ public:
|
||||||
void spacer_qel(app_ref_vector& vars, model& mdl, expr_ref& fml) {
|
void spacer_qel(app_ref_vector& vars, model& mdl, expr_ref& fml) {
|
||||||
TRACE(qe, tout << "Before projection:\n" << fml << "\n" << "Vars: " << vars << "\n";);
|
TRACE(qe, tout << "Before projection:\n" << fml << "\n" << "Vars: " << vars << "\n";);
|
||||||
|
|
||||||
|
// The array term-graph projection is unsound when an array variable to
|
||||||
|
// be eliminated occurs inside a select/store index. Fall back to the
|
||||||
|
// classic model-based projection in that case (issues #7259, #7036).
|
||||||
|
if (has_array_var_in_index(vars, fml)) {
|
||||||
|
TRACE(qe, tout << "array var in index: using model-based projection\n";);
|
||||||
|
spacer_qe_lite(vars, mdl, fml);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
model_evaluator eval(mdl, m_params);
|
model_evaluator eval(mdl, m_params);
|
||||||
eval.set_model_completion(true);
|
eval.set_model_completion(true);
|
||||||
app_ref_vector other_vars(m);
|
app_ref_vector other_vars(m);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue