Class CellReadResolver¶
Defined in File cell_registry.hpp
Class Documentation¶
-
class CellReadResolver¶
Resolves one consumer cell’s operand fetches to table reads. Installed on a scratch cache for one consumer cell at a time (
begin_consumerresets the per-operand read cursors);fetchis consulted byevaluate_implahead of every other probe.Positional matching — the invariant that makes a cursor per operand value sufficient, and the reason a
Readcarries no leg number:the table emits a consumer’s
Readentries in production-tree leg order (cell_table_builder.hpp walksCellTableInputs::operands_of, which is per-leg with repetition, left leg then right leg);the executor fetches a consumer’s legs in that same order (
evaluate_implrequests the left operand, then the right);therefore the i-th surviving
Readof one value incursor_is the i-th leg of that value, and popping the front matches legs to reads with no leg index anywhere.begin_consumerasserts (1) by checking each per-value cursor is ordered by table position;every table value in a consumer’s production tree is a direct leg. A non-leg intermediate node of that tree is a transient (not a value of the table) and
fetchreports it as such; a table value reached from an intermediate node instead of a leg has noReadof its own andfetchthrows rather than borrowing another leg’s read.
Public Functions
-
inline CellReadResolver(CellRegistry ®, std::function<std::optional<std::size_t>(std::size_t)> vid_of_hash)¶
-
inline void begin_consumer(CellId consumer)¶
Resets the per-operand read cursors to every
Readofconsumer, in table order — i.e. in the consumer’s production-tree leg order, which is what makes the front of a per-value cursor the next leg to fetch (see the positional-matching invariant on this class).
-
inline CellRegistry const ®istry() const noexcept¶
The registry this resolver reads from — the storage the table owns. Read-only: a caller that wants to observe what a cell currently holds (a test probe, a diagnostic) goes through here; production is the executor’s business.
-
inline std::optional<ResultPtr> fetch(std::size_t operand_node_hash, BatchContext const &ctx)¶
- Returns:
nullopt when
operand_node_hashis not a value of the table (a transient of this production tree, evaluated in place by the caller), or when the matched Read’s source is a leaf cell with no current result yet (first touch: the caller must evaluate the leaf and callrecord_leaf; the cursor entry is left unconsumed so the same Read is served — and consumed — by a later fetch once the leaf is recorded). Any other matched Readwhose source has no current result throws naming the consumer cell, the source cell and the value (spec section 4: “missing entry or non-resident source: throw with both
ids”)
— a well-formed table guarantees a Build cell’s own source is always resident when read (registry lookups go by residency, seeCellRegistry::cell_of, and persistent cross-call values are seeded into the registry at entry, seerun_ordered_schedule_pre_results), so this is a genuine table/tree or recording gap, never deferred. Otherwise the sliced source: each(pos, key) of the matched Read’ssliceis applied asslice_mode(pos, range.first, range.second) withrangetaken from thectxentry whoselevel.key()equalskey. Throws when the consumer has no remaining Read of that value at all (table/tree disagreement), or when a declared slice names a loop instance absent fromctx.
-
inline void record_leaf(std::size_t hash, ResultPtr r)¶
Records a leaf’s freshly evaluated result in the registry (called after the leaf evaluator runs on
hash'sfirst touch): a no-op ifhashis not a value of the table or names no Leaf cell.
-
inline bool operand_drained(std::size_t operand_node_hash) const¶
Whether
operand_node_hash'soperand is currently safe for in-place accumulation — the registry-derived counterpart of theCacheManager::chain_holds_shared(f.left) check thateval.hpp'sin-place gate applies on the forest path.True in exactly two cases:
operand_node_hashis not a value of this table at all (a private transient of the current production tree, e.g. an intermediate running total of an accumulate-in-placeSumchain that was never promoted to its own cell) — no table cell could possibly be sharing it, exactly aschain_holds_shared()reports “not held” for the analogous untracked case; orit is a table value, has been
fetch()'dat least once, and its most recent fetch’s source cell (last_served_source_) is currentlyCellRegistry::drained— i.e. that read spent the source’s last declared life (never true for a persistent cell, which never drains) and the registry has already let go of its own reference (CellRegistry::readmoves the value out on that read), so nothing this evaluation will read it again.
A table value never yet
fetch()'dthrough this resolver (no entry inlast_served_source_) is reported not drained — the safe default. The registry may well be holding that value for other readers (it is a table cell, so some cell owns it), and this resolver has no evidence either way; answering “drained” would license an in-place mutation of a buffer this evaluation is going to read again. Out-of-place costs one allocation; the wrong answer corrupts a shared value.
-
inline std::size_t served() const noexcept¶
Diagnostic: the number of operand fetches this resolver has actually served (table-driven, sliced-per-Read) rather than deferring to the caller (a transient, a leaf’s first touch, or a value this call’s runtime cache-halt gate skipped — see
fetch'sown doc comment).