ADR-146: A match Covers Every Case, and the Refusal Is This Compiler’s

Status: Superseded by ADR-291 — its D1-D3 are D19-D21 there Date: September 19, 2026 Target Version: Nikaia 0.0.30 Related: ADR-145 (the catch-all arm is else), ADR-137 §4 (which left this open), ADR-023 D1 (an error type is an enum), ADR-024 D1 (the ledger, and the absence of a claim), Part I 3.4, Part III C.1 and C.4

1. Context

ADR-137 §4 left exhaustiveness open — a question about types this compiler does not yet answer. It is not open below: Rust refuses a non-exhaustive match, so a Nikaia match with a missing variant was refused by the backend, in its words, on a Nikaia line. That is Part III C.1’s class, and all this decides is whose message the reader gets.

The page already promised the rule. Part I 3.4’s own first sentence says a match ensures that every possible case is handled, and nothing in this compiler ensured it.

2. Decisions

D1 — Every case is covered, and else is how the rest is said.

match code {
    200 => "ok",
    404 => "not found",
    else => "other",          // required: the set of `i64` is not written out
}

match op {
    Op::Plus  => a + b,
    Op::Times => a * b,       // complete: the enum has two variants
}

A match over an enum is complete when every variant is named, and needs no else; over anything else it needs one, because the set is not enumerable in arms. NK1151 names what is missing: the variants for an enum, and else for everything else.

else was _ when the question was asked, and the answer arrived one record later than the spelling (ADR-145).

D2 — A bare name is a catch-all, and covers.

other => … binds and matches anything, so a match that ends in one is complete. That is not a special case bolted on: it is what the arm already means (Part I 3.4), read by the same rule else is.

D3 — Where the scrutinee’s type is not known, nothing is claimed.

Part III C.4: a refusal on a guess is a correct program refused, and it is the worse of the two mistakes. A match over a value this compiler could not type keeps the answer it has today — the backend’s — and gets a better one the day the type does.

This is the one place the rule is weaker than it reads, and it is the same weakness every other check here has for the same reason.

3. Consequences

4. What this does not decide

5. What is built

All of it, and the shape it took is smaller than the question suggested: the check is does anything here catch everything, and only where nothing does is the scrutinee’s type asked about at all.

An enum’s variants come from the same map NK1135 and the variant rule already read, so a package’s enum is covered as this file’s is the day its ledger is in hand.

The bool case is the one that is neither, and it is written out: true and false are two arms and a complete match, which no enum map knows and no else is needed for. Without it match flag { true => 1, false => 0 } — which Rust accepts — would have been refused, and that is C.4 exactly.

A char, an i64 and a String need else, and the message says so rather than listing a set nobody can write out.

Seven tests in crates/nikaia/tests/match_coverage.rs.

This site is open source. Improve this page.