Status: Superseded by ADR-285 — its D1-D3 are D23-D25, D4 is D27, D5 is D30 there
Date: September 28, 2026
Target Version: Nikaia 0.0.251
Supersedes: ADR-060 D3 (“there is no second answer for a
use to ask for, and nothing to find out”)
Narrows: ADR-063 D2 (“a bare let pins the type its own
value took” - the type its uses decide, and the first that holds its value only
where none does)
Related: ADR-048 D1 (a length and an index are i64),
ADR-248 (u64 and u32), ADR-135 D2 (an empty
list’s element type comes from its use, asked once the body is walked), Part I
2.4, #176 and #177
Part I 2.4 says it already: a number takes the type its use asks for, and the
first type that holds it where nothing asks. ADR-060 D1 kept the first half
and said how - it was the language below’s inference, “inherited and
working”: the let was written bare, and rustc looked at the uses. D3 then
argued that nothing more was needed, because “a literal that does not hold in
an i32 can only be an i64“.
Both halves of that have stopped holding.
3000000000 is a u32’s as
well as an i64’s, and let b = 3000000000 handed to a parameter taking a
u64 was refused - as NK1102, because the bare let had been made an
i64 to say NK1199 in this language’s words.let i = 0 then v[i].push(x) lowers to v[index::at(i)].push(x), and
index::at takes any integer: rustc stops with type annotations needed
(#177). A read, v[i].len(), compiled only because another path happened to
pin it.A program’s author does not see either failure as theirs, and neither is: the line says what it means, and the answer is in the uses. The uses are this compiler’s to read.
let without an annotation whose value is only numbers is an open number.A literal, a sum of literals, or an operation over other open numbers:
let i = 0, let big = 3000000000, let c = a + b over two open a and b.
An operation joins its sides: a + b, a < b and c = a + b make one number
of all of them, because one operation has one type. A shift’s count is any
integer and joins nothing.
A use asks where a type stands beside the number:
return or a function’s last expression;let, a struct field it goes into;i < xs.len() asks
for an i64;n = xs.len(), and total = n into a typed
total.An index asks for an i64, and only where nothing else asks: a length and
an index are i64 (ADR-048 D1), and the language below takes any integer
through index::at - so an index is a preference, never a conflict.
Two uses that ask for two types are refused (NK1200), naming both, with
the annotation to write. It is the program rustc refused as mismatched types
about the generated file.
Its own value and every one assigned to it: the first type that holds all of
them, i32, else i64 - ADR-060 D2, over every value rather than the first.
let mut n = 1 then n = 3000000000 is an i64.
Every value it is given has to fit the type decided, and one that does not
is NK1116 at that value, with a note that the type came from the uses. What
an operation over it comes to is held to the type too, and never decides it: a
name is still where the widening stops (ADR-063 D2), so let a = 2000000000
then let c = a + a is refused where nothing asks and an i64 where a use asks
for one.
let.let i: i64 = 0; - so the language below infers nothing this compiler decided,
and a literal the emitter would give an i64’s suffix carries the decided
type’s instead. A number no use asked for is written as before: the language
below’s default is D3’s, and a bare let stays bare.
Not only at a let, a return and an argument (ADR-043 D5): in an f"…"
hole, a condition, a list, a receiver - once, at the innermost operation that
overflows, and not again by the statement around it. An operation over an open
number is measured once that number’s type is decided (D3).
NK1200 joins Part III’s table.std sources carry their counters’ types (let mut i: i64 = 0).The first type that holds the value, full stop (Kotlin’s rule, and ADR-060
§4’s alternative). Simpler, and let a = 1 beside an i64 b would be
NK1199 - a program that compiles today, and that Part I 2.4’s own example
(wide(small)) is.
index::at::<i64>(i) where the index has no type. Mends #177 at the one
site and collides with the first use that asks for anything else.
An index as a use like any other. It would make let i = 0 handed to an
i32 and used as an index a conflict, where the language below converts either.
Built at 0.0.251.
check::OpenNumber): an unannotated let of numbers is
open (D1) and stays of no type while the body is walked; each use records
what it asks - constant_fits and expect_kept for a type beside it, the
operator arm for the other side, the assignment arm for both, the index arm
for its preference - and joins what one operation joins. When the body has
been walked, numbers_typed_by_their_uses decides each (D2, D3), refuses a
conflict as NK1200 and a value that does not fit as NK1116 with the
note, and records the type in Checked::number_lets.let i: i64 = 0; from number_lets, and a suffix
from unsigned_literals where it would otherwise write an i64’s (D4).an_operation_that_overflows at the operator arm,
where the fold has a pinned type; constant_fits stays silent for a
statement that already overflowed and for a value with an open number in it.crates/nikaia/tests/numbers_by_use.rs: v[i].push(x) over let i = 0
runs and writes let i: i64 = 1;; a u64 parameter, a u32 annotation and
a loop counter compared with a length each type their number; a value
assigned later decides where no use asks; a + a is refused where nothing
asks and runs where an i64 does; NK1200; and the overflow in a hole, a
condition, a list and a receiver, once each. bits.rs’s i32 + 3000000000
is NK1116 at b now, naming the use, where it was NK1199.