mirror of
https://github.com/Z3Prover/z3
synced 2026-07-04 22:36:10 +00:00
Fix model generation bugs causing crashes in QF_ABV, UFBVLIA, and UFDT benchmarks
1. Fix Ackermannization model converter (QF_ABV): When solve-eqs or elim-unconstrained eliminates abstract constants introduced by ackermannize_bv, those constants are missing from the model when the Ackermann model converter runs. This causes missing array entries and invalid models. Fix: iterate over ALL entries in ackr_info (not just model constants) to reconstruct missing values via model completion. 2. Fix null dereference in model generator (UFBVLIA): When get_fresh_value returns nullptr (finite sort exhausted) or when m_root2value has no entry for a dependency child, fall back to get_some_value instead of crashing. 3. Fix UNREACHABLE assertions in model finder (UFBVLIA): Handle nested quantifiers as atoms and unsimplified OP_IMPLIES/OP_XOR gracefully instead of aborting. 4. Reduce datatype max fresh depth from 512 to 128 (UFDT): Prevents stack overflow on Linux for deeply recursive datatype models. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
b1d4f36c85
commit
4b32bab0ac
5 changed files with 43 additions and 6 deletions
|
|
@ -71,6 +71,10 @@ class ackr_info {
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef obj_map<func_decl, app*>::iterator c2t_iterator;
|
||||||
|
c2t_iterator begin_c2t() const { return m_c2t.begin(); }
|
||||||
|
c2t_iterator end_c2t() const { return m_c2t.end(); }
|
||||||
|
|
||||||
inline app* get_abstr(app* term) const {
|
inline app* get_abstr(app* term) const {
|
||||||
return m_t2c.find(term);
|
return m_t2c.find(term);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,8 @@ void ackr_model_converter::convert_constants(model * source, model * destination
|
||||||
evaluator.set_model_completion(true);
|
evaluator.set_model_completion(true);
|
||||||
array_util autil(m);
|
array_util autil(m);
|
||||||
|
|
||||||
|
obj_hashtable<func_decl> processed;
|
||||||
|
|
||||||
for (unsigned i = 0, n = source->get_num_constants(); i < n; ++i) {
|
for (unsigned i = 0, n = source->get_num_constants(); i < n; ++i) {
|
||||||
func_decl * const c = source->get_constant(i);
|
func_decl * const c = source->get_constant(i);
|
||||||
app * const term = info->find_term(c);
|
app * const term = info->find_term(c);
|
||||||
|
|
@ -110,9 +112,30 @@ void ackr_model_converter::convert_constants(model * source, model * destination
|
||||||
TRACE(ackermannize, tout << mk_ismt2_pp(c, m) << " " << mk_ismt2_pp(term, m) << "\n";);
|
TRACE(ackermannize, tout << mk_ismt2_pp(c, m) << " " << mk_ismt2_pp(term, m) << "\n";);
|
||||||
if (!term)
|
if (!term)
|
||||||
destination->register_decl(c, value);
|
destination->register_decl(c, value);
|
||||||
else if (autil.is_select(term))
|
else if (autil.is_select(term)) {
|
||||||
add_entry(evaluator, term, value, array_interpretations);
|
add_entry(evaluator, term, value, array_interpretations);
|
||||||
else
|
processed.insert(c);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
add_entry(evaluator, term, value, interpretations);
|
||||||
|
processed.insert(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process any abstract constants from ackr_info that are missing from the model.
|
||||||
|
// This can happen when downstream tactics (e.g., solve-eqs) eliminate the constant
|
||||||
|
// before it reaches the solver, so it has no model value.
|
||||||
|
for (auto it = info->begin_c2t(); it != info->end_c2t(); ++it) {
|
||||||
|
func_decl * const c = it->m_key;
|
||||||
|
if (processed.contains(c))
|
||||||
|
continue;
|
||||||
|
app * const term = it->m_value;
|
||||||
|
expr_ref value(m);
|
||||||
|
value = evaluator(m.mk_const(c));
|
||||||
|
TRACE(ackermannize, tout << "missing from model: " << mk_ismt2_pp(c, m) << " " << mk_ismt2_pp(term, m) << " -> " << value << "\n";);
|
||||||
|
if (autil.is_select(term))
|
||||||
|
add_entry(evaluator, term, value, array_interpretations);
|
||||||
|
else
|
||||||
add_entry(evaluator, term, value, interpretations);
|
add_entry(evaluator, term, value, interpretations);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ class datatype_factory : public struct_factory {
|
||||||
datatype_util m_util;
|
datatype_util m_util;
|
||||||
obj_map<sort, expr *> m_last_fresh_value;
|
obj_map<sort, expr *> m_last_fresh_value;
|
||||||
unsigned m_fresh_depth = 0;
|
unsigned m_fresh_depth = 0;
|
||||||
static const unsigned m_max_fresh_depth = 512;
|
static const unsigned m_max_fresh_depth = 128;
|
||||||
|
|
||||||
expr * get_last_fresh_value(sort * s);
|
expr * get_last_fresh_value(sort * s);
|
||||||
expr * get_almost_fresh_value(sort * s);
|
expr * get_almost_fresh_value(sort * s);
|
||||||
|
|
|
||||||
|
|
@ -2163,7 +2163,9 @@ namespace smt {
|
||||||
}
|
}
|
||||||
|
|
||||||
SASSERT(is_quantifier(atom));
|
SASSERT(is_quantifier(atom));
|
||||||
UNREACHABLE();
|
// Nested quantifiers as atoms are not expected but can occur
|
||||||
|
// in unsimplified formulas. Skip gracefully.
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
void process_literal(expr* atom, polarity pol) {
|
void process_literal(expr* atom, polarity pol) {
|
||||||
|
|
@ -2205,7 +2207,9 @@ namespace smt {
|
||||||
switch (static_cast<basic_op_kind>(to_app(curr)->get_decl_kind())) {
|
switch (static_cast<basic_op_kind>(to_app(curr)->get_decl_kind())) {
|
||||||
case OP_IMPLIES:
|
case OP_IMPLIES:
|
||||||
case OP_XOR:
|
case OP_XOR:
|
||||||
UNREACHABLE(); // simplifier eliminated ANDs, IMPLIEs, and XORs
|
// Implies/XOR should be simplified away but handle gracefully
|
||||||
|
// by treating as uninterpreted boolean.
|
||||||
|
process_literal(curr, pol);
|
||||||
break;
|
break;
|
||||||
case OP_OR:
|
case OP_OR:
|
||||||
case OP_AND:
|
case OP_AND:
|
||||||
|
|
|
||||||
|
|
@ -143,6 +143,8 @@ namespace smt {
|
||||||
CTRACE(model, n == 0,
|
CTRACE(model, n == 0,
|
||||||
tout << mk_pp(r->get_expr(), m) << "\nsort:\n" << mk_pp(s, m) << "\n";
|
tout << mk_pp(r->get_expr(), m) << "\nsort:\n" << mk_pp(s, m) << "\n";
|
||||||
tout << "is_finite: " << m_model->is_finite(s) << "\n";);
|
tout << "is_finite: " << m_model->is_finite(s) << "\n";);
|
||||||
|
if (!n)
|
||||||
|
n = m_model->get_some_value(s);
|
||||||
}
|
}
|
||||||
return alloc(expr_wrapper_proc, to_app(n));
|
return alloc(expr_wrapper_proc, to_app(n));
|
||||||
}
|
}
|
||||||
|
|
@ -371,7 +373,11 @@ namespace smt {
|
||||||
TRACE(mg_top_sort, tout << "#" << n->get_owner_id() << " (" << mk_pp(n->get_expr(), m) << "): "
|
TRACE(mg_top_sort, tout << "#" << n->get_owner_id() << " (" << mk_pp(n->get_expr(), m) << "): "
|
||||||
<< mk_pp(child->get_expr(), m) << " " << mk_pp(child->get_root()->get_expr(), m) << "\n";);
|
<< mk_pp(child->get_expr(), m) << " " << mk_pp(child->get_root()->get_expr(), m) << "\n";);
|
||||||
child = child->get_root();
|
child = child->get_root();
|
||||||
dependency_values.push_back(m_root2value[child]);
|
app * child_val = nullptr;
|
||||||
|
m_root2value.find(child, child_val);
|
||||||
|
if (!child_val)
|
||||||
|
child_val = to_app(m_model->get_some_value(child->get_sort()));
|
||||||
|
dependency_values.push_back(child_val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val = proc->mk_value(*this, dependency_values);
|
val = proc->mk_value(*this, dependency_values);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue