Status: Superseded by ADR-293 — its D2-D8 are D5-D11 there; D1 (ADR-114 D1-D4 built as written) is D1-D4 there, its NK1162 in D2
Date: September 20, 2026
Target Version: Nikaia 0.0.78
Related: ADR-114 (the record this builds, D1–D4),
ADR-080 D2 (the write, and the question it left open),
ADR-008 D5 (a copy is never the compiler’s),
ADR-011 D2 (this emitter does not know types),
ADR-044 D2 (an abort reports the Nikaia line), Part I 1.3,
Part I 2.3, Part I 4.5, Part III A.2, Part III C.1
ADR-114 decided that m[k] on a map answers a T? — a key is
data and may be absent, so there is nothing there is an answer rather than the
end of the program. Its §5 said nothing of it was built, and named the order
of work. This is that work, and a record of its own because three things it met
are decisions the earlier one did not make.
Two lines in the corpus were the evidence: &totals.stations[name] in
examples/1brc.nika and &report.paths[path] in examples/access-log/src/main.nika,
both safe only because the key came from the same map a line earlier.
m[k] on a map is a T? and on a sequence a T (D1, D3); the write is
unchanged and a compound one is refused with the written-out form (D2,
NK1162); and the read is one call, nikaia_std::index::get, with an output
type per container, so this emitter writes the same three tokens for both and
the language below picks (D4).
?? joins a view with a value, and the language below decides which.ADR-114 D4 says the value reached is a copy where V copies and
a view of the map otherwise. So a map read is an Option<&V> — copying what
the map holds is never the compiler’s to do
(ADR-008 D5) — while the fallback is written as the value it
stands for: m[k] ?? 0. The two sides do not have the same type.
nikaia_std::index::or is where they meet, as two impls that do not
overlap: one over Option<T> and one over Option<&T> where T copies. A
number is copied out, a struct stays a view, and the emitter chooses nothing —
which is ADR-011 D2’s rule kept on the one side of the read that
could have broken it.
Where the value does not copy, the fallback has to be a view too, or the
program writes .to_owned(). A Clone there instead of a Copy would put an
allocation nobody wrote into m[k] ?? "none", which is exactly what
ADR-008 D5 forbids.
panic(message) exists, because it is D1’s own way out.ADR-114 D1 writes report.paths[path] ?? panic(f"{path} was a key
a moment ago") — the abort it used to get for free is the one it now writes.
panic is on Part I 1.3’s list and in Part III A.2’s own list of unrecoverable
errors, and it did not exist: the form lowered to a call to a function
nothing declares, and rustc said so about the generated file
(Part III C.1).
It hands back never, so it stands where a value is wanted, and it reports
the Nikaia line through the hook ADR-044 D2 installs. It is not
the testing chapter’s assert, and does not wait on it.
T? is a nullable view.&str? is Part I 2.3’s own spelling and is_view and is_nullable are
independent flags on a written type for that reason. The checker answered
unknown for it, which claimed nothing — so let s = &m[k] followed by
s.min reached no NK1125 and was refused by rustc, about the generated
file. That was invisible until a map read became a T?, and it was the shape
both evidence lines were written in.
m[k] = (m[k] ?? 0) + 1 is the form D2 of ADR-114 hands a reader
in its own message. Once the read is index::get(&m, …), that line has a &m
inside the arguments of a set(&mut m, …) — cannot borrow as immutable
because it is also borrowed as mutable, about a file nobody wrote. A let
ends the read before the write begins, which is what a reader would write by
hand.
(*get(…)), and a map’s answer derefs to the option.ADR-114 D4 says a T for a sequence, an Option<&V> for a map,
and one trait cannot answer both unless the caller writes the same thing for
each. So the caller writes a *: for a sequence that is the element in its
place — which is what return xs[at] on a -> i64 needs, and what keeps
self.bodies[i].vx a field of a Body — and for a map it takes apart a small
wrapper whose Deref hands back the option.
That is the one place D4’s mechanism had to be made precise rather than
followed. A T for a sequence cannot be a value in the language below: a
sequence read is a place, because reading one out by value would move out of
the container where the element does not copy. The * is what a place is
spelled with.
a ?? b on a T? is a T, and the checker says so.It claimed nothing before — and nothing was enough while nothing common
produced a T?. The day a map read became one, let s = m[k] ?? panic(…) made
s unknown, and s.mean() became one more method call nobody could answer:
ADR-028’s arrangement losing a receiver it used to have. The
ceiling in crates/nikaia/tests/sequences.rs is what said so.
Only that shape. A ?? over something this checker could not type still claims
nothing, which is Part III C.4.
io::Lines’ step throws io::IoError.ADR-158 D1 gave every function in std a name for what it
throws and missed the one iterator: a step of io::lines yielded the
language below’s own error. Nothing saw it while every program that read a line
also had a "?" in its set and travelled in the opaque channel. The day one of
them had a named channel (ADR-159 D1), the ? the loop’s step
takes had a raw error on the left and a named one on the right.
An absent key is a value. counts["b"] ?? -1 answers -1 where the
program used to stop.
The two evidence lines say what they know. Both now read
… ?? panic(f"… was a key a moment ago"), which is
ADR-114 D1’s own example: the key came from the map, and the line
says so rather than relying on it silently.
Five rustc messages about generated files went away (D3, D4, D5, D6, D8),
and four of them were only reachable because of the change — which is the
shape a record that builds an older one tends to have. Each was found by a test
that runs the program or by a ceiling that counts something: reading the
emitted Rust would have found none of them.
assert, which stays with the testing chapter (Part III 14)..to_owned(). Nothing in the tree writes it,
so nothing was measured.get as a method. ADR-114 D1 says get says the same,
and std has no get on a map yet: the brackets are the whole surface
today.dna[i..<i + k] on text is a slice and not this
rule’s; nothing changed for it.nikaia_std::index::Get with an impl per container; the checker
typing a map read Ty::Nullable and a sequence read its element;
NK1162 for a compound write to a map slot.nikaia_std::index::Or and or, and the ?? lowering through it.nikaia_std::abort::panic, in the prelude; the ?? lowering treats
a call to it as one that never comes back.check::view_of answers a nullable view for a nullable.nikaia_std::index::Found and the * the emitter writes.Expr::Coalesce in the checker.io::Lines’ Item.crates/nikaia/tests/map_reads.rs, six of which compile and
run the program; and the two corpus lines.