ADR-217: What the Specification Stated and the Compiler Did Not Build — T::variants, Kept Function Values, and Views Kept by a Parameter or a Result

Status: Accepted Date: September 26, 2026 Answers: the owner’s request, after the specification lost its status notes: fix the Bytes/fs::read contradiction, build T::variants if it makes sense, and build what is missing where a sentence taken from a status note describes what is not built rather than the language — NK1142 and the NK2302 cases of Part I 6.6 Builds: ADR-277 D11 (the kept lowering), ADR-304 D2 (T: Enum has T::variants), ADR-283 D1 (a destination that names a buffer is not a refusal) Related: ADR-283 (the two NK2304 refusals, which stay)

1. Context

The specification states the contract, and four of its sentences need a decision before the compiler can honour them:

  the page without this record
a Part I 2.2: fs::read → Vec[u8]; 1.3: Bytes is the shared buffer fs::read hands back Bytes, so the two pages disagree
b Part II 10.3: a T: Enum has T::variants NK1171
c ADR-277 D6: a function type stands wherever a type does NK1142 in a field, a result, a let
d Part I 6.6: a view kept where the destination names a buffer is lowered refused for a struct literal handed back (NK2302); accepted and then rustc for a field of a struct parameter

(d)’s second half is Part III C.1’s class: a view analysis that follows fields of self only records a store into s.label for a parameter s nowhere, and the signature does not tie the two lifetimes.

2. Decisions

D1 — T::variants is the enum half of a shape walk, and a variant answers two members.

Under [T: Enum], for v in T::variants is unrolled once per variant, in declared order, as T::fields is once per field. A reflected variant answers .name (its name as text) and .is(value) (whether the value is that variant; below, matches!(value, E::V { .. }), which fits every variant shape). Anything else is NK1180. What a variant carries is not reflected: a payload is a different value per variant, and match reads it — the question ADR-304 D2 left open is answered by not asking it. T::fields without a Struct bound, or T::variants without an Enum bound, is NK1171 naming the bound.

The specialised copy’s name is keyed by statement, so where a call to a shape walk sits inside another call (println(which(x))), only the call whose callee is the walking function is renamed.

D2 — A kept function value is one shared closure.

A function type in a field, a result, a let, an element, or a parameter the callee keeps lowers to nikaia_std::func::Kept<dyn Fn(A) -> R>: an Arc of the closure, cloned by a count, printed as <fn>, called as the closure. A type that may pause hands back a boxed future (Boxed<R>); one that may fail hands back the Result a throws function does. Where tasks run on threads the closure is Send + Sync and the future Send.

NK1142 is retired. Two values of a type holding a function do not compare (NK1188), which the structural walk already answered.

D3 — A view kept by the result, or by one struct parameter, names its buffer.

The two NK2304 refusals of ADR-283 stay. One buffer handed both to a task and out of the function has no single owner. A container of structs holding views that drops entries in a loop would need each such struct lowered a second time with a per-view handle in every view field, and every method and construction site to follow — a lowering of its own, not a missing piece. Whether the language should hold it is a question for the owner, on the former open-decisions.md.

D4 — Part I 2.2 says what fs::read hands back.

Bytes, one shared buffer handed on by a count (Part III 17.1). Part I 1.3 does not tie Bytes to NK2303 as a refusal: NK2303 is not a refusal.

This site is open source. Improve this page.