From 727d70d2bac266a3f1a5f9ac3de19650e58b9693 Mon Sep 17 00:00:00 2001 From: araq Date: Sun, 30 Aug 2026 21:10:49 +0200 Subject: [PATCH] IC: keep the node bridge out of the `nimKochBootstrap` build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `koch boot` failed with `bodynav.nim(278) Error: undeclared identifier: 'program'`. `transf` imported `nodebridge` unconditionally, which pulls in `bodynav`, which resolves names through `ast.program` — and `program` does not EXIST under `-d:nimKochBootstrap`. That define disables the IC subsystem wholesale: `ast.nim` guards `program` and the loader callbacks with it, and `koch.bootic` records that it also disables `commandIc`. The bridge was being compiled into a build with nothing for it to talk to. Guarded the same way `ast.nim` already guards `program`. `handOffBody` goes with it; its only caller is `cgen`, under `-d:newIcBackend`. `koch temp` cannot catch this — it does not set the define — so the break survived several rounds of verification that all used it. Predates this branch's recent work: `transf`'s unconditional import and `bodynav`'s use of `program` are both there at 8afd306b0. Also moved `bnode`'s `std / assertions` import inside the `newIcBackend` branch, the only one that asserts. At module scope it warned "imported and not used" on every default build, which is how it showed up in the boot output. Verified: `koch boot -d:release` reaches "executables are equal: SUCCESS!"; both non-bootstrap configurations build; `tests/ic` 40/40; the generated C is unchanged — 67/67 identical to before this commit, and cursor still identical to `PNode`. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01FMyRHByv7hhaQJ4Pa1bHbE --- compiler/bnode.nim | 5 ++--- compiler/transf.nim | 45 +++++++++++++++++++++++++++------------------ 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/compiler/bnode.nim b/compiler/bnode.nim index c75708d5f4..9fcec4d799 100644 --- a/compiler/bnode.nim +++ b/compiler/bnode.nim @@ -243,9 +243,6 @@ import ast, lineinfos, idents -when defined(nimPreviewSlimSystem): - import std / assertions - # ---- opt-in profiling (-d:icBNodeProf) -------------------------------------- # Counts and coarse phase timings; the accessors are far too small to time # individually. Each backend process appends one line to $NIM_IC_BNODE_PROF (or @@ -305,6 +302,8 @@ else: template timed*(s: untyped; body: untyped) = body when defined(newIcBackend): + when defined(nimPreviewSlimSystem): + import std / assertions # only this branch asserts import "../dist/nimony/src/lib/nifcore" except pool import ic / enum2nif import ast2nif, icnifcore, bodynav diff --git a/compiler/transf.nim b/compiler/transf.nim index 424464d75a..ce9510210b 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -37,7 +37,15 @@ type proc transformBody*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; flags: TransformFlags): PNode import closureiters, lambdalifting -import nodebridge + +when not defined(nimKochBootstrap): + # The `PNode` -> `TokenBuf` bridge, and through it `bodynav`, which resolves + # names against `ast.program`. `program` does not EXIST under + # `-d:nimKochBootstrap` — that define disables the whole IC subsystem (see + # `ast.nim` and `koch.bootic`) — so the bridge has to be out of that build + # too, not merely unused by it. `handOffBody` below is guarded for the same + # reason; its only caller is `cgen`, under `-d:newIcBackend`. + import nodebridge type PTransCon = ref object # part of TContext; stackable @@ -1437,23 +1445,24 @@ proc transformBody*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; flags: Transf #if prc.name.s == "main": # echo "transformed into ", renderTree(result, {renderIds}) -proc handOffBody*(body: PNode; conf: ConfigRef): BridgeBuf = - ## THE HANDOFF from the rewriting stage to the reading stage: the transformed - ## body, as a `TokenBuf` a reader can cursor over (`nodebridge`). - ## - ## It lives here because the invariant it carries is this module's: a bridged - ## buffer is a SNAPSHOT, so it must be taken after the LAST rewrite the body - ## will receive. Anything that mutates a node afterwards — `cgen.easyResultAsgn` - ## setting `nfPreventCg` is the one that does — leaves the buffer describing a - ## tree that no longer exists. - ## - ## The call site is in `cgen` rather than at the end of `transformBody` for - ## exactly that reason: destructor injection runs *after* `transformBody` - ## returns and is another rewrite, so transforming is not the last step and a - ## buffer taken here would be stale before it was read. `transformBody` returns - ## a `PNode` on purpose; this is the point where a caller that has finished - ## rewriting says so. - result = toTokenBuf(body, conf) +when not defined(nimKochBootstrap): + proc handOffBody*(body: PNode; conf: ConfigRef): BridgeBuf = + ## THE HANDOFF from the rewriting stage to the reading stage: the transformed + ## body, as a `TokenBuf` a reader can cursor over (`nodebridge`). + ## + ## It lives here because the invariant it carries is this module's: a bridged + ## buffer is a SNAPSHOT, so it must be taken after the LAST rewrite the body + ## will receive. Anything that mutates a node afterwards — `cgen.easyResultAsgn` + ## setting `nfPreventCg` is the one that does — leaves the buffer describing a + ## tree that no longer exists. + ## + ## The call site is in `cgen` rather than at the end of `transformBody` for + ## exactly that reason: destructor injection runs *after* `transformBody` + ## returns and is another rewrite, so transforming is not the last step and a + ## buffer taken here would be stale before it was read. `transformBody` returns + ## a `PNode` on purpose; this is the point where a caller that has finished + ## rewriting says so. + result = toTokenBuf(body, conf) proc transformStmt*(g: ModuleGraph; idgen: IdGenerator; module: PSym, n: PNode; flags: TransformFlags = {}): PNode = if nfTransf in n.flags: