ADR-138: throw, return, break and continue Are Expressions of the Never Type

Status: Superseded by ADR-276 — its D1-D3 are D20-D22 there Date: September 19, 2026 Target Version: Nikaia 0.0.18 Related: ADR-093 (a function that never returns, and the never type), ADR-084 D3 (a statement after a break), ADR-025 D2 (a failure leaves without a sigil), Part I 3.1, 3.4 and 7.1

1. Context

=> throw NotFound is a parse error. So is ?? throw Missing and an else branch that is one return. Each has to be written with braces — => { throw NotFound } — and the braces say nothing: there is no second statement they are holding together and no value they are producing.

The four words are statements in the grammar. ADR-093 already gives the language the type they would have as expressions — the never type, which a function that never returns has — so the type side of the question is answered and what is left is the grammar and one ruling.

2. Decisions

D1 — The four are expressions, and their type is never.

let user = find(id) ?? throw NotFound(id)

let grade = match score {
    90..100 => "A",
    else => throw OutOfRange(score),
}

fn first(xs: Vec[i64]) -> i64 {
    for x in xs { return x }
    throw Empty
}

A throw, a return, a break and a continue may stand wherever an expression may. Their type is never, so they fit every expected type without widening anything: an arm that throws sits beside an arm that hands back a &str and the match is a &str, because the arm that throws hands back nothing at all.

D2 — Nothing about what they do changes.

A throw still leaves the function and still makes it throws (ADR-025 D2); a return still returns; a break and a continue still need a loop and still may not cross a function boundary (ADR-084 D4). The statement forms keep working, because a statement whose expression is one of these is exactly what they were.

D3 — NK1133 is unchanged and is the reason this is safe.

A statement after a break or a continue in the same block is already refused (ADR-084 D3), and that rule is what keeps D1 from turning break x into a quiet dropped value: the shape it was written for is still refused, one position over.

3. Consequences

4. What this does not decide

5. What is built

All three steps.

  1. The four as expression alternatives, last in primary_expr and keyword-led, so no earlier alternative can take one and none of them costs a failed match in front of every primary. Expr::Return, Expr::Break and Expr::Continue join the Expr::Throw that was already there.
  2. Never, as a question about the expression rather than as a Ty. What never has to mean here is this is not one of the answers that have to agree, and the only place that matters is a match’s arms — so an arm that jumps is skipped when the arms agree on a type, and the match is the other arm’s. The language below reads the jumping arm as the ! it is, so the two halves agree by construction rather than by a coercion this had to write.
  3. The pages and examples/json/src/main.nika without the braces, and eight tests.

Two things the record did not see, and both are the same rule from ADR-084 D4: a jump does not cross a function boundary.

A ?? whose fallback jumps lowers to a match. unwrap_or_else takes a closure, so let user = find(id) ?? throw NotFound(id) — this record’s own first example — would have thrown from the closure while the program carried on, and bound a value nobody produced. That is a different program, not a refusal, which is the worst kind of defect this compiler can emit.

And expr_stmt normalises a bare jump back to its statement. break_stmt and continue_stmt are last in stmt, measured and argued in ADR-084 D8, so once D1 put the two words in the expression grammar expr_stmt reached them first: a bare break became Stmt::Expr(Expr::Break), every analysis that asks about a jump asks about the statement, and NK1133 — D3’s whole safety net — stopped firing. The action maps the three back, which costs nothing to parse and hands the rest of the compiler the tree it had.

#262 carried it.

This site is open source. Improve this page.