3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-07-22 23:15:51 +00:00

bugfixes to front-end and matcher

This commit is contained in:
Nikolaj Bjorner 2026-07-06 15:29:02 -07:00
parent 6c8a5cd853
commit 470e966791
8 changed files with 275 additions and 67 deletions

View file

@ -170,6 +170,147 @@ namespace euf {
m_matcher.add_pattern(pat.get());
m_matcher(pat, t, 3, 1);
}
// Structural regression test derived from TPTP ANA067^1 (which fails
// under smt.ho_matching=true inside the full solver).
// pattern: (select (select v0 v3) v2) with v0 a doubly-nested array (flex head)
// term: (select K ...) matched term is itself array-sorted.
// Exercises imitation/projection of a flex head against an array-sorted
// term. The matcher must build only well-sorted bindings; the debug
// asserts in match_goals::push and mk_project catch any regression that
// commits an ill-sorted (extra array level) lambda/select.
void test7() {
sort_ref r(m_arith.mk_real(), m);
sort_ref arr_rb(m_array.mk_array_sort(r, m.mk_bool_sort()), m); // (Array Real Bool)
sort_ref arr_r_rb(m_array.mk_array_sort(r, arr_rb), m); // (Array Real (Array Real Bool))
sort_ref arr_r_r_rb(m_array.mk_array_sort(r, arr_r_rb), m); // v0 sort
expr_ref v0(m.mk_var(0, arr_r_r_rb), m);
expr_ref v2(m.mk_var(2, r), m);
expr_ref v3(m.mk_var(3, r), m);
expr_ref pat(m_array.mk_select(v0, v3), m);
pat = m_array.mk_select(pat, v2);
expr_ref K(m.mk_const(symbol("K"), arr_r_rb), m);
expr_ref b(m.mk_const(symbol("b"), r), m);
expr_ref t(m_array.mk_select(K, b), m);
IF_VERBOSE(0, verbose_stream() << "test7: " << pat << " =?= " << t << "\n";);
m_matcher.add_pattern(pat.get());
m_matcher(pat, t, 5);
// Faithful variant: the term index argument is itself
// (select fun (lambda (t) c)) as in ANA067^1, i.e. a term whose
// subterm is a function(array)-valued lambda. This forces the
// matcher to decompose/imitate against a lambda-bearing term.
sort_ref arr_rr(m_array.mk_array_sort(r, r), m); // (Array Real Real)
sort_ref fun_sort(m_array.mk_array_sort(arr_rr, r), m); // (Array (Array Real Real) Real)
symbol tt("t");
sort* r_s = r.get();
expr_ref c0(m.mk_const(symbol("c0"), r), m);
expr_ref lam(m.mk_lambda(1, &r_s, &tt, c0), m); // (lambda (t Real) c0)
expr_ref fun(m.mk_const(symbol("fun"), fun_sort), m);
expr_ref idx(m_array.mk_select(fun, lam), m); // : Real
expr_ref t2(m_array.mk_select(K, idx), m);
IF_VERBOSE(0, verbose_stream() << "test7b: " << pat << " =?= " << t2 << "\n";);
m_matcher.add_pattern(pat.get());
m_matcher(pat, t2, 5);
}
// Structural regression test derived from TPTP PHI008^4 (which fails
// under smt.ho_matching=true inside the full solver).
// pattern: (select v3 v4) v3 flex head, v4 a flex arg of an array sort
// term: (select P ...) P a concrete array constant.
// Exercises projecting/imitating a flex head over a function(array)-sorted
// argument (incl. a lambda-valued term arg). The matcher must build only
// well-sorted bindings; debug asserts guard against regressions.
void test8() {
sort_ref i(m.mk_uninterpreted_sort(symbol("qML_i")), m);
sort_ref mu(m.mk_uninterpreted_sort(symbol("qML_mu")), m);
sort_ref arr_ib(m_array.mk_array_sort(i, m.mk_bool_sort()), m); // (Array qML_i Bool)
sort_ref arr_mu_ib(m_array.mk_array_sort(mu, arr_ib), m); // (Array qML_mu (Array qML_i Bool))
sort_ref p_sort(m_array.mk_array_sort(arr_mu_ib, arr_ib), m); // P sort
expr_ref v3(m.mk_var(3, p_sort), m);
expr_ref v4(m.mk_var(4, arr_mu_ib), m);
expr_ref pat(m_array.mk_select(v3, v4), m);
expr_ref P(m.mk_const(symbol("P"), p_sort), m);
expr_ref ell(m.mk_const(symbol("ell"), arr_mu_ib), m);
expr_ref t(m_array.mk_select(P, ell), m);
IF_VERBOSE(0, verbose_stream() << "test8: " << pat << " =?= " << t << "\n";);
m_matcher.add_pattern(pat.get());
m_matcher(pat, t, 9);
// Variant with a lambda-valued term argument, mirroring PHI008's
// (select scott_P (lambda (Y) (lambda (Z) ...))) goal that forces
// the matcher to decompose a flex head against a lambda term.
symbol yv("Y");
sort* mu_s = mu.get();
expr_ref cbody(m.mk_const(symbol("C"), arr_ib), m);
expr_ref lam(m.mk_lambda(1, &mu_s, &yv, cbody), m); // (lambda (Y qML_mu) C) : arr_mu_ib
expr_ref t2(m_array.mk_select(P, lam), m);
IF_VERBOSE(0, verbose_stream() << "test8b: " << pat << " =?= " << t2 << "\n";);
m_matcher.add_pattern(pat.get());
m_matcher(pat, t2, 9);
}
// Structural regression test for the ITP127-style shape (fails under
// smt.ho_matching=true inside the full solver). The flex head H has an
// applied result sort that is *itself* an array (monomo = (Array d Bool)).
// Matching (select (select H x1) x2) =?= f where f is array-sorted is a
// case where imitation could build a lambda with an extra array level
// (an ill-sorted select). The matcher must build only well-sorted
// bindings; debug asserts guard against regressions.
void test9() {
sort_ref c(m.mk_uninterpreted_sort(symbol("c")), m);
sort_ref d(m.mk_uninterpreted_sort(symbol("d")), m);
sort_ref monomo(m_array.mk_array_sort(d, m.mk_bool_sort()), m); // (Array d Bool)
sort_ref h1(m_array.mk_array_sort(c, monomo), m); // (Array c monomo)
sort_ref h2(m_array.mk_array_sort(c, h1), m); // (Array c (Array c monomo))
expr_ref H(m.mk_var(0, h2), m);
expr_ref x1(m.mk_var(1, c), m);
expr_ref x2(m.mk_var(2, c), m);
expr_ref pat(m_array.mk_select(H, x1), m);
pat = m_array.mk_select(pat, x2); // (select (select H x1) x2) : monomo
expr_ref f(m.mk_const(symbol("f"), monomo), m); // f : (Array d Bool)
IF_VERBOSE(0, verbose_stream() << "test9: " << pat << " =?= " << f << "\n";);
m_matcher.add_pattern(pat.get());
m_matcher(pat, f, 3);
}
// Faithful isolation test for the refine-time sort guard used by
// ho_matcher::refine_ho_match (throws "sort mismatch ..." on failure).
// Mirrors the SEV510^1 family where a bound-variable binding's sort
// disagrees with the pattern variable it would fill. subst_sorts_match
// must detect the mismatch (return false) and accept the matching case.
void test10() {
sort_ref int2int(m_array.mk_array_sort(m_int, m_int), m);
// pattern (select v0 v1): v0 is the array (idx 0), v1 the index (idx 1)
expr_ref v0(m.mk_var(0, int2int), m);
expr_ref v1(m.mk_var(1, m_int), m);
expr_ref pat(m_array.mk_select(v0, v1), m);
// std_order=true: var idx maps to s[size-idx-1], so idx0->s[1], idx1->s[0].
expr_ref arr(m.mk_const(symbol("arr"), int2int), m);
expr_ref i0(m_arith.mk_int(0), m);
// Well-sorted substitution: idx0 -> arr (array), idx1 -> 0 (int).
expr_ref_vector s_ok(m);
s_ok.push_back(i0); // s[0] -> var idx 1 (Int) OK
s_ok.push_back(arr); // s[1] -> var idx 0 (Array) OK
VERIFY(ho_matcher::subst_sorts_match(m, pat, s_ok, true));
// Ill-sorted substitution: idx0 (array var) bound to an Int -> mismatch.
expr_ref_vector s_bad(m);
s_bad.push_back(i0); // s[0] -> var idx 1 (Int) OK
s_bad.push_back(i0); // s[1] -> var idx 0 expects Array, got Int -> mismatch
VERIFY(!ho_matcher::subst_sorts_match(m, pat, s_bad, true));
IF_VERBOSE(0, verbose_stream() << "test10: subst_sorts_match detects sort mismatch\n";);
}
};
}
@ -183,6 +324,10 @@ void tst_ho_matcher() {
tm.test4();
tm.test5();
tm.test6();
tm.test7();
tm.test8();
tm.test9();
tm.test10();
}
catch (std::exception const& ex) {
std::cout << ex.what() << "\n";