Next Session — Maranget Pattern Exhaustiveness Checker
Baseline: ef169d31 (trunk, clean, fixpoint 1991 functions)
Plan file: .claude/plans/abstract-herding-gizmo.md
Estimated effort: ~5 hours (quartz-time)
Handoff Prompt (copy-paste to start next session)
Read the implementation plan at `.claude/plans/abstract-herding-gizmo.md` and execute it. This is a 7-phase implementation of a Maranget-based pattern match exhaustiveness checker for the Quartz compiler.
Summary: Replace the flat ad-hoc `tc_check_exhaustiveness` in `self-hosted/middle/typecheck_match.qz:364-654` with a proper pattern matrix algorithm based on Maranget (2007) "Warnings for pattern matching" — the same algorithm used by Rust, OCaml, and every production compiler. The new checker handles nested patterns, generates witness patterns for error messages ("missing: `Some(Err(_))`"), detects unreachable arms, and treats guards conservatively.
The plan has 7 phases:
0. Data structures (DeconstructedPat, PatStack, PatMatrix, constructor tags)
1. Constructor helpers (equality, arity, type enumeration)
2. AST → DeconstructedPat conversion (all NODE_* pattern types)
3. Core algorithm (specialize, default matrix, usefulness)
4. Witness generation (human-readable missing pattern display)
5. Integration (replace tc_check_exhaustiveness, retain legacy as fallback)
6. Tests + cleanup (17+ new tests, remove legacy)
Key constraints:
- All new code in `self-hosted/middle/typecheck_match.qz`
- Call site at `typecheck_expr_handlers.qz:1036` unchanged
- Error code QZ0107 for non-exhaustive (existing), QZ0108 for unreachable arms (new warning)
- `quake guard` mandatory after Phase 5 (compiler source changes)
- Existing `match_exhaustiveness_spec.qz` tests must not regress
- The compiler must compile itself at every phase
Prime Directives:
1. World-class only — Maranget algorithm, not ad-hoc
2. Design before building — plan is approved, execute it
3. No holes left behind — nested patterns, guards, redundancy all addressed
4. Report reality — guards are undecidable, treated conservatively
5. Binary discipline — quake guard before every commit touching self-hosted/*.qz
6. Delete freely — the old tc_check_exhaustiveness dies in Phase 6
Tree is clean at ef169d31, guard stamp valid, smoke green. Execute the plan phase by phase with commits after each phase.
Context for the implementing session
What exists now (typecheck_match.qz:364-654)
- Flat variant counting: builds
covered[]array, marks variants as covered, reports first uncovered - Special cases for Bool (true/false), Result (Ok/Err), Union types
- Wildcard early-exit: any
_pattern → skip all checks - ~290 lines, handles only top-level patterns
- Cannot do: nested patterns, guard interaction, redundancy, witness generation
What we’re building
A pattern matrix algorithm with:
DeconstructedPat: pattern = constructor + sub-patterns (recursive)PatMatrix: rows of patterns, one per match armmx_specialize(ctor, matrix): filter rows compatible with constructormx_default(matrix): keep wildcard rows, drop column 0mx_is_useful(matrix, query): core recursive function- Witness generation: build example patterns of what’s missing
- Redundancy: check each arm against prior arms
Research sources
- Maranget 2007 paper (JFP): http://moscova.inria.fr/~maranget/papers/warn/warn.pdf
- Rust
rustc_pattern_analysiscrate docs - GHC “Lower Your Guards” (ICFP 2020) — rejected as overkill (no GADTs)
- Swift Space Engine — rejected as less battle-tested
Key APIs already available
tc_lookup_enum(tc, name)→ Found(idx) or NotFoundtc_enum_variant_names(tc, idx)→ Vectc_enum_variant_payload_count(tc, enum_name, variant_name)→ Inttc_error(tc, msg, line, col)/tc_warning(tc, msg, line, col)assert_compile_error(source, expected)/assert_compile_warning(source, expected)in qspec