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

Fix memory leaks in polymorphism instantiation engine

The polymorphism theory routed polymorphic (\) problems through
theory_polymorphism, which instantiated axioms during search. Two leaks:

1. In inst::instantiate, insert_ref_map was constructed with an expr_ref
   argument, so its template parameter D deduced to expr_ref instead of
   expr*. Trail objects are region-allocated and freed without running
   destructors, so the embedded expr_ref never released its reference,
   leaking one AST subtree per instantiation. Pass e_inst.get() so D is
   expr*, matching the raw hashtable + manual inc_ref/dec_ref pattern.

2. trail_stack's destructor does not call reset(), so level-0 trail items
   (including the inc_ref balancing entries for m_from_instantiation) were
   never undone when the theory was destroyed. Added a ~theory_polymorphism
   destructor that calls m_trail.reset().

Also keeps a defensive alias check in util::unify and a fresh per-iteration
substitution in inst::instantiate.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Nikolaj Bjorner 2026-07-03 15:00:10 -07:00
parent 6d5e09e2fa
commit 348bb3b6a4
3 changed files with 13 additions and 2 deletions

View file

@ -107,13 +107,14 @@ namespace polymorphism {
auto const& [tv, fns, substs] = m_instances[e];
for (auto* f2 : fns) {
substitution sub1(m), new_sub(m);
substitution sub1(m);
if (!u.unify(f1, f2, sub1))
continue;
if (substs->contains(&sub1))
continue;
substitutions new_substs;
for (auto* sub2 : *substs) {
substitution new_sub(m);
if (!u.unify(sub1, *sub2, new_sub))
continue;
if (substs->contains(&new_sub))
@ -128,7 +129,7 @@ namespace polymorphism {
new_substs.insert(new_sub1);
m_from_instantiation.insert(e_inst);
m.inc_ref(e_inst);
t.push(insert_ref_map(m, m_from_instantiation, e_inst));
t.push(insert_ref_map(m, m_from_instantiation, e_inst.get()));
}
}
for (auto* sub2 : new_substs) {

View file

@ -244,6 +244,8 @@ namespace polymorphism {
bool util::unify(substitution const& s1, substitution const& s2,
substitution& sub) {
sort* v2;
SASSERT(&s1 != &sub);
SASSERT(&s2 != &sub);
for (auto const& [k, v] : s1) {
// Guard against building a cyclic substitution (e.g. A |-> list(A)),
// which would make substitution application diverge. Such a binding

View file

@ -96,6 +96,14 @@ namespace smt {
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 { }
};