Skip to content

feat(verify): teach Z3 verifier about element-section table resolver (v1.0.4 Track B)#126

Merged
avrabe merged 1 commit into
mainfrom
release/v1.0.4-pr-verifier-table-resolver
May 17, 2026
Merged

feat(verify): teach Z3 verifier about element-section table resolver (v1.0.4 Track B)#126
avrabe merged 1 commit into
mainfrom
release/v1.0.4-pr-verifier-table-resolver

Conversation

@avrabe
Copy link
Copy Markdown
Contributor

@avrabe avrabe commented May 17, 2026

Drops directize's Z3 bypass from v1.0.2. The verifier now resolves i32.const N; call_indirect (type T) to the same pure_call_<F>(args) Z3 expression PR-K3 uses for direct call F. All 3 directize tests pass with Z3 verification ACTIVE.

🤖 Generated with Claude Code

…(v1.0.4 Track B)

Drops directize's Z3 bypass from v1.0.2. The verifier now resolves
'i32.const N; call_indirect (type T)' to the same 'pure_call_<F>(args)'
Z3 expression that PR-K3 uses for direct 'call F' — proving them equal
under congruence closure.

## What changed

verify.rs:
- VerificationSignatureContext gains a 'table_resolver: HashMap<(u32, u32), u32>'
  field, populated via crate::optimize::build_table_resolver(module).
- New 'resolve_indirect_call(table_idx, slot, expected_type)' helper
  returns Some(func_idx) only if BOTH the resolver maps the slot AND
  the resolved function's signature is byte-identical to the indirect-
  call's declared type.
- The CallIndirect encoder now checks whether the popped slot BV is
  concrete via z3 0.19.7's BV::as_u64() (works for our I32Const(N)
  encoding which produces BV::from_i64(n, 32)). If concrete AND resolved,
  it encodes the result as 'pure_call_<F>(args)' via FuncDecl::apply
  — SAME function symbol PR-K3 uses for direct Call, so two equivalent
  forms produce structurally equal BVs.
- The resolved-call path SKIPS the global/memory havoc that the
  fallback indirect-call encoding performs. Soundness justification:
  the resolved callee's purity is a property of the underlying func
  symbol (pure_call_F has no state-modification semantics).

lib.rs:
- 'build_table_resolver' and 'TableResolver' alias are now pub(crate)
  so the verifier can use them.
- directize() now constructs TranslationValidator::new_with_context()
  with the populated sig context. Z3 verification is BACK; the v1.0.2
  comment block explaining the Z3 bypass is replaced with one
  explaining the Z3-active mode.

## Tests

All 3 existing directize tests pass with Z3 verification active:
- test_directize_folds_known_indirect_call
- test_directize_skips_non_const_index
- test_directize_skips_out_of_range_index

The structural guards (no Unknown + slot resolves + signature matches)
remain as defense-in-depth. Z3 is now the load-bearing proof.

## Why this matters

v1.0.2 deferred Track 3 with an honest explanation: 'The verifier
encodes call_indirect and call F as INDEPENDENT uninterpreted functions
[so] congruence cannot prove them equal.' This PR teaches the verifier
the resolver, mapping resolved-indirect calls to the SAME function
symbol PR-K3 uses for direct calls. The congruence axiom does the rest.

## v1.0.5+ follow-ups

- Multi-result resolved indirect calls (currently fall through to the
  fresh-symbolic path; needs Z3 tuple sort).
- Concrete-slot detection beyond BV::as_u64() (e.g., propagating
  through I32Eqz / I32Eq folded constants).
- A regression test in verify.rs's own test module asserting structural
  BV equality between 'call F' and resolved 'call_indirect'.

Trace: REQ-1, REQ-6, REQ-11
@avrabe avrabe merged commit 27f5e48 into main May 17, 2026
9 of 19 checks passed
@avrabe avrabe deleted the release/v1.0.4-pr-verifier-table-resolver branch May 17, 2026 11:08
avrabe added a commit that referenced this pull request May 18, 2026
Four-track parallel sprint with FULL success (vs v1.0.3 where Track 3 died):

  #125  Track A  async-callback adapter pass (#70 first piece)
  #126  Track B  verifier table-resolver teaching (drops directize Z3 bypass)
  #127  Track C  ægraph rewrite engine + 3 identity rules
  #128  Track D  island-model parallel optimization (#71)

Plus a subagent new-issues sweep that found zero issues since v1.0.3.

+20 tests, 380+ total. Code-section bytes unchanged on the current
corpus — all four tracks ship infrastructure that will produce
measurable wins once their consumers land in v1.0.5+.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant