Skip to content

fix: obligate + guard the Int->Nat narrowing at apply_fn arguments (#1017) - #1025

Merged
aallan merged 3 commits into
mainfrom
fix/1017-apply-fn-nat-narrowing-obligation
Jul 13, 2026
Merged

fix: obligate + guard the Int->Nat narrowing at apply_fn arguments (#1017)#1025
aallan merged 3 commits into
mainfrom
fix/1017-apply-fn-nat-narrowing-obligation

Conversation

@aallan

@aallan aallan commented Jul 13, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes the false Tier-1 in #1017: the @Int@Nat narrowing at an apply_fn argument (into the closure's @Nat formal) was not obligated by the verifier and not guarded by codegen — the narrowing dual of the #820 argument-widening handler at the same site.

apply_fn(clo_nat_formal, 0 - 5) verified clean (a false Tier 1); a runtime value entered the @Nat formal reinterpreted with no trap (a silent negative — worse than the issue's framing, confirmed by vera run).

Fix — both surfaces, mirroring the generic call-argument path

  • vera/verifier.py — the apply_fn branch now emits the @Nat narrowing obligation (_nat_binding_target + _narrows_into_nat_check_nat_binding_obligation, guarded=True): E503 for a provably-negative value, a guarded Tier 3 for a runtime value.
  • vera/wasm/closures.py_translate_apply_fn now guards the call_indirect argument with _emit_nat_bind_guard (the narrowing dual of its existing _emit_int_widen_guard), so a Tier-3 narrowing traps at runtime rather than entering the formal silently.

Validation

  • RED → GREEN: a four-quadrant verifier↔codegen differential in test_nat_narrowing_return_differential.py — provably-negative arg (E503), runtime arg (obligated + call_indirect-guarded, run traps), requires-bounded arg (Tier-1 proven, guard dead), @Nat@Nat arg (unobligated, no false guard on a u64.MAX value).
  • Mutation-validated both arms independently: neuter the codegen guard → the runtime-trap test fails; neuter the verifier arm → the obligation tests fail. The cross-component differential is genuine, not two green unit suites hiding a desync.
  • Completeness sweep: the @Nat base (≥ 0) and @Nat-alias formals are covered; the refined-predicate dual (the > 0 part beyond the ≥ 0 base) is still a false Tier-1 at the same site — filed as Verifier+codegen: refined-predicate narrowing into an apply_fn closure formal is not obligated/guarded (false Tier-1) #1024 and swapped into the KNOWN_ISSUES / ROADMAP / SKILL rows.
  • Full local gate green: mypy, 157 conformance programs, 38 examples, 408 targeted verifier/codegen tests, doc counts (7,410).

Closes #1017

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Fixed apply_fn-related narrowing through indirect calls so @Int@Nat is now both statically obligated and runtime-guarded.
    • Added enforcement at the call_indirect boundary to ensure invalid negative values trap as expected.
  • Tests
    • Added differential verification↔codegen coverage for apply_fn argument narrowing outcomes, including obligation and trapping behaviour.
    • Updated test metrics and scenario coverage.
  • Documentation
    • Refreshed known-issues/workarounds, changelog entry, roadmap notes, and updated project test-count reporting.

aallan and others added 2 commits July 13, 2026 20:35
…1017)

The verifier's `apply_fn` branch obligated only the `@Nat` -> `@Int` argument
WIDENING (#820); the `@Int` -> `@Nat` NARROWING dual was missing, so
`apply_fn(clo_nat_formal, 0 - 5)` verified clean (a false Tier 1) and, for a
runtime value, entered the `@Nat` formal reinterpreted with no trap (a silent
negative).

- `vera/verifier.py`: the `apply_fn` loop now emits the `@Nat` narrowing
  obligation (mirroring the generic call-argument path) — E503 for a provable
  negative, a guarded Tier 3 otherwise.
- `vera/wasm/closures.py`: `_translate_apply_fn` now guards the `call_indirect`
  argument with `_emit_nat_bind_guard`, the narrowing dual of its widen guard.

Both arms are mutation-validated independently (neuter each -> a distinct RED).
A four-quadrant verifier<->codegen differential (violated / runtime-trap /
proven / unobligated) is added to `test_nat_narrowing_return_differential.py`.

The completeness sweep found the refined-predicate dual at the same site is
still a false Tier 1 (the strict predicate beyond the `>= 0` base this fixes);
filed as #1024 and swapped into the KNOWN_ISSUES / ROADMAP / SKILL rows.

Co-Authored-By: Claude <noreply@anthropic.invalid>
…ntial

The #1017 corpus resolved every argument to `violated` or `verified` — it
never exercised a genuine Z3-`unknown` `tier3` status, so the `guarded=True`
deferral path (the crux of the cross-component soundness argument) was not
directly asserted. Add the opaque-`float_to_int` argument case (mirroring the
#758 return `_TIER3` quadrant): the verifier records the argument nat_bind
`tier3` and the SAME compiled program carries the `call_indirect` narrowing
guard, so `guarded=True` can never mean "promised but never emitted".

Surfaced by the adversarial code review of the #1017 fix.

Co-Authored-By: Claude <noreply@anthropic.invalid>
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 3586499d-6d87-4be5-802b-cc998f0a1268

📥 Commits

Reviewing files that changed from the base of the PR and between 8a40022 and 8db7078.

📒 Files selected for processing (1)
  • TESTING.md
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • aallan/vera-bench (manual)

📝 Walkthrough

Walkthrough

Changes

The apply_fn closure-argument path now obligates @Int to @Nat narrowing in the verifier and emits a matching WebAssembly runtime guard. Differential tests cover obligation states, traps, safe values, non-narrowing arguments, and tier3 codegen guards. Project documentation and test metrics are updated.

Apply_fn narrowing enforcement

Layer / File(s) Summary
Verifier obligation and call_indirect guard
vera/verifier.py, vera/wasm/closures.py
The verifier records nat_bind obligations for narrowing closure arguments, while _translate_apply_fn emits the corresponding @Nat runtime guard.
Differential narrowing coverage
tests/test_nat_narrowing_return_differential.py, TESTING.md
New tests cover violated, obligated, verified, non-narrowing, and tier3 cases, including runtime traps and emitted guards.
Release and issue tracking updates
CHANGELOG.md, KNOWN_ISSUES.md, README.md, ROADMAP.md, SKILL.md
Documentation records the fixed behaviour, revised bug scope and workaround guidance, and updated test totals.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant apply_fn
  participant Verifier
  participant WasmTranslator
  participant call_indirect
  apply_fn->>Verifier: inspect closure argument narrowing
  Verifier->>Verifier: record nat_bind obligation
  apply_fn->>WasmTranslator: translate closure argument
  WasmTranslator->>call_indirect: emit `@Nat` runtime guard
  call_indirect-->>apply_fn: return or trap
Loading

Possibly related issues

Possibly related PRs

  • aallan/vera#986: Updates the same apply_fn verifier and codegen paths for the opposite @Nat to @Int widening direction.

Suggested labels: compiler, tests, docs

🚥 Pre-merge checks | ✅ 7 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (7 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the apply_fn Int→Nat narrowing fix and its obligation/guard behaviour.
Linked Issues check ✅ Passed The verifier and wasm changes match #1017: they add the narrowing obligation and the matching runtime guard while preserving valid cases.
Out of Scope Changes check ✅ Passed The docs and test updates are directly tied to the same apply_fn narrowing fix, with no unrelated feature work introduced.
Changelog Covers Public-Surface Changes ✅ Passed No public-surface files changed in the PR range; the only code changes were internal verifier/codegen and docs, and CHANGELOG.md describes the fix.
Spec And Implementation Move Together ✅ Passed PASS: the apply_fn narrowing/guard fits existing spec text on call-arguments and apply_fn parameter checking, so no spec drift is introduced.
Diagnostics Carry An Error Code ✅ Passed PASS: the commit delta only changes TESTING.md, so no new or changed diagnostics are introduced; existing verifier/codegen diagnostic paths already use stable E-codes.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/1017-apply-fn-nat-narrowing-obligation

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.51%. Comparing base (1d3c9df) to head (8db7078).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1025   +/-   ##
=======================================
  Coverage   93.51%   93.51%           
=======================================
  Files          96       96           
  Lines       32019    32024    +5     
  Branches      456      456           
=======================================
+ Hits        29943    29948    +5     
  Misses       2063     2063           
  Partials       13       13           
Flag Coverage Δ
javascript 78.41% <ø> (ø)
python 95.29% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@greptile-apps

greptile-apps Bot commented Jul 13, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes a false Tier-1 verifier pass and missing runtime guard for @Int → @Nat narrowing at apply_fn argument positions — the narrowing dual of the #820 argument-widening handler. Before this fix, apply_fn(clo, 0 - 5) where the closure formal is @Nat verified clean and silently stored the negative value with no trap.

  • vera/verifier.py: the apply_fn branch now checks _nat_binding_target + _narrows_into_nat before the existing widening arm, emitting a nat_bind obligation (guarded=True) that yields E503 for a provably-negative argument or an honest Tier 3 for a runtime value.
  • vera/wasm/closures.py: _translate_apply_fn now prepends _emit_nat_bind_guard on the argument instructions when the closure formal is @Nat and the argument narrows into it, providing the runtime trap the verifier promises.
  • Tests: a four-quadrant verifier↔codegen differential (TestApplyFnArgNarrowingDifferential1017) covers the violated / runtime-trap / proven / unobligated cases, with mutation-validated independence between the two arms.

Confidence Score: 5/5

Safe to merge — the verifier and codegen changes are tightly symmetric, the four-quadrant differential was mutation-validated independently for each arm, and the remaining gap (#1024) is correctly scoped as a follow-on issue.

Both changed components mirror the existing #820 widening handler at the same site; no unguarded code path remains for the @Nat-base narrowing at apply_fn argument positions.

No files require special attention.

Important Files Changed

Filename Overview
vera/verifier.py Adds @int -> @nat narrowing obligation in the apply_fn branch, mirroring the existing @nat -> @int widening arm; logic is symmetric and correct.
vera/wasm/closures.py Adds _emit_nat_bind_guard for @Nat-formal arguments in _translate_apply_fn, the codegen dual of the verifier's new narrowing obligation; placed correctly as the narrowing-first arm before the existing widening arm.
tests/test_nat_narrowing_return_differential.py Adds four-quadrant differential for apply_fn argument narrowing (violated/runtime-trap/proven/unobligated); corpora are well-isolated. Docstring on _return_nat_bind_statuses no longer describes all callers accurately.
CHANGELOG.md Adds changelog entry for #1017 fix; accurate and well-scoped.
KNOWN_ISSUES.md Replaces #1017 row with #1024 (refinement-predicate narrowing), correctly tracking the remaining known gap.

Reviews (2): Last reviewed commit: "docs: note the tier-3 quadrant in the #1..." | Re-trigger Greptile

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@TESTING.md`:
- Line 89: The TESTING.md row for test_nat_narrowing_return_differential.py
omits the `#1017` tier3 apply_fn argument-narrowing coverage. Update that row’s
`#1017` description to include the _APPLYFN_ARG_TIER3 /
test_tier3_arg_promised_guard_is_emitted quadrant, noting that it cross-checks
the verifier’s tier3 promise with the emitted i64.lt_s/unreachable runtime guard
in one pipeline run.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f1b3d031-6b3d-4916-9565-739979790009

📥 Commits

Reviewing files that changed from the base of the PR and between 1d3c9df and 8a40022.

⛔ Files ignored due to path filters (2)
  • docs/SKILL.md is excluded by !docs/**
  • docs/llms-full.txt is excluded by !docs/**
📒 Files selected for processing (9)
  • CHANGELOG.md
  • KNOWN_ISSUES.md
  • README.md
  • ROADMAP.md
  • SKILL.md
  • TESTING.md
  • tests/test_nat_narrowing_return_differential.py
  • vera/verifier.py
  • vera/wasm/closures.py
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • aallan/vera-bench (manual)

Comment thread TESTING.md Outdated
The row's #1017 description listed only the four original outcomes; the
follow-on tier-3 quadrant (an opaque float_to_int argument recording tier3
with the codegen guard emitted) was added to the test file but not the prose.
Add the clause so the description matches the actual coverage.

Co-Authored-By: Claude <noreply@anthropic.invalid>
@aallan
aallan merged commit bce2701 into main Jul 13, 2026
29 checks passed
@aallan
aallan deleted the fix/1017-apply-fn-nat-narrowing-obligation branch July 13, 2026 21:07
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.

Verifier: @Int->@Nat narrowing into an apply_fn closure formal is not obligated (false Tier-1)

1 participant