3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-07-05 14:56:11 +00:00

Fix TPTP polymorphism crashes in final-check and model checking

Root-caused and fixed 261 debug-assertion crashes found by running Z3
across the TPTP benchmarks (-tptp -T:5 model_validate=true):

1. theory_polymorphism::final_check_eh returned FC_DONE after assigning
   the negation of its (already-true) theory assumption, which creates a
   conflict. Returning FC_DONE reported l_true while the context was
   inconsistent, tripping SASSERT(status != l_true || !inconsistent())
   in context::restart. Return FC_CONTINUE so conflict resolution turns
   it into l_false and the normal research loop runs.

2. model_evaluator::get_macro, polymorphic branch: def = subst(def)
   assigned an expr_ref temporary to a raw expr*&; the temporary freed
   the freshly substituted term, leaving def dangling (use-after-free
   during model evaluation). Pin the substituted def in m_pinned, as the
   as-array path already does.

3. smt_model_checker::add_instance: relax stale
   SASSERT(!m.is_model_value(sk_term)); get_inv may legitimately return a
   model value in polymorphic settings, already handled downstream by
   get_type_compatible_term.

Unit tests: 92 passed, 0 failed. All 261 assertion crashes resolved;
the 3 remaining files are controlled ERR_PARSER (exit 103) rejections.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Nikolaj Bjorner 2026-07-03 20:32:04 -07:00
parent 348bb3b6a4
commit 3d29d81607
3 changed files with 16 additions and 3 deletions

View file

@ -404,7 +404,9 @@ struct evaluator_cfg : public default_rewriter_cfg {
polymorphism::substitution subst(m); polymorphism::substitution subst(m);
polymorphism::util util(m); polymorphism::util util(m);
util.unify(f, m.poly_root(f), subst); util.unify(f, m.poly_root(f), subst);
def = subst(def); expr_ref d = subst(def);
m_pinned.push_back(d);
def = d;
SASSERT(def != nullptr); SASSERT(def != nullptr);
} }

View file

@ -222,7 +222,8 @@ namespace smt {
expr * sk_term = m_model_finder.get_inv(q, i, sk_value, *cex, sk_term_gen); expr * sk_term = m_model_finder.get_inv(q, i, sk_value, *cex, sk_term_gen);
if (sk_term != nullptr) { if (sk_term != nullptr) {
TRACE(model_checker, tout << "Found inverse " << mk_pp(sk_term, m) << "\n";); TRACE(model_checker, tout << "Found inverse " << mk_pp(sk_term, m) << "\n";);
SASSERT(!m.is_model_value(sk_term)); // get_inv may return a model value in polymorphic settings;
// this is handled downstream by get_type_compatible_term.
max_generation = std::max(sk_term_gen, max_generation); max_generation = std::max(sk_term_gen, max_generation);
sk_value = sk_term; sk_value = sk_term;
} }

View file

@ -67,8 +67,18 @@ namespace smt {
} }
final_check_status final_check_eh(unsigned) override { final_check_status final_check_eh(unsigned) override {
if (m_inst.pending()) if (m_inst.pending()) {
// There are still polymorphic axioms to instantiate. Force the
// solver to fail under the theory assumption so that a new
// research round (see should_research) can assert the new
// instances. Assigning the negation of the (already true)
// assumption creates a conflict, so we must return FC_CONTINUE
// to let conflict resolution turn it into l_false; returning
// FC_DONE here would report l_true while the context is
// inconsistent, violating a core search invariant.
ctx.assign(~mk_literal(m_assumption), nullptr); ctx.assign(~mk_literal(m_assumption), nullptr);
return FC_CONTINUE;
}
return FC_DONE; return FC_DONE;
} }