throw, return, break and continue Are Expressions of the Never TypeStatus: 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
=> 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.
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.
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.
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.
=> throw NotFound is what the specification’s own error chapter writes,
and the braces that are there to satisfy the grammar come off.?? may end in a throw, which is the shape a value or a failure is
written in everywhere.break carrying a value. Rust’s break x hands a value out of a
loop; this language has no unconditional loop (ADR-070) and
no value for one to carry, and NK1133 refuses the shape.never a program can write in a declaration. ADR-093
leaves that open and this does not reopen it.All three steps.
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.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.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.