mirror of
https://github.com/Z3Prover/z3
synced 2026-05-20 17:09:35 +00:00
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>
57 lines
2 KiB
Markdown
57 lines
2 KiB
Markdown
# [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
|
|
|
|
```smt2
|
|
; 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 z` → `y = 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.cpp` — `contains_true_axiom`, `unroll_not_contains`
|
|
- `src/smt/seq/seq_nielsen.h` / `seq_nielsen.cpp` — word equation propagation
|
|
- `src/smt/theory_nseq.cpp` — `assign_eh` for contains, `populate_nielsen_graph`
|