3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-05-16 23:25:36 +00:00

fix and optimize not-contains and regex equalities

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2026-04-21 09:16:00 +02:00
parent c18188cba8
commit 352b14fe2b
4 changed files with 86 additions and 42 deletions

View file

@ -866,8 +866,7 @@ namespace seq {
// pass 2: detect symbol clashes, empty-propagation, and prefix cancellation
for (str_eq& eq : m_str_eq) {
if (!eq.m_lhs || !eq.m_rhs)
continue;
SASSERT(eq.well_formed());
// one side empty, the other not empty => conflict check
// (the actual substitution is done in apply_det_modifier)
@ -1915,8 +1914,7 @@ namespace seq {
for (str_eq const& eq : node->str_eqs()) {
if (eq.is_trivial())
continue;
if (!eq.m_lhs || !eq.m_rhs)
continue;
SASSERT(eq.well_formed());
for (unsigned od = 0; od < 2; ++od) {
bool fwd = (od == 0);
euf::snode* lhead = dir_token(eq.m_lhs, fwd);
@ -1951,8 +1949,7 @@ namespace seq {
for (str_eq const& eq : node->str_eqs()) {
if (eq.is_trivial())
continue;
if (!eq.m_lhs || !eq.m_rhs)
continue;
SASSERT(eq.well_formed());
for (unsigned od = 0; od < 2; ++od) {
bool fwd = od == 0;
euf::snode* lhead = dir_token(eq.m_lhs, fwd);
@ -2189,8 +2186,7 @@ namespace seq {
str_eq const& eq = node->str_eqs()[eq_idx];
if (eq.is_trivial())
continue;
if (!eq.m_lhs || !eq.m_rhs)
continue;
SASSERT(eq.well_formed());
// EqSplit only applies to regex-free equations.
if (!eq.m_lhs->is_regex_free() || !eq.m_rhs->is_regex_free())
continue;
@ -2368,8 +2364,7 @@ namespace seq {
for (str_eq const& eq : node->str_eqs()) {
if (eq.is_trivial())
continue;
if (!eq.m_lhs || !eq.m_rhs)
continue;
SASSERT(eq.well_formed());
euf::snode_vector toks;
eq.m_lhs->collect_tokens(toks);
for (euf::snode* t : toks) {
@ -2399,8 +2394,7 @@ namespace seq {
for (str_eq const& eq : node->str_eqs()) {
if (eq.is_trivial())
continue;
if (!eq.m_lhs || !eq.m_rhs)
continue;
SASSERT(eq.well_formed());
for (unsigned od = 0; od < 2; ++od) {
bool local_fwd = (od == 0);
euf::snode* lhead = dir_token(eq.m_lhs, local_fwd);
@ -2439,7 +2433,7 @@ namespace seq {
str_eq const*& eq_out,
bool& fwd) const {
for (str_eq const& eq : node->str_eqs()) {
SASSERT(eq.m_lhs && eq.m_rhs && !eq.is_trivial());
SASSERT(eq.well_formed() && !eq.is_trivial());
for (unsigned od = 0; od < 2; ++od) {
bool local_fwd = (od == 0);
@ -2506,8 +2500,7 @@ namespace seq {
for (str_eq const& eq : node->str_eqs()) {
if (eq.is_trivial())
continue;
if (!eq.m_lhs || !eq.m_rhs)
continue;
SASSERT(eq.well_formed());
if (eq.m_lhs->is_empty() && eq.m_rhs->first() && eq.m_rhs->first()->is_power()) {
power = eq.m_rhs->first();
dep = eq.m_dep;
@ -2560,10 +2553,9 @@ namespace seq {
// Look for two directional endpoint power tokens with the same base.
for (str_eq const& eq : node->str_eqs()) {
SASSERT(eq.well_formed());
if (eq.is_trivial())
continue;
if (!eq.m_lhs || !eq.m_rhs)
continue;
for (unsigned od = 0; od < 2; ++od) {
bool fwd = (od == 0);
euf::snode* lhead = dir_token(eq.m_lhs, fwd);
@ -2619,12 +2611,9 @@ namespace seq {
bool nielsen_graph::apply_split_power_elim(nielsen_node* node) {
for (str_eq const& eq : node->str_eqs()) {
SASSERT(eq.well_formed());
if (eq.is_trivial())
continue;
// NB: Shuvendu - this test is always false based on how str_eqs are constructed
// it can be an assertion to force the invariant that str_eqs always have both sides non-null.
if (!eq.m_lhs || !eq.m_rhs)
continue;
for (int side = 0; side < 2; ++side) {
euf::snode* pow_side = (side == 0) ? eq.m_lhs : eq.m_rhs;
@ -2760,10 +2749,9 @@ namespace seq {
bool nielsen_graph::apply_gpower_intr(nielsen_node* node) {
for (str_eq const& eq : node->str_eqs()) {
SASSERT(eq.well_formed());
if (eq.is_trivial())
continue;
if (!eq.m_lhs || !eq.m_rhs)
continue;
// Try both directions (ZIPT's ExtendDir(fwd=true/false)).
for (unsigned od = 0; od < 2; ++od) {
@ -3896,7 +3884,7 @@ namespace seq {
// Step 1: Compute LHS (|x|) for each non-eliminating substitution
// using current m_mod_cnt (before bumping).
// Also assert |x|_k >= 0 (mirrors ZIPT's NielsenNode constructor line 172).
svector<std::pair<unsigned, expr*>> lhs_exprs;
vector<std::pair<unsigned, expr_ref>> lhs_exprs;
for (unsigned i = 0; i < substs.size(); ++i) {
auto const& s = substs[i];
if (!s.m_var->is_var())
@ -3904,7 +3892,7 @@ namespace seq {
if (!m_seq.is_seq(s.m_var->get_expr()))
continue;
expr_ref lhs = compute_length_expr(s.m_var);
lhs_exprs.push_back({i, lhs.get()});
lhs_exprs.push_back({i, lhs});
if (s.is_eliminating())
continue;
has_non_elim = true;
@ -3918,12 +3906,10 @@ namespace seq {
// Step 3: Compute RHS (|u|) with bumped mod counts and add |x| = |u|.
for (auto const& p : lhs_exprs) {
unsigned idx = p.first;
expr* lhs_expr = p.second;
for (auto const &[idx, lhs] : lhs_exprs) {
auto const& s = substs[idx];
expr_ref rhs = compute_length_expr(s.m_replacement);
e->add_side_constraint(mk_constraint(m.mk_eq(lhs_expr, rhs), s.m_dep));
e->add_side_constraint(mk_constraint(m.mk_eq(lhs, rhs), s.m_dep));
}
// Step 4: Restore mod counts (temporary bump for computing RHS only).