Skip to content

fix(checker,codegen): reject generic-at-Unit (E206) + support Unit tuple components (#900, #902)#903

Merged
aallan merged 1 commit into
release/v0.1.0from
fix/900-generic-over-unit
Jul 3, 2026
Merged

fix(checker,codegen): reject generic-at-Unit (E206) + support Unit tuple components (#900, #902)#903
aallan merged 1 commit into
release/v0.1.0from
fix/900-generic-over-unit

Conversation

@aallan

@aallan aallan commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Closes #900
Closes #902

Targets release/v0.1.0. Two Unit-as-degenerate-component bugs, different (cited) resolutions.

#900 — generic instantiated at Unit → clean checker rejection (E206)

A forall<T> at T=Unit was check-green but crashed at codegen (E699 / dropped main / invariant-violation, hash-seed-dependent). Decision: checker rejection — DESIGN.md principle 1 (checkability over correctness) + spec §11.2.2/§11.3.1 (Unit is 0 bytes; a monomorphized @T.n slot resolves to no runtime local). New E206 at the single inference choke point (_check_fn_call_with_info, vera/checker/calls.py), scoped to USER forall<T> fns — built-in generics (async/await/combinators) excluded so async(IO.print(...)) keeps its Future<Unit> path (a regression this caught and fixed). Boxed Option<Unit> stays a valid type argument. Entry paths: @Unit-returning fn as generic arg, bare () literal, Unit binding one of several params.

#902 — Tuple with a Unit component → codegen support

fn mkt(@Int -> @Tuple<Unit, Int>) was check-green but the function was silently skipped at codegen (unknown func $mkt). Decision: codegen support — spec §2.3.1 (Tuple<>Unit; no Unit exclusion) + §11.2.2 (Unit is systematically zero-size). Localized fix in _translate_constructor_call (vera/wasm/data.py): a Unit tuple field is laid out genuinely zero-size (arg instructions still run for side effects, occupies no bytes), mirroring the existing _translate_let_destruct Unit skip; distinguishes a real Unit field from a genuine inference failure (still CodegenSkip). Entry paths: Unit in any position, multiple/all-Unit, nested tuples, ADT-with-Unit-field, Unit+String (i32_pair), Int-Unit-Int (offset verified 22-11=11).

Evidence

RED-first both (4 E206 + 5 codegen tests fail pre-fix across seeds); mutation-killed both independently; boxed Option<Unit> + 10 pre-existing tuple tests stay green. Full suite 6283 passed, 0 failed; conformance 127 (+ch02_generic_over_unit_rejected [E206 negative], +ch02_tuple_unit_component [run positive]). KNOWN_ISSUES → No known bugs. Adversarial review by orchestrator (CodeRabbit at rate limit).

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 14bf0aa9-b9d4-4e02-bf0d-3d40bedc6a52

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/900-generic-over-unit

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

#900, #902

Two pre-existing check-green-then-codegen-broken bugs in the same
Unit-as-a-degenerate-component family (both on release/v0.1.0 and main).

#900 — a USER forall<T> function whose body READS @t, inferred to
T = Unit, passed vera check but crashed in codegen: Unit is 0 bytes with
no WASM representation (spec 11.2.2 / 11.3.1), so the monomorphized body's
@T.n slot read lowers to a local.get with no local — the dangling-slot
invariant, whose symptom was hash-seed-dependent (E699, a dropped main,
or a CodegenInvariantError "file a bug report" crash). The checker's
type-argument inference now rejects a body-read forall parameter inferred
to bare Unit with the new E206, per DESIGN.md principle 1 (checkability
over correctness).

The check is NARROWED to bodies that materialize @t (a @T.n SlotRef read,
computed once at registration): a @t parameter the body never reads erases
cleanly from the ABI, so firstInt(@t, @int){ @Int.0 } and ignore(@t){ 0 }
still run at T = Unit and are NOT rejected. Scoped to user-declared
functions the monomorphizer clones — built-in generics
(async/await/collection + prelude combinators) have hand-written codegen
and are excluded, so async(IO.print(...)) keeps its Future<Unit> W002
path. Keyed to bare Unit only: a boxed Option<Unit> stays valid. Covers
every @T-reading arg-pinned entry path (Unit-returning fn, () literal, one
of several params, reads nested in let/if/match).

#902 — a Tuple (or any constructor) with a Unit component (Tuple((), 3)
returning Tuple<Unit, Int> passed vera check but crashed in codegen with
unknown func: failed to find name : _translate_constructor_call
could not infer a WASM type for the Unit field, raised a CodegenSkip, and
dropped the whole function while its caller still called it. A Unit field
is now laid out as a genuine zero-size field (its arg instructions run for
side effects, but it occupies no bytes and stores nothing), mirroring the
existing Unit skip in _translate_let_destruct. Unit detection uses
_is_void_expr (the canonical produces no stack value check), so a
Unit-RETURNING call in field position — IO.print(x), a user @Unit fn, a
void effect op, a ModuleCall to a @Unit fn — is also laid out zero-size
with its side effect preserved, not just a bare () literal. Covers Unit in
any tuple position, multiple Unit fields, nested tuples, a user ADT with a
Unit field, and a side-effecting Unit-returning field.

Adds a check-level negative conformance fixture
(ch02_generic_over_unit_rejected, E206) and a run-level positive one
(ch02_tuple_unit_component), reconciles doc counts (127 conformance
programs), and restores KNOWN_ISSUES to No known bugs.

Co-Authored-By: Claude <[email protected]>
EOF
)
@aallan aallan force-pushed the fix/900-generic-over-unit branch from 6e74003 to 573d7a1 Compare July 3, 2026 17:52
@aallan aallan merged commit 505e113 into release/v0.1.0 Jul 3, 2026
1 check passed
@aallan aallan deleted the fix/900-generic-over-unit branch July 3, 2026 18:11
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