Measured before designing: of the 180 `PNode`-taking procs in `cgen`/`ccgexprs`/
`ccgstmts`/`ccgcalls`, **99 build a `TLoc` from a node**. So the generator cannot
move to the seam without an answer for `TLoc.lode`, and the obvious answers are
both bad — leaving it a `PNode` means a cursor-driven proc cannot fill it, and
changing its representation means editing `TLoc`, which lives in `astdef` at the
bottom of the module graph, pushing the seam far below the backend and forcing a
flag day.
There is a third answer, and nifcore already had the piece it needs.
`cursorToPosition` is documented as a stable per-token key, and `TokenBuf.len`
is where the next token lands — so the encoder records `position -> PNode` as it
walks, and `originOf` inverts it. A cursor-driven generator can then put the
ORIGINAL node into a location: `TLoc.lode` stays a `PNode`, and the identity
comparisons already in the backend (`preventNrvo`'s `dest != le`,
`isPartOf(d.lode, …)`) keep meaning what they meant, because it is the same
object and not an equal copy.
Asserted, not assumed: the bridge grinder now walks cursor and tree together and
requires `originOf(c) == a` by REFERENCE at every node — 1431 bodies, 0
failures. Recording the position one token off makes it fail on the first body,
so the check is not vacuous.
This unblocks the generator migration without a flag day: `expr` and its ~60
emitters can move to `AnyNode` with `TLoc` untouched.
Verified: grind clean (1431 bodies, 260_431 nodes, 0 disagreements); the default
path is byte-identical to HEAD; all four build configurations compile.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XEF7FJvUkGKvG9LSGuEaNR