3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-04-21 03:13:30 +00:00
z3/research/docs/nseq-issues/02-soundness-contains-monotonicity.md
Copilot f518faac9b
Add nseq issue drafts from Ostrich benchmark analysis (discussion #9071) (#9073)
Agent-Logs-Url: https://github.com/Z3Prover/z3/sessions/04321ea7-2a53-4ed5-9f43-816dc6f7476b

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
2026-03-20 19:07:51 -07:00

2 KiB

[nseq] Soundness bug: str.contains not preserved through concatenation

Labels: bug, c3, nseq, soundness

Summary

The nseq solver returns sat for a benchmark that asserts:

  1. z is a substring of y (str.contains y z)
  2. x = y ++ y
  3. z is NOT a substring of x (not (str.contains x z))

The third assertion contradicts the first two, so the formula should be unsat. The seq solver correctly returns unsat; nseq incorrectly returns sat.

Affected benchmark

File seq verdict nseq verdict
contains-4.smt2 unsat sat (WRONG)

Data from: https://github.com/Z3Prover/z3/discussions/9071

Reproducing example

; contains-4.smt2 — EXPECTED: unsat, nseq returns: sat
(set-logic QF_S)
(declare-const x String)
(declare-const y String)
(declare-const z String)
(assert (str.contains y z))
(assert (= x (str.++ y y)))
(assert (not (str.contains x z)))
(check-sat)

Analysis

If str.contains y z is true, then y = L ++ z ++ R for some L, R. Then x = y ++ y = L ++ z ++ R ++ L ++ z ++ R, so z is a substring of x. Therefore not (str.contains x z) is a contradiction.

The nseq solver should derive this via the combination of:

  • contains_true_axiom: str.contains y zy = f1 ++ z ++ f2 (skolems)
  • The equality x = y ++ y
  • The Nielsen graph constraint that propagates the concatenation decomposition

The root cause may be that the nseq solver does not propagate str.contains facts through word equation equalities in the Nielsen graph. When x = y ++ y is processed as a word equation, the contains constraint on y should propagate to derive a contains constraint on x, but this inference appears to be missing.

Files to investigate

  • src/ast/rewriter/seq_axioms.cppcontains_true_axiom, unroll_not_contains
  • src/smt/seq/seq_nielsen.h / seq_nielsen.cpp — word equation propagation
  • src/smt/theory_nseq.cppassign_eh for contains, populate_nielsen_graph