Status: Accepted
Date: September 22, 2026
Closes: #140 (a grammar’s entry does
not say what it keeps)
Related: ADR-296 D24 (an entry rule is a ledger entry),
ADR-140 D3 (the entry is reached by name), ADR-094
D2 (keeps), ADR-283 D2, D5 (the states a view is in),
ADR-296 D35 (an action may not pause),
ADR-010 D1 (absence read fail-closed)
ADR-140 D3 makes a grammar entry a call by name, so its
contract is a ledger entry like any other and its four columns have to be
answered. sync is ADR-296 D35, asserted rather than inferred;
touches and locks are derived over the action blocks, with the grammar as the
unit (ADR-292 D3’s over-approximation). That leaves keeps.
Leaving it absent is not neutral. keeps_its reads an absent keeps on a
present entry as keeps nothing — the same convention std.contracts uses
for sync. An absent column on a parse would answer lends, in the open
direction: read, whose body is one Stock::file(data), would lose
keeps = ["data"], though the parse it calls hands back Entry, which holds a
ref String into the very text data names.
No new mechanism is needed. The walk exists and the predicate exists; what this adds is the item kind.
input exactly when its declared result may hold a view into it.A grammar entry takes one parameter, the text
(ADR-296 D24). A parse hands back views into that text — a
ref String field of a record it built is a view of the bytes it was given —
and a value that outlives the call is what keeps means
(ADR-094 D2). So Stock::file -> Vec[Entry] keeps its input
where Entry holds a ref String, and Calc::expr -> i64 keeps nothing.
An empty column means asked and no, never nobody derived it — the two are written the same way, so an underived column would be an answer rather than a silence.
touches walks the actions, because what a parse does is in them. What a
parse can hand back is not: it is the rule’s declared result type, and an
action that built something else would not type-check. The input is not a name
an action block can reach, so the only way the text leaves the call is through
the result.
That is also why this needs no fixpoint over the grammar the way touches does.
One rule, one declared type, one answer.
ADR-094’s polarity, unchanged: a use this walk cannot account for
counts as keeping, because the two wrong answers are not symmetric. Saying
kept of a text only read costs a caller an owned argument, which is where
every caller already is. Saying lent of a text a parse holds views into is a
& on a value that outlives the call, and that is rustc’s words about a file
nobody wrote (Part III C.1).
So the walk answers no view only where it can account for every name in the result: a type this package declares that holds none, or one Part I 2.2 offers, or a container whose arguments it has already read. Anything else is a declaration it did not read.
views column, and the state is Borrowed.An entry whose result may view gets input: borrowed and
<result>: borrowed (ADR-283 D5). The buffer is the caller’s
text, which outlives the call — the same sentence tether::of already reaches
for a function with a view among its parameters, read off the shape of a grammar
instead of off a signature.
One predicate for both columns, so the two cannot drift: a parse that keeps its text is a parse whose result views it, and there is no third answer for one of them to hold.
keeps and views appear on every parse that hands back a record that
may view its text, and on none that hands back a number. read carries
keeps = ["data"], and Stock::file carries keeps = ["input"] and its two
view positions.input is typed ?, and
keeps::lends does not lend a type it cannot name; read’s data is written
ref String, which the declaration lends whatever the column says. The answer
is in the file before a call site reads it.touches, and a rule whose own result holds no view still sits in a
grammar whose records do. No column distinguishes them, and none needs to:
the question is asked of the entry a caller names.keeps is a restriction on lending
and nothing reads it yet (ADR-094 §5). When one does, a parse is
an ordinary entry with an ordinary column.