impl Where They Were WrittenStatus: Superseded by ADR-295 — its D1-D5 are D10-D14 there
Date: September 16, 2026
Target Version: Nikaia 0.0.8
Related: ADR-078 D2 and D3 (the bound, and its lookup in
the ledger), ADR-095 (the impl side already crosses the
boundary), ADR-046 D1 and D2 (use brings no name in — which
this record keeps), ADR-100 D1 (a dependency is read through
its ledger), ADR-010 D1 (absence is never an answer)
A bound names a trait, and a trait may live in another package. Everywhere
else a type is named, a path is allowed — r: http::Request,
http::Response(status: 400) — and a bound is the one position where it is
not: [H: http::Handler] does not parse, and [H: Handler] after use http
finds nothing, because use brings no name in. The impl side already
crosses the boundary (ADR-095); the bound side cannot say
where the trait lives, and the ledger has nowhere to say what the trait
declares or who implements it.
fn dispatch[H: http::Handler](r: Router[H]) -> String parses, and
[T: A + http::B] mixes a local and a foreign bound. The grammar’s rule for
a type name is the rule for a bound’s trait name.
use is unchanged: no name is brought in, for a trait as for anything else.The reason ADR-046 gives holds for a trait: a reader of any line knows where every name in it comes from without the top of the file. A trait is not an exception to it; D1 is what makes the exception unnecessary.
trait table, and a trait’s methods are ordinary fn entries.[trait."http::Handler"]
pub = true
[fn."http::Handler::handle"]
signature = "(&Self, request: http::Request) -> http::Response"
sync = true
A trait entry has no fields; its methods are the entries the impl owes
(NK1130), with signature, sync and throws as a declaration writes
them. The table name is the one word the checker needs: a bound and an
impl … for must name a trait, and a trait is never a type of a value
(Part I 4.7).
impl is an entry in the ledger of the package that wrote it, and whether a type implements a trait is the union of what the program and its dependencies’ ledgers say.[impl."http::Handler for http::Static"]
An impl may be written in the trait’s package or in the type’s, and a
consumer may add one for its own type, so no single ledger can list a
trait’s implementors or a type’s traits completely. A list read as
complete would turn absence into an answer, which ADR-010 D1
forbids. Each ledger therefore says only what it wrote, and the question at
a call — does Fixed implement http::Handler? — is answered over every
ledger this program reads plus its own. An impl no read ledger wrote does
not exist for this program.
There is no dyn, so at every call through a bound the concrete type is
known. h.handle() on an H bound to Fixed is answered from
[fn."Fixed::handle"] — its own sync and throws — and the trait’s
declaration is the ceiling an impl may not exceed, checked where the
impl is written (NK1129, NK1130).
A package can publish a trait and a consumer can constrain a generic by
it, so Repository, Render and Handler bounds cross a package the way
a type already does. The three refusals a cross-package bound met — the
parse error, NK1126, and the false NK1129 — become a lookup.
What a library author writes is nothing new: pub trait and impl,
as within one file. The ledger records both where they stand.
What it costs: two tables in the ledger and their parser; the union in the bound check; and the bound’s path in the grammar.
sync.impl A for T — is
rustc’s to refuse until a program meets it.Nothing. [H: http::Handler] is a parse error, the ledger has no trait
and no impl table, and a bound is looked up in the current unit alone.
The order of work:
trait table and the method entries, written by the package’s own
build (D3);impl table (D4) and the union in the bound check;#193 carries it.