ReviewOS

home-lang/home

feat(types): enforce `match` exhaustiveness in the checker

#781
Closed chris opened this 8 days ago ·
8 days ago

Problem

A PatternChecker is allocated and freed by the type checker but never invoked. Codegen's strict-exhaustiveness switch defaults to off and nothing enables it, so an unhandled enum variant silently evaluates to 0 at runtime. The Home-only diagnostic HM2000 exists for exactly this and is never emitted.

Evidence (origin/main @ d4b219507)

Scope

  • Call the pattern checker for every match and emit HM2000 for non-exhaustive matches.
  • Require all arms to agree on a result type (join/LUB).
  • Remove the codegen fallback that yields 0 for an unmatched value.

Acceptance criteria

  • A non-exhaustive match on an enum / bool / Result is a checker error.
  • Arms with incompatible result types are an error.
  • Negative tests plus a runtime test that no unmatched path exists.

Depends on: #778 Part of #814.

8 days ago

Pushed the first verified checker batch to main in 199dc188c (feat(types): enforce match exhaustiveness (#781)).

This batch:

  • invokes exhaustiveness checking for both statement-form and expression-form matches;
  • treats guarded arms as non-covering because a guard can reject after the pattern matches;
  • covers boolean, enum, and Result constructors;
  • records the existing stable HM2000 code on non-exhaustive Home checker diagnostics;
  • keeps incompatible match-expression arm result types rejected.

Guarded verification under the fixed 3,840 MB ceiling:

  • exact types_assert: 30/30 passed;
  • exact types_control_flow: 44/44 passed;
  • final guarded run peak tree footprint: 341 MB;
  • git diff --check and touched-file zig fmt --check passed.

The codegen-side acceptance work remains open: expression-form match fall-through still needs to be made impossible, followed by an exact native codegen regression. I am keeping the issue open for that next batch.

8 days ago

Pushed the second verified #781 batch to main in 571df3d04 (fix(codegen): trap unmatched match expressions (#781)).

This batch:

  • compiles boolean literal patterns in expression-form matches;
  • emits a non-returning exit-101 panic path whenever every expression arm (or its guard) rejects the scrutinee, replacing the old implicit rax = 0 result;
  • makes codegen own one canonical copy of every registered string literal and frees those copies, fixing the three leaks exposed by the new panic path;
  • adds a permanent codegen emission regression for the unmatched path.

Guarded verification under the fixed 3,840 MB ceiling:

  • exact codegen: 13/13 passed with zero leaks;
  • peak tree footprint: 308 MB;
  • git diff --check and touched-file zig fmt --check passed.

The checker and codegen portions are now implemented. I am leaving #781 open until an executable runtime regression exercises the emitted unmatched path end-to-end, as required by the final acceptance checkbox.

8 days ago

Completed and pushed the final acceptance gate in 0636982bd (test(codegen): execute match fallthrough traps (#781)).

The permanent end-to-end regression now writes a native executable containing an unmatched boolean match expression, runs it with a five-second bound, and proves that it exits 101 and prints the stable non-exhaustive-match panic instead of returning the old implicit zero. The panic contract is single-sourced through the codegen package.

Final guarded verification: exact codegen 14/14 passed with zero leaks, peak tree footprint 368 MB; formatting and diff checks passed. Together with 199dc188c and 571df3d04, all issue acceptance points are now covered for bool/enum/Result checking, incompatible arm types, HM2000, and runtime fall-through.

Sign in to comment on this issue.