ADR-106: A Bound Takes a Path, and the Ledger Records a Trait and Each impl Where They Were Written

Status: 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)

1. Context

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.

2. Decisions

D1 — A bound takes a path, as every other position that names a type does.

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.

D2 — 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.

D3 — The ledger has a 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).

D4 — Each 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.

D5 — A call through a bound resolves to the implementing type’s own entry.

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).

3. Consequences

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.

4. What this does not decide

5. What is built

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:

  1. the path in the bound’s grammar (D1);
  2. the trait table and the method entries, written by the package’s own build (D3);
  3. the impl table (D4) and the union in the bound check;
  4. the resolution at a call through a bound (D5).

#193 carries it.

This site is open source. Improve this page.