3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-07-18 13:05:46 +00:00
z3/src/smt/theory_polymorphism.h
Nikolaj Bjorner 3d29d81607 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>
2026-07-03 20:32:04 -07:00

123 lines
4.2 KiB
C++

/*++
Copyright (c) 2023 Microsoft Corporation
Module Name:
theory_polymorphism.h
Abstract:
Plugin for handling polymorphism
The plugin instantiates polymorphic axioms based on occurrences of polymorphic functions in other axioms.
It uses blocking literals to restart search when there are new axioms that can be instantiated.
Author:
Nikolaj Bjorner (nbjorner) 2013-07-11
--*/
#pragma once
#include "ast/polymorphism_inst.h"
#include "smt/smt_theory.h"
namespace smt {
class theory_polymorphism : public theory {
trail_stack m_trail;
polymorphism::inst m_inst;
expr_ref m_assumption;
unsigned m_qhead = 0;
bool m_pending = true;
bool internalize_atom(app*, bool) override { return false; }
bool internalize_term(app*) override { return false; }
void new_eq_eh(theory_var, theory_var) override { }
void new_diseq_eh(theory_var, theory_var) override {}
theory* mk_fresh(context* new_ctx) override { return alloc(theory_polymorphism, *new_ctx); }
char const * get_name() const override { return "polymorphism"; }
void display(std::ostream& out) const override {}
void push_scope_eh() override {
m_trail.push_scope();
}
void pop_scope_eh(unsigned n) override {
m_trail.pop_scope(n);
}
bool can_propagate() override {
return m_pending;
}
/**
* Assert instances of polymorphic axioms
*/
void propagate() override {
if (!m_pending)
return;
m_pending = false;
vector<polymorphism::instantiation> instances;
m_inst.instantiate(instances);
if (instances.empty())
return;
for (auto const& [orig, inst, sub] : instances)
ctx.add_asserted(inst);
ctx.internalize_assertions();
}
final_check_status final_check_eh(unsigned) override {
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);
return FC_CONTINUE;
}
return FC_DONE;
}
void add_theory_assumptions(expr_ref_vector & assumptions) override {
if (m_qhead == ctx.get_num_asserted_formulas())
return;
m_assumption = m.mk_fresh_const("poly", m.mk_bool_sort());
assumptions.push_back(m_assumption);
ctx.push_trail(value_trail(m_qhead));
for (; m_qhead < ctx.get_num_asserted_formulas(); ++m_qhead)
m_inst.add(ctx.get_asserted_formula(m_qhead));
m_pending = true;
}
bool should_research(expr_ref_vector & assumptions) override {
for (auto * a : assumptions)
if (a == m_assumption)
return true;
return false;
}
public:
theory_polymorphism(context& ctx):
theory(ctx, poly_family_id),
m_inst(ctx.get_manager(), m_trail),
m_assumption(ctx.get_manager()) {}
~theory_polymorphism() override {
// Undo level-0 trail items (e.g. the inc_ref balancing entries that
// m_inst pushes for m_from_instantiation). trail_stack's destructor
// does not call reset(), so without this the references those items
// hold would leak when the theory is destroyed.
m_trail.reset();
}
void init_model(model_generator & mg) override { }
};
}