Skip to content

Fix #655 Shape B: refinement-of-Array element inference + release v0.0.146#662

Merged
aallan merged 2 commits into
mainfrom
fix-655-shape-b
May 12, 2026
Merged

Fix #655 Shape B: refinement-of-Array element inference + release v0.0.146#662
aallan merged 2 commits into
mainfrom
fix-655-shape-b

Conversation

@aallan

@aallan aallan commented May 12, 2026

Copy link
Copy Markdown
Owner

Summary

  • Closes Layer-1 #626 surfaced 8 silent-skip cases: 7 spurious generic-decl warnings + 1 real codegen gap (head over refinement-of-Array) #655 Shape B: head([1, 2, 3]) over a NonEmptyArray = { @Array<Int> | predicate } refinement-alias now compiles and runs (returns 1) instead of trapping with unknown func: $head at WASM validation.
  • One-line root cause: _alias_array_element in vera/wasm/inference.py followed alias-target chains via isinstance(target, ast.NamedType) only; RefinementType wrapping was never unwrapped. Fix peels the RefinementType layers (mirroring the existing pattern in _canonical_named_type).
  • Bundle v0.0.146 release prep (per the ABSOLUTE RULE — no separate cleanup PR).

Closes #655.

Root cause (one paragraph)

vera/wasm/inference.py::_alias_array_element resolved alias chains by checking isinstance(target, ast.NamedType) only. For type NonEmptyArray = { @Array<Int> | array_length(@Array<Int>.0) > 0 }, the alias target is a RefinementType(base_type=NamedType("Array", ...), predicate=...) — the isinstance(..., NamedType) check failed and the helper returned None. Downstream _infer_index_element_type returned None for @NonEmptyArray.0[0], the head function got dropped via [E602], and any call site referenced a non-existent $head at WASM validation.

Fix

if type_name in self._type_aliases:
    target = self._type_aliases[type_name]
    while isinstance(target, ast.RefinementType):
        target = target.base_type
    if isinstance(target, ast.NamedType):
        return self._alias_array_element(target.name, target.type_args)

Three lines added (the while loop). Mirrors the existing RefinementType unwrap pattern in _canonical_named_type (lines 619-622).

Regression tests

TestHeadOverRefinement655ShapeB class with two tests:

  1. test_head_over_refinement_compiles_and_runs(func $head ...) appears in WAT (function not dropped); execute() returns 1.
  2. test_head_emits_no_e602_for_refinement_alias — no [E602] warning for head in the diagnostic stream.

Both fail against the pre-fix code path; both pass post-fix.

Allowlist + KNOWN_ISSUES + example cleanup

  • scripts/check_e602_clean.py allowlist shrinks 6 → 5 entries (the head entry removed)
  • KNOWN_ISSUES.md: #655 row removed entirely (limitation count 35 → 34)
  • examples/refinement_types.vera::test_refine now calls head([42, 1, 2]) directly so the codepath is exercised end-to-end by scripts/check_examples.py + vera run

Release prep (v0.0.146)

  • vera/__init__.py + pyproject.toml + uv.lock: 0.0.145 → 0.0.146
  • CHANGELOG.md: dated [0.0.146] - 2026-05-12 heading + link refs
  • HISTORY.md Stage 12 row: v0.0.146 line + clarified v0.0.145 row (Shape A only)
  • docs/index.html: version tag
  • README.md / TESTING.md / ROADMAP.md: count refreshes

Test plan

  • mypy clean
  • pytest passes (3,799 passed, 14 skipped — was 3,797 + 2 new regression tests)
  • All 86 conformance programs pass
  • All 34 examples pass vera check + vera verify
  • e602 gate: 116 files clean, 5 allowlist matched, 0 stale
  • version-sync, doc-counts, limitations-sync: consistent
  • CI green

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Fixed indexing/element-inference for refinement-of-Array type aliases so code compiles and runs as expected.
  • Tests

    • Added tests covering the reported case; overall test count increased to 3,813.
  • Chores

    • Version bumped to v0.0.146.
    • Documentation, changelogs, roadmap, README and known-issues updated and synchronised.

Review Change Stack

…er + release v0.0.146

Closes #655 (Shape A was closed in v0.0.145; Shape B is this fix).

## Root cause

`vera/wasm/inference.py::_alias_array_element` followed alias
chains by checking `isinstance(target, ast.NamedType)` only.
When the alias target was a `RefinementType` (e.g.
`type NonEmptyArray = { @array<Int> | array_length(@array<Int>.0) > 0 }`),
the helper returned `None`.  Downstream `_infer_index_element_type`
then returned `None` for `@NonEmptyArray.0[0]`, the enclosing
`head` function got dropped via `[E602]` ("body contains
unsupported expressions — skipped"), and any call site to `head`
referenced a non-existent `$head` → `unknown func: $head` at WASM
validation.

## Fix

In `_alias_array_element`, peel any `RefinementType` layers from
the alias target before checking whether the base is a
`NamedType` pointing at `Array<T>`:

```python
if type_name in self._type_aliases:
    target = self._type_aliases[type_name]
    while isinstance(target, ast.RefinementType):
        target = target.base_type
    if isinstance(target, ast.NamedType):
        return self._alias_array_element(target.name, target.type_args)
```

Mirrors the existing `RefinementType` unwrap pattern in
`_canonical_named_type` (lines 619-622).  Refinement-of-Array
aliases now resolve their element type the same as a bare
`Array<T>`.

## Allowlist + KNOWN_ISSUES cleanup

`scripts/check_e602_clean.py` allowlist shrinks 6 → 5 entries
(the `head` entry is removed).  `KNOWN_ISSUES.md` `#655` row
removed entirely.

## Example update

`examples/refinement_types.vera::test_refine` previously called
only `safe_divide` and `to_percentage`.  Now also calls
`head([42, 1, 2])` so the `head` codepath is exercised
end-to-end by `scripts/check_examples.py` + `vera run`.  Returns
145 instead of 103 (`10/3 + 100` + `42`).

## Regression tests

New `TestHeadOverRefinement655ShapeB` class in
`tests/test_codegen.py` with two tests:

1. `test_head_over_refinement_compiles_and_runs` — asserts
   `(func $head ...)` appears in WAT (function not dropped via
   [E602]) AND that `execute()` returns 1 (`head([1, 2, 3])`).
2. `test_head_emits_no_e602_for_refinement_alias` — asserts no
   `[E602]` warning for `head` in the diagnostic stream.

Both fail against the pre-fix code path; both pass post-fix.

## Release prep (v0.0.146)

Bundled per the ABSOLUTE RULE (no separate cleanup PRs):

- `vera/__init__.py` + `pyproject.toml` + `uv.lock`: 0.0.145 → 0.0.146
- `CHANGELOG.md`: dated `[0.0.146] - 2026-05-12` heading + link refs
- `HISTORY.md` Stage 12 row: v0.0.146 line clarifying Shape A/B split
  on v0.0.145 + adding v0.0.146 row
- `docs/index.html`: version tag
- `README.md` / `TESTING.md` / `ROADMAP.md`: count refreshes
  (3,813 tests, 146 releases)
- `KNOWN_ISSUES.md`: removed #655 row (now fully closed); limitation
  count 34 (was 35)

## Verification

- mypy clean (59 source files)
- pytest: 3,799 passed, 14 skipped (was 3,797 + 2 new regression tests)
- All 86 conformance programs pass
- All 34 examples pass `vera check` + `vera verify`
- e602 gate: 116 files clean, 5 allowlist matched, 0 stale
- version-sync, doc-counts, limitations-sync: consistent

Refs #655 #659

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

coderabbitai Bot commented May 12, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: e7356695-89e0-4524-9b66-d778d20ba110

📥 Commits

Reviewing files that changed from the base of the PR and between e871394 and 379ba8b.

📒 Files selected for processing (2)
  • HISTORY.md
  • scripts/check_e602_clean.py

📝 Walkthrough

Walkthrough

Fixes Shape B of #655: _alias_array_element now unwraps RefinementType alias layers so refinement-of-Array aliases resolve their Array element type. Adds a regression test, removes the head E602 allowlist entry, and bumps release/version metadata to 0.0.146.

Changes

Fix refinement-of-Array element type inference and cleanup

Layer / File(s) Summary
Refinement-of-Array element type inference implementation
vera/wasm/inference.py
InferenceMixin._alias_array_element unwraps RefinementType layers from alias targets before checking for a NamedType array target so element-type resolution works for refinement-of-Array aliases.
Regression test for head over NonEmptyArray refinement
tests/test_codegen.py
Adds TestHeadOverRefinement655ShapeB asserting WAT contains (func $head ...), no [E602] is emitted for head, and head([1,2,3]) returns 1.
Remove head from E602 allowlist
scripts/check_e602_clean.py
Removes ALLOWED_SKIPS["head"] and adds a NOTE that alias unwrapping makes the allowlist entry unnecessary.
Version bump and release documentation
CHANGELOG.md, HISTORY.md, KNOWN_ISSUES.md, README.md, ROADMAP.md, TESTING.md, pyproject.toml, vera/__init__.py
Bump version to 0.0.146, add 0.0.146 changelog entry and compare link, remove known-issue row for head+NonEmptyArray, and update test/metrics totals.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • aallan/vera#629: Related changes in vera/wasm/inference.py that add RefinementType unwrapping for index/return-type inference and helpers for similar alias resolution flows.

Suggested labels

compiler, tests, ci, docs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: fixing issue #655 Shape B (refinement-of-Array element inference) and releasing v0.0.146. It is concise, specific, and clearly reflects the primary objectives.
Linked Issues check ✅ Passed The PR fully addresses Shape B of issue #655 by unwrapping RefinementType layers in vera/wasm/inference.py, adding regression tests, removing the head allowlist entry, and updating documentation. Shape A (spurious generic-decl warnings) is correctly deferred to #604.
Out of Scope Changes check ✅ Passed All changes are in scope: implementation fix in inference.py, regression tests in test_codegen.py, allowlist update in check_e602_clean.py, documentation updates (CHANGELOG, HISTORY, KNOWN_ISSUES, README, ROADMAP, TESTING), and version bumps (pyproject.toml, vera/init.py). No unrelated changes detected.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-655-shape-b

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

@codecov

codecov Bot commented May 12, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 91.05%. Comparing base (01281f9) to head (379ba8b).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #662      +/-   ##
==========================================
+ Coverage   91.03%   91.05%   +0.01%     
==========================================
  Files          60       60              
  Lines       23197    23199       +2     
  Branches      259      259              
==========================================
+ Hits        21117    21123       +6     
+ Misses       2073     2069       -4     
  Partials        7        7              
Flag Coverage Δ
javascript 57.36% <ø> (ø)
python 94.84% <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 Sentry.
📢 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.

@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: 2

🤖 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 `@HISTORY.md`:
- Around line 292-293: The HISTORY.md roll-up counter is out of sync: update the
summary that currently reads "144 tagged releases" to "146 tagged releases" to
reflect the two new entries (v0.0.145 and v0.0.146); also search for and update
the matching tagged-release counter in ROADMAP.md so both counters remain
synchronized after adding the two releases.

In `@scripts/check_e602_clean.py`:
- Around line 95-98: Replace the placeholder PR reference and update the
explanatory block so it correctly states that the `head` entry (`#655` Shape B)
was removed in v0.0.146: find the note mentioning `_alias_array_element` in
`vera/wasm/inference.py` and replace "PR #<TBD>" with the actual PR number that
removed `head`, then revise the surrounding text to stop referring to `head` as
an active allowlist case (explain it was removed and why—due to
`_alias_array_element` unwrapping `RefinementType` layers) so the narrative and
status are consistent.
🪄 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

Run ID: 24268b45-0dd3-47c5-89a4-291cb4357bf4

📥 Commits

Reviewing files that changed from the base of the PR and between 01281f9 and e871394.

⛔ Files ignored due to path filters (6)
  • docs/index.html is excluded by !docs/**
  • docs/index.md is excluded by !docs/**
  • docs/llms-full.txt is excluded by !docs/**
  • docs/llms.txt is excluded by !docs/**
  • examples/refinement_types.vera is excluded by !**/*.vera
  • uv.lock is excluded by !**/*.lock, !uv.lock
📒 Files selected for processing (11)
  • CHANGELOG.md
  • HISTORY.md
  • KNOWN_ISSUES.md
  • README.md
  • ROADMAP.md
  • TESTING.md
  • pyproject.toml
  • scripts/check_e602_clean.py
  • tests/test_codegen.py
  • vera/__init__.py
  • vera/wasm/inference.py
💤 Files with no reviewable changes (1)
  • KNOWN_ISSUES.md

Comment thread HISTORY.md
Comment thread scripts/check_e602_clean.py Outdated
CR-1 (`HISTORY.md:334`) — release counter
The "Total: 810+ commits, 144 tagged releases, 58 active
development days." roll-up was 2 tags out of date.  Updated to
146 to reflect v0.0.145 + v0.0.146.  CodeRabbit also asked to
check ROADMAP.md for a matching counter — verified, ROADMAP
does not carry a tagged-release count (only test/example/chapter
counts).  Skipped that half.  `README.md:184` already had the
correct "146 releases" from the earlier release-prep update.

CR-2 (`scripts/check_e602_clean.py:98`) — allowlist note
Replaced `PR #<TBD>` with `PR #662` and rewrote the surrounding
prose to reflect that `head` is no longer an active allowlist
case.  The new note explains why it was removable (the
`_alias_array_element` `RefinementType` unwrap means
refinement-of-Array aliases resolve their element type the same
as bare `Array<T>`) and explicitly states `#655` is fully
closed.  Narrative + status now consistent.

Validation: e602 gate clean (5/5 matched, 0 stale); mypy clean.

Refs #655 #662

Co-Authored-By: Claude <noreply@anthropic.invalid>
@aallan aallan merged commit d22601e into main May 12, 2026
23 checks passed
@aallan aallan deleted the fix-655-shape-b branch May 12, 2026 08:33
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.

Layer-1 #626 surfaced 8 silent-skip cases: 7 spurious generic-decl warnings + 1 real codegen gap (head over refinement-of-Array)

1 participant