v0.0.145
Fixed
-
#604 / #655 Shape A — generic prelude combinator mono clones now produce the correct type-arg suffix when the closure argument is a
SlotReftyped as an FnType alias (e.g.@Doubler.0wheretype Doubler = fn(Int -> Int)). Pre-fix_unify_param_arginvera/codegen/monomorphize.pyhad anAnonFn-specific alias-resolution path;SlotRefargs typed as FnType aliases skipped that path and left the closure's return type variable unbound. The unbound type var fell to the"Bool"phantom-var fallback at result-building time, producing mono suffixes likeoption_map$Int_Boolinstead ofoption_map$Int_Intand trapping at runtime withwasm trap: indirect call type mismatch. Post-fix: bothAnonFnliterals andSlotRef-typed-as-FnType-alias args flow through a shared_resolve_arg_fn_shapehelper, binding the closure's return type uniformly. Same fix applied at the WASM call-site rewriting layer (vera/wasm/calls.py::_infer_fn_alias_type_args_wasm). Three of the five[E602]/[E604]prelude-skip cases (option_map,option_and_then,result_map) close at runtime; the other two (option_unwrap_or,result_unwrap_or) were already working via mono and only emitted misleading template warnings. -
#604 / #655 Shape A — template-warning suppression — audit recommendation 2 from the #604 investigation comment: post-compile suppression pass in
vera/codegen/core.py::compile_programdrops[E602]/[E604]/[E605]template-only warnings on genericforall<T>decls whose mono clones successfully compile. Pre-fix every program importing the prelude saw 5 spurious warnings aboutoption_unwrap_or/option_map/option_and_then/result_unwrap_or/result_mapeven when those functions worked end-to-end via mono. Post-fix the warnings only fire for forall decls whose generic body cannot be compiled AND has no working mono clone — preserving the "this generic can never compile and you're never using a mono clone of it" signal for genuinely-broken or unused user generics, while removing the prelude-noise. Allowlist shrinks from 11 to 6 entries (5 user-code generics from #655 Shape A removed; the 6 remaining are the prelude generics still firing in test files that don't call them, plus theheadreal codegen gap). -
Documentation fix in
CLAUDE.mdrelease-workflow section — "Stage 9 table" reference (stale since the project moved through Stages 10, 11, 12) replaced with a stage-agnostic instruction: "the most recent Stage table inHISTORY.md" with agrep "^## Stage" HISTORY.md | tail -1hint for confirming the current stage before writing. Caught during the 2026-05-11 review cycle.
Added
-
Layer 3 of #626 — new
vera/skip.pywith two control-flow exception classes:CodegenSkip(node, reason)(raised when a translator hits an unsupported AST shape; caught at the_compile_fn/_compile_lifted_closureboundary and converted to a structured[E602]diagnostic with the unsupported-node's source span) andCodegenInvariantError(msg, node=None)(raised on states that type-check should have rejected; surfaced as a new[E699]"Internal compiler error" at severity=errorsovera compileexits non-zero — compiler bugs shouldn't be maskable as soft warnings in CI logs). An audit of all 372return Nonesites invera/codegen/**andvera/wasm/**classified each into SILENT_SKIP / PROPAGATE / OPTIONAL_RETURN / INVARIANT_DEFENSIVE buckets; 104 SILENT_SKIP sites converted toraise CodegenSkipin this PR (55 incalls_arrays.pyvia a shared_array_elem_triad_or_skiphelper, 24 indata.py, 11 incalls_containers.py, 9 incalls_handlers.py, 4 incontext.py, 1 incalls.py). Pre-conversion these all silently dropped to a generic enclosing-function-level[E602]; post-conversion they each emit a source-located diagnostic pointing at the specific unsupported expression. The remaining 39 INVARIANT_DEFENSIVE sites and the 154 PROPAGATE sites that may now be unreachable are tracked in #657. -
New
_error()API on the codegen builder (vera/codegen/core.py) parallel to the existing_warning()method, hardcodingseverity="error". Used by the[E699]catch handlers in_compile_fn(vera/codegen/functions.py) and_compile_lifted_closure(vera/codegen/closures.py) — both updated in this PR to routeCodegenInvariantErrorthrough_error()rather than_warning()so internal-compiler-error diagnostics propagate to a non-zero CLI exit code rather than a swallowable warning. Only those two internal handlers changed; no user-facing API surface or other_warning()call sites were modified. -
Layer 1 of #626 — new
scripts/check_e602_clean.pypre-commit + CI gate that fails when any compile of an example or conformance program emits[E602](body unsupported) or[E604](param unsupported) outside an explicit allowlist. The[E602]warning channel was the project's only signal for silent translator-skip failures, and several long-standing instances of it were buried in every WASM compile — making it impossible to spot a new genuine skip without manually sifting through expected noise. The gate makes a new silent skip a hard build failure unless explicitly allowlisted with a tracking-issue reference. -
Allowlist of 11 currently-expected silent skips, each tagged with a tracking issue: 5 prelude combinators (
option_unwrap_or/result_unwrap_or/option_map/option_and_then/result_map— #604), 6 user-code cases surfaced by the new gate's first run (5 generic-decl spurious warnings + 1 real codegen gap — #655).
Documentation
Small docs sweep — closes six aging documentation issues in one PR. No code changes; touches spec/02-types.md, spec/03-slot-references.md, spec/06-contracts.md, SKILL.md, README.md, and HISTORY.md.
-
#557 —
spec/03-slot-references.mdExample 9 said the match-arm pattern binding "shadows the function parameter" without defining "shadow". Two readings were equally consistent with the prose (replace vs push-on-top); the compiler implements push-on-top. Replaced the one-liner with an explicit paragraph spelling out push-on-binding semantics, the resulting De Bruijn ordering for multi-field constructors (leftmost = deepest = highest index, rightmost = shallowest), and the non-commutative-operations caveat that exposes the rule. -
#561 — two tier-accuracy bugs in
spec/06-contracts.md. (1) §6.3.1 (Tier 1) was missing pure-fn calls inensures/@T.result/if/then/else, which actually verify at Tier 1 today; §6.3.2 (Tier 2 NYI) incorrectly listed them. Moved all three from §6.3.2 to §6.3.1. (2) §6.3.3 said "Bounded quantification is decidable for finite bounds and is handled by Z3 via finite unrolling" — that reads as Tier 1, but Tier 2 (the tier that would handle quantifier unrolling) is #427 NYI. Clarified that everyforall/existsin a contract falls to Tier 3 today, both forforalland the symmetricexiststext. -
#560 — the
invariant(...)clause ondatadeclarations is documented inspec/02-types.md§2.4.1,spec/06-contracts.md§6.2.3, andSKILL.md, but every documented form fails with[E130] no <DataName> bindings in scopeat v0.0.144. Added inline NYI markers at all three sites pointing at #560, with the working alternative (refinement types). Added the limitation tospec/06-contracts.md's §6.9 Limitations table. -
#607 — added a new
spec/02-types.md§2.2.1 "IntandNatcompatibility" subsection covering the bidirectional subtyping (Nat <: Intalways;Int <: Natpermitted with verifier-discharged obligation). Practical-implication note tells agents not to insertnat_to_intdefensively when callingarray_lengthetc. into@Natpositions. Cross-reference added toSKILL.md's "Primitive types" listing. -
#608 — added
SKILL.md"IO model: terminal vs browser" subsection (under §Browser compilation) explaining that programs usingIO.sleep+ ANSI escapes for terminal pacing/rendering compile cleanly to--target browserbut render escapes as literal text and busy-wait the main thread. The recommended browser pattern is "Vera pure simulation core + JS driver viarequestAnimationFrame". Two runtime gaps that would make the recommended pattern more ergonomic are tracked separately (#609 JSPI sleep, #610 ANSI subset interpreter).README.md's "write once, run anywhere" line qualified to acknowledge the IO seam. -
#512 — trimmed all 31 Stage 11 rows in
HISTORY.md(v0.0.112 → v0.0.138) to match the early-stage one-sentence format established in Stage 1–8. Per the canonical template now in long-term memory:**X** ([#N]).per row, no em-dash separator, no secondary clauses, no implementation detail. Detailed mechanism descriptions for each version stay in CHANGELOG under their respective## [0.0.X]section.
Changed
- mypy 1.20.2 → 2.0.0 (
pyproject.toml,uv.lock). Mypy 2.0 enables three flags by default that were opt-in under 1.x:--local-partial-types(changes inference of types based on assignments in other scopes),--strict-bytes(per PEP 688:bytearrayandmemoryviewno longer assignable tobytes), and--allow-redefinitionbehaves like 1.x's--allow-redefinition-new(more flexible variable redefinition across blocks). Running mypy 2.0 againstvera/produced zero errors with the existing source — no compiler-source changes needed to clear the upgrade. Manual upgrade in favour of dependabot PR #647 (closed in favour of this change).