3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-07-15 11:35:42 +00:00
z3/src/tactic/core
Lev Nachmanson 22c779c77c
[snapshot-regression-fix] Fix elim_uncnstr disabled by manager-wide has_type_vars() flag (iss-6260/small-2) (#10063)
## Summary

Fixes a completeness regression where `elim_uncnstr` was silently
disabled for ordinary (non-polymorphic) goals, detected by the
`snapshot-regression` corpus.

- **Originating discussion:**
https://github.com/Z3Prover/bench/discussions/3054
- **Benchmark:** `iss-6260/small-2.smt2` (corpus `Z3Prover/bench`,
`inputs/issues/iss-6260/`)
- **Divergence:** recorded oracle `sat` → current z3 produces `unknown`

### Divergence diff

```diff
--- small-2.expected.out (expected)
+++ produced (current z3)
@@ -1,3 +1,3 @@
-sat
+unknown
 (error "line 17 column 0: unexpected character")
 (error "line 17 column 1: unexpected character")
```

(The `(error ...)` lines are expected: the benchmark contains a stray
```` ``` ```` fence on line 17. Only the `sat` → `unknown` change is the
regression.)

## Root cause

`git bisect` over the regression window pins the flip to commit
`208cc5686` ("fix build"), which added `|| m().has_type_vars()` to the
`elim_uncnstr_tactic` guard and an equivalent `if (m.has_type_vars())
return;` to the `elim_unconstrained` simplifier.

`ast_manager::has_type_vars()` is a **manager-wide, sticky** flag: it is
set to `true` as soon as *any* type variable is created, and is never
reset. In particular `finite_set_decl_plugin::init()` creates type
variables `A`/`B` to define its polymorphic signatures. Those type
variables never occur in the user's assertions, but once the finite_set
plugin is initialized — which happens while processing this benchmark —
the flag is globally `true` (confirmed by instrumenting `mk_type_var`:
the only type vars created for this benchmark are the finite_set
signature vars `A` and `B`).

As a result `elim_uncnstr` bails out for goals that contain **no**
polymorphic terms at all, i.e. it is effectively disabled. Unconstrained
subterms that used to be eliminated now reach the theory solvers.

For this benchmark the (single) assertion is `(not (xor (>= x 0) (>= x1
0) (>= x 0) x4 (str.contains ...)))`. The duplicated `(>= x 0)` cancels
(`a xor a = false`), and the free Boolean `x4` can fix the parity
regardless of the value of the `str.contains` term, so the goal is
trivially `sat`. That `str.contains`/`str.replace_re` subterm is
unconstrained and was previously removed by `elim_uncnstr`; without that
elimination it reaches `theory_seq`, which marks `str.replace_re` as
unhandled and gives up in `final_check` → `unknown` (`incomplete (theory
seq)`).

## Fix

Make the guard precise. Keep `has_type_vars()` as a cheap pre-filter
(matching existing usage in `ast_translation.cpp` and
`ast_manager::has_type_var`), but only bail out when the goal / asserted
formulas **actually** contain type-variable typed terms, using the
existing `polymorphism::util::has_type_vars(expr*)`.

This preserves the polymorphism crash-protection (goals with genuine
type-variable terms still skip `elim_uncnstr`) while restoring
`elim_uncnstr` for the vast majority of goals that merely triggered a
polymorphic-plugin initialization. Both twin guards (the `elim_uncnstr`
tactic and the `elim_unconstrained` simplifier) are fixed consistently.

## Validation

Built this checkout and re-ran the benchmark (step 5 of the fixer
workflow):

- `./configure && make -C build -j$(nproc)` — Z3 version 4.17.0.
- Unpatched master reproduced the divergence: `z3 -T:20
iss-6260/small-2.smt2` → `unknown`.
- After the fix, `z3 -T:20 inputs/issues/iss-6260/small-2.smt2` produces
**exactly** the recorded oracle:

```
sat
(error "line 17 column 0: unexpected character")
(error "line 17 column 1: unexpected character")
(error "line 17 column 2: unexpected character")
```

- Regression sanity checks: sibling `iss-6260/small.smt2` unchanged
(`sat`); plain arithmetic unconstrained goals unchanged; polymorphic
goals with genuine type-variable terms (including `declare-type-var` and
forcing `(check-sat-using (then elim-uncnstr smt))`) still solve without
crashing — the guard still fires for those.

Opened as a **draft** for human review.




> Generated by [Fix a Z3 snapshot-regression
divergence](https://github.com/Z3Prover/bench/actions/runs/28844840657)
· 861.2 AIC · ⌖ 45.8 AIC · ⊞ 8.9K ·
[◷](https://github.com/search?q=repo%3AZ3Prover%2Fz3+%22gh-aw-workflow-id%3A+snapshot-regression-fixer%22&type=pullrequests)

<!-- gh-aw-agentic-workflow: Fix a Z3 snapshot-regression divergence,
engine: copilot, version: 1.0.63, model: claude-opus-4.8, id:
28844840657, workflow_id: snapshot-regression-fixer, run:
https://github.com/Z3Prover/bench/actions/runs/28844840657 -->

<!-- gh-aw-workflow-id: snapshot-regression-fixer -->
<!-- gh-aw-workflow-call-id: Z3Prover/bench/snapshot-regression-fixer
-->

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-07-07 13:02:37 -07:00
..
blast_term_ite_tactic.cpp Remove old blast-term-ite tactic class, rename blast-term-ite2 to blast-term-ite 2026-02-26 20:07:42 +00:00
blast_term_ite_tactic.h Remove old blast-term-ite tactic class, rename blast-term-ite2 to blast-term-ite 2026-02-26 20:07:42 +00:00
CMakeLists.txt really add fold-unfold as option 2026-04-30 09:56:08 -07:00
cofactor_elim_term_ite.cpp Standardize for-loop increments to prefix form (++i) (#8199) 2026-01-14 19:55:31 -08:00
cofactor_elim_term_ite.h booyah 2020-07-04 15:56:30 -07:00
cofactor_term_ite_tactic.h Remove old cofactor_term_ite_tactic.cpp, use simplifier-based implementation 2026-02-22 00:45:44 +00:00
collect_occs.cpp Standardize for-loop increments to prefix form (++i) (#8199) 2026-01-14 19:55:31 -08:00
collect_occs.h booyah 2020-07-04 15:56:30 -07:00
collect_statistics_tactic.cpp build warnings 2026-05-29 10:17:46 -07:00
collect_statistics_tactic.h booyah 2020-07-04 15:56:30 -07:00
ctx_simplify_tactic.cpp Standardize for-loop increments to prefix form (++i) (#8199) 2026-01-14 19:55:31 -08:00
ctx_simplify_tactic.h Remove unnecessary semicolons (Attempt 2) (#10020) 2026-07-02 12:47:29 -07:00
demodulator_tactic.h update release notes 2023-01-31 12:19:33 -08:00
der_tactic.h Remove old der_tactic implementation; rename mk_der2_tactic to mk_der_tactic 2026-02-22 00:40:49 +00:00
distribute_forall_tactic.h update release notes 2023-01-31 12:19:33 -08:00
dom_simplify_tactic.h update release notes 2023-01-31 12:19:33 -08:00
elim_term_ite_tactic.cpp Standardize for-loop increments to prefix form (++i) (#8199) 2026-01-14 19:55:31 -08:00
elim_term_ite_tactic.h doc 2022-12-07 08:51:18 -08:00
elim_uncnstr2_tactic.h update release notes 2023-01-31 12:19:33 -08:00
elim_uncnstr_tactic.cpp [snapshot-regression-fix] Fix elim_uncnstr disabled by manager-wide has_type_vars() flag (iss-6260/small-2) (#10063) 2026-07-07 13:02:37 -07:00
elim_uncnstr_tactic.h elim_uncnstr_tactic: remove m_imp idiom to reduce mem alloc 2018-12-21 19:48:18 +00:00
eliminate_predicates_tactic.h enable interactive example 2023-03-25 18:13:44 +01:00
fold_unfold_tactic.h really add fold-unfold as option 2026-04-30 09:56:08 -07:00
injectivity_tactic.h Deprecate injectivity_tactic.cpp: forward mk_injectivity_tactic to simplifier-based impl 2026-03-12 05:32:32 +00:00
nnf_tactic.cpp Standardize for-loop increments to prefix form (++i) (#8199) 2026-01-14 19:55:31 -08:00
nnf_tactic.h remove comment that does not align with result 2022-12-06 15:53:55 -08:00
occf_tactic.cpp Refactor mk_and/mk_or call sites to use vector overloads (#8286) 2026-01-22 13:21:22 -08:00
occf_tactic.h more tactic descriptions 2023-01-05 20:23:01 -08:00
pb_preprocess_tactic.cpp Centralize and document TRACE tags using X-macros (#7657) 2025-05-28 14:31:25 +01:00
pb_preprocess_tactic.h doc 2022-12-07 08:51:18 -08:00
propagate_values2_tactic.h update release notes 2023-01-31 12:19:33 -08:00
propagate_values_tactic.cpp Standardize for-loop increments to prefix form (++i) (#8199) 2026-01-14 19:55:31 -08:00
propagate_values_tactic.h spread a few anonymous namespaces and remove some m_imp idioms 2018-12-21 23:02:15 +00:00
reduce_args_tactic.cpp Standardize for-loop increments to prefix form (++i) (#8199) 2026-01-14 19:55:31 -08:00
reduce_args_tactic.h update release notes 2023-01-31 12:19:33 -08:00
simplify_tactic.cpp Standardize for-loop increments to prefix form (++i) (#8199) 2026-01-14 19:55:31 -08:00
simplify_tactic.h Fix : typo-in-simplify-tactic (#7587) 2025-03-18 13:43:12 -10:00
solve_eqs_tactic.h update release notes 2023-01-31 12:19:33 -08:00
special_relations_simplifier.h Fix Python build: move special_relations_simplifier.h to tactic/core to resolve expr_pattern_match.h dependency 2026-03-12 18:02:43 +00:00
special_relations_tactic.cpp Remove old special_relations_tactic class, use simplifier-based tactic as the sole special-relations tactic 2026-03-12 05:30:57 +00:00
special_relations_tactic.h Fix Python build: move special_relations_simplifier.h to tactic/core to resolve expr_pattern_match.h dependency 2026-03-12 18:02:43 +00:00
split_clause_tactic.cpp Standardize for-loop increments to prefix form (++i) (#8199) 2026-01-14 19:55:31 -08:00
split_clause_tactic.h add tactic doc 2023-01-10 18:58:25 -08:00
symmetry_reduce_tactic.cpp Remove unnecessary semicolons (Attempt 2) (#10020) 2026-07-02 12:47:29 -07:00
symmetry_reduce_tactic.h more tactic descriptions 2023-01-05 20:23:01 -08:00
tseitin_cnf_tactic.cpp Standardize for-loop increments to prefix form (++i) (#8199) 2026-01-14 19:55:31 -08:00
tseitin_cnf_tactic.h more tactic descriptions 2023-01-05 20:23:01 -08:00