3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-07-17 04:25:44 +00:00

fix HO-matcher imitation curry-order and instance-assembly ordering bugs

The higher-order matcher produced ill-typed instantiations that aborted
the solve (sort-mismatch / unbound-variable exceptions), making
smt.ho_matching=true net-negative on the TPTP THF benchmarks.

Two root causes:

1. Imitation rule (ho_matcher.cpp): the select chain 'pats' is collected
   outermost-first, i.e. in reverse application order. The imitating
   lambda must curry arguments in application order (first-applied select
   binds the outermost lambda). Reversing 'pats' before building the
   domain/argument/body vectors and the lambda-wrapping loop makes the
   constructed lambda's sort agree with the flex head variable. Fixes
   unit-test ho_matcher test6c/test6d (previously asserted at
   add_binding: v->get_sort() == t->get_sort()).

2. Instance assembly (smt_quantifier.cpp on_ho_match): the fixpoint
   binding substitution used var_subst with the default std_order=true
   while the binding vector is directly indexed (binding[k] = value for
   var k). This resolved chained HO variable references against the wrong
   slots and built ill-sorted terms (assertion at rewriter_def.h:52).
   Use direct (std_order=false) substitution to match the binding layout.

Also adds defensive guards as belt-and-suspenders: subst_sorts_match
skips sort-inconsistent substitutions, an is_ground check skips bindings
with leftover de Bruijn variables, and on_ho_match catches z3_exception
to skip an unusable heuristic instance rather than aborting the solve
(re-raising only on cancellation/resource-limit).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Nikolaj Bjorner 2026-07-05 17:25:01 -07:00
parent 72d27e1cbb
commit 6c8a5cd853
3 changed files with 91 additions and 1 deletions

View file

@ -667,6 +667,21 @@ namespace smt {
quantifier_manager_plugin * mk_fresh() override { return alloc(default_qm_plugin); }
void on_ho_match(euf::ho_subst& s) {
ast_manager& m = m_context->get_manager();
try {
on_ho_match_core(s);
}
catch (z3_exception &) {
// A higher-order binding produced an ill-typed or otherwise
// unusable instantiation term. Adding a heuristic HO instance is
// optional, so we skip this match rather than aborting the solve.
// Re-raise only if the failure was due to cancellation/resource limits.
if (!m.inc())
throw;
}
}
void on_ho_match_core(euf::ho_subst& s) {
ast_manager& m = m_context->get_manager();
auto& st = m_ho_state;
auto* hoq = st.m_q;
@ -684,12 +699,20 @@ namespace smt {
<< "\n"
<< binding << "\n";);
if (binding.size() > q->get_num_decls()) {
var_subst sub(m);
// binding is indexed directly (binding[k] = value for var k),
// so the substitution must use direct (non-standard) order to
// resolve chained HO variable references; the sort guard below
// is checked with the matching order.
var_subst sub(m, false);
bool change = true;
while (change) {
change = false;
for (unsigned i = 1; i < binding.size(); ++i) {
if (!binding.get(i)) continue;
// Skip ill-typed substitutions: a misaligned higher-order
// binding would build an ill-sorted term and abort the solve.
if (!euf::ho_matcher::subst_sorts_match(m, binding.get(i), binding, false))
return;
auto r = sub(binding.get(i), binding);
change |= r != binding.get(i);
binding[i] = r;
@ -708,6 +731,11 @@ namespace smt {
for (expr* e : binding) {
if (!e)
return; // incomplete binding
// A leftover free (de Bruijn) variable means the binding is
// incomplete/misaligned; adding such a term would raise
// "Formulas should not contain unbound variables". Skip it.
if (!is_ground(e))
return;
if (!m_context->e_internalized(e)) {
m_context->internalize(e, false);
}