Status: Superseded by ADR-286 — its D1-D3 are D4-D6 there
Date: September 19, 2026
Target Version: Nikaia 0.0.26
Related: ADR-140 D1 (the brace struct literal, whose build
picked this in silence), ADR-133 §5 (whose open question named
the shape), ADR-047 D1 and Part I 9.1 (a package is a directory
and its files share one namespace), ADR-046 D5 (one name per
file, one namespace over), ADR-096 (NK1135), Part III C.1
Nothing said a name may denote only one thing, and the compiler behaved as if several rules were in force at once.
Across files it was refused: two files of a package declaring one name is
modules.rs’s own message, because the files share one namespace
(ADR-047 D1) and rustc would otherwise report a duplicate
definition against the generated file.
Inside one file nothing was, unless the build happened to go through a
manifest — nikaia --input skips the module layer entirely, and that is the
path the whole corpus, the specification’s blocks and every reader’s first
program take. struct Foo beside fn Foo, two struct Foos, a struct and
an enum of one name, two fn fs: each lowered, and the backend answered.
And the list of what counts was short. fn, struct and enum were
counted; a trait and a grammar were not, so trait Foo beside
struct Foo was accepted even through a manifest.
Found by building ADR-140 D1, from the case that matters
most: a name that is both a type and a function resolves to the type,
so Foo(n: 1) is NK1146 and the function is uncallable through the only
spelling ADR-133 D1 gives it — with a help that sends the reader
to a line building the struct, which is a different program. That record’s own
open question had named the shape and neither record took it.
struct Foo { n: i64 }
fn Foo(n: i64 = 0) -> i64 { return n } // error[NK1148]
Foo is declared twice in this file: as a struct and as a fn. The caret
is on the second declaration, because that is the one that arrived and the
one to move; the note names the first.
Two of a kind are the same rule and the same message — two struct Foos, two
fn fs — because the reader is doing the same thing either way: finding out
which of two declarations a line means, and being told that the question does
not arise.
fn, struct, enum, trait, grammar.Those five and nothing else. A method is not one — it belongs to its type,
and two types may each have a len. A rule of a grammar is not one — it
belongs to its grammar, and Json::value is how it is reached
(ADR-140 D3). A field, a variant, a type parameter
and an impl block declare nothing at this level either.
The anonymous constructor needs no clause. struct Foo puts Foo::new in
the ledger and fn Foo puts Foo, so the two never collide there — and they
collide here, where a reader reads, which is the whole point of the rule.
ADR-047 D1’s refusal stays where it is, because it has something
this one does not: two paths to name. What changes is that it counts a trait
and a grammar too, and that it no longer has to serve the one-file case —
which it did badly, naming one file twice in a sentence about two.
rustc’s E0428 was, which is
Part III C.1 closed for a shape the language
allowed.NK1146 is always right about what to write,
because the case that made its help wrong cannot be declared.modules.rs counts.std publishes — fn Vec(), struct String.
That is a question about a prelude rather than about this file’s
declarations, and no program has asked it.let shadowing an item’s name, which is a question about scopes
and is not this.All of it, and the reason the rule reaches both paths now is that it moved:
the per-file case is NK1148 in the checker, which every build runs, where it
used to be reachable only through the module layer a --input build skips.
The message names both kinds and the caret is on the second. Foo is
declared twice in this file: as a struct and as a fn — and where they are
of one kind it says so once, twice in this file, both as a fn, which is the
same sentence rather than a second one.
Three shapes were accepted through every path before this, and the list is
here because it is what the rule’s absence actually cost: a trait and a
grammar were not counted at all, so trait Foo beside struct Foo reached
rustc even through a manifest; and everything else reached it whenever the
build was a single file, which is how the corpus, the specification’s blocks and
a reader’s first program are all compiled.
And modules.rs keeps its refusal for what only it can say. Two files
under one name is its message, with both paths in it; it skips a pair that is
the same file, because the checker has that one and says it better.
Six tests in crates/nikaia/tests/one_name.rs.