Skip to content

Pattern-matching test coverage (matching.ml / parmatch.ml)#4

Draft
JonoPrest wants to merge 8 commits into
masterfrom
jono/pattern-match-coverage
Draft

Pattern-matching test coverage (matching.ml / parmatch.ml)#4
JonoPrest wants to merge 8 commits into
masterfrom
jono/pattern-match-coverage

Conversation

@JonoPrest

Copy link
Copy Markdown
Owner

Goal

Lift test coverage of the two most central, correctness-critical pattern-matching files in the compiler, using portable .res fixtures so alternative bsc implementations can run the same suite.

Baseline (fresh make coverage off master):

  • compiler/ml/matching.ml72.6% (pattern → lambda compilation)
  • compiler/ml/parmatch.ml75.6% (exhaustiveness / redundancy analysis)

Approach

Split by what's portable:

  • matching.ml → runtime-behavior fixtures in tests/tests/src/*_test.res (Mocha eq(__LOC__, …)), fully implementation-agnostic. Compiles under -warn-error A, so all matches are exhaustive.
  • parmatch.ml → warning snapshots in tests/build_tests/super_errors/ (these also emit JS, so they exercise matching.ml partial-match paths too).

Targets were derived from line-level coverage + reading the source, excluding genuinely unreachable branches (e.g. expand_stringswitch — the JS backend emits Lstringswitch instead; Const_int32/64 assert false; when false guards; if dbg debug code). Those will be documented as unreachable rather than chased.

Planned fixture matrix

matching.ml behavior (tests/tests/src/)

  • pattern_match_constants_testcombine_constant: int/char (call_switcher), float/bigint (make_test_sequence)
  • pattern_match_intswitch_test — dense vs sparse int intervals (edges/holes)
  • pattern_match_constructors_test — variant arity 0/1/N, unboxed, extension constructors
  • pattern_match_polyvariant_test — const-tag + block-tag polymorphic variants
  • pattern_match_record_tuple_test — immutable/mutable/optional-field/inlined records, tuples
  • pattern_match_array_test — match by length
  • pattern_match_orpat_test — nested or-patterns, or-pats binding vars / under constructors

parmatch.ml analysis (super_errors/)

  • non-exhaustive witnesses per category (w8)
  • redundant / partially-dead or-pattern arms (w11/w12)
  • overlapping or-patterns forcing lub (w11)
  • ambiguous guard bindings across or-branches (w57)
  • cross-module variant closing / GADT exhaustiveness (super_errors_multi/)

Draft — pushing fixtures incrementally. Coverage lift will be re-measured at the end.

🤖 Generated with Claude Code

JonoPrest and others added 8 commits June 2, 2026 15:00
First batch of fixtures targeting uncovered branches in
compiler/ml/matching.ml (pattern compilation to lambda).

Covers `combine_constant` across every constant kind, each routed to a
distinct compilation strategy:
  - Const_int    -> call_switcher (interval edges, holes, negatives)
  - Const_char   -> call_switcher
  - Const_float  -> make_test_sequence with Pfloatcomp
  - Const_bigint -> make_test_sequence with Pbigintcomp

Runtime-asserted via Mocha so the suite stays portable across alternative
bsc implementations.

Signed-off-by: Jono Prest <jono@envio.dev>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ts, records, arrays, or-patterns)

Second batch of portable runtime-behavior fixtures targeting uncovered
branches in compiler/ml/matching.ml:

  - constructors: exhaustive variant Lswitch, extension/exception
    constructors (Pextension_slot_eq), unboxed single constructor
  - polyvariant: const + block tags (Pis_poly_var_block split, both
    variant switchers)
  - record/tuple: field projection, mutable fields, `if` guards
  - array: dispatch by length
  - or-patterns: constant-constructor or, shared-variable or-binding,
    or-patterns inside tuple columns (explode_or_pat / precompile_or)

All matches exhaustive (warning-clean under -warn-error A); 942 mocha
tests pass.

Signed-off-by: Jono Prest <jono@envio.dev>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Apply `make format` to pattern_match_array_test.res and commit the
generated .mjs for all pattern-match fixtures, matching the repo
convention of checking in tests/tests compiled output.

Signed-off-by: Jono Prest <jono@envio.dev>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
float/bigint/char matches now have >= 4 cases, so make_test_sequence
exercises split_sequence/cut (the binary-search comparison tree in
matching.ml) rather than only the flat comparison chain.

Signed-off-by: Jono Prest <jono@envio.dev>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ines)

Add tests/PATTERN_MATCHING_COVERAGE.md cataloguing, for matching.ml and
parmatch.ml, which uncovered lines are structurally unreachable (dead
OCaml-bytecode string-search tree, int32/64 asserts, `when false`
guards, `if dbg` debug printers, defensive fatal_error/assert) versus
reachable-but-cold edges worth fixtures (witness pretty-printers,
All_clauses_guarded, lub, dichotomic comparison split, partial-match
failaction paths).

Companion to ERROR_VARIANTS.md.

Signed-off-by: Jono Prest <jono@envio.dev>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Target parmatch.ml analysis paths and matching.ml partial-match
compilation via non-exhaustive / redundant matches:

  - non_exhaustive_{tuple,list,record,variant,polyvariant,array}: drive
    the per-shape witness pretty-printers (pretty_val) that render the
    "you forgot to handle" counter-example
  - non_exhaustive_{int,char,string,float,bigint}: drive pretty_const
    for each scalar witness
  - all_clauses_guarded: Warnings.All_clauses_guarded
  - redundant_or_branch: Upartial (partially-redundant or-pattern)
  - overlapping_or_lub: least-upper-bound of overlapping or-branches

Snapshots generated via `node input.js update`; verify run passes.

Signed-off-by: Jono Prest <jono@envio.dev>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Cross-reference PATTERN_MATCHING_COVERAGE.md and the new non_exhaustive_*
/ redundancy fixtures from the Warnings section of ERROR_VARIANTS.md;
apply `make format` to the constants fixture comment.

Signed-off-by: Jono Prest <jono@envio.dev>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ess printer

Direct per-fixture BISECT_FILE measurement showed parmatch.ml's
pretty_val/pretty_const family (~364-434) stays cold even when a
non-exhaustive witness prints: the user-facing witness is rendered by
Pattern_printer.print_pattern (compiler/common, already ~88% covered),
not by these debug printers. Move them to the dead-code table.

Also record the measured contribution of this PR's fixtures (~10 lines in
matching.ml from the dichotomic split, ~5 in parmatch.ml from
All_clauses_guarded/Upartial) and note the matching.ml partial-match
paths remain cold (simple non-exhaustive matches don't reach them).

Signed-off-by: Jono Prest <jono@envio.dev>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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