while true Is the Unconditional LoopStatus: Superseded by ADR-276 — its D1-D3 are D1, D2 and D6 there; D4 is not carried (a deferral, answered by ADR-071)
Date: September 14, 2026
Target Version: Nikaia 0.0.8
Supersedes: — nothing. loop has been on the roadmap since the roadmap was
written and was never in the specification, so this closes a question rather than
reversing an answer
Related: ADR-051 (every keyword is a reserved word, and the free
moment to reserve one is before programs exist), ADR-023 D8 and
ADR-025 D1 (what a loop does when a step fails), Part I 3.3
Part I 3.3 has while and for and no third form. loop { … } has been on
project_status_and_roadmap.md since it was written, is not in the
specification, and the compiler having no rule for it is therefore correct rather
than a gap. What a program writes today is while true { … }.
The evidence that decided it, stated accurately. Go is the language usually
named here, and what it shows is one step to the side of what it is quoted for.
Go has no while at all: for { … } is Go’s unconditional loop, for cond
{ … } its conditional one, for … range its iterating one. So Go did not do
without an unconditional loop — it did without a second keyword for it, by
letting one word carry all three forms.
That is the lesson, and it is the lesson this record applies. Nikaia already has
the unconditional loop; it is spelled while true. The question was only whether
it deserves a word of its own, and Go’s answer is no.
while true { … } is the unconditional loop, and there is no second spelling for it.One fewer keyword, and a reader has the whole of control flow on one page. A keyword is the most expensive thing a language adds and the hardest thing it removes — ADR-051 made every one of them a reserved word, which is what makes the addition cost more than the construct.
loop keyword does not apply here, because there is no break.Rust’s loop earns its word twice over: break value makes it an expression,
and the checker knows loop { } with no break diverges, so it has type !
and nothing after it needs to exist.
Neither half reaches Nikaia today. break and continue are not in the grammar,
not in the parser, and not among the 29 reserved words — a loop is left by its
condition going false or by return leaving the whole function. So the
expression argument is empty: there is no value for a break to carry.
The day that changes is the day to reopen this, and the reason is worth
stating now rather than rediscovering: while true { … } whose break hands back
a value reads as a lie, because the head says the loop does not end and the body
says what it ends with.
Run rather than argued. This is refused today:
fn forever() -> i32 {
while true {
let x = 1
}
}
error[NK1104]: this function hands back `()`, and it declares `i32`
so a function that genuinely never returns — an accept loop, an event loop, a
supervisor — has to end with a return that cannot be reached. That is the one
real cost of D1, and it does not need a keyword to fix: a while whose
condition is the literal true cannot be left except by return, precisely
because D2’s break does not exist, so everything after it is unreachable and the
checker may say so. It is on #183 as work rather than here as a
question.
loop is a separate call, and this record does not make it.ADR-051’s rule — reserving a word costs nothing before programs
exist and breaks them afterwards, so the free moment is now — applies to loop,
break and continue alike, and applies more strongly to the two that D2 says
are likely to be wanted. Reserving is not adding: it keeps a door open at the
price of a name nobody may use. That is a decision about the reserved list and
not about control flow, and putting it here would smuggle a language change into
a record that says no to one.
Part I 3.3 says the absence is a decision. That is the whole cost of D1 and the point of having asked: an absence a reader can see the reason for is not an omission.
No program changes. No .nika file in the tree writes while true, so the
form nobody uses did not need a second spelling — which is also why this was
cheap to decide and would not have been cheap to decide later.
One item joins the work list (D3), and one question is named but not answered (D4).
The specification. Part I 3.3 carries the sentence and names this record.
Nothing in the compiler, which already had no rule for loop and needed none
added. D3’s unreachability is on #183; it is an improvement to a
diagnostic and not a condition of this decision.