From d4edf4e4da74b6bd37ca042ca576be2628744e75 Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 27 Jun 2026 21:03:48 +0200 Subject: [PATCH] make koch-boot work again --- compiler/ast.nim | 8 ++++++++ compiler/ast2nif.nim | 21 +++++++++++++++++++++ compiler/cgen.nim | 7 ++++++- 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 0c284a19f5..494b70a81e 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -546,6 +546,14 @@ proc idGeneratorForPackage*(nextIdWillBe: int32): IdGenerator = proc nextSymId(x: IdGenerator): ItemId {.inline.} = assert(not x.sealed) + when not defined(nimKochBootstrap): + if x.backendMinted: + # Share the loader's per-module backend counter so a freshly-minted + # backend sym never collides with an `@bk` sym loaded from the module's + # `.t.bif` (see ast2nif.nextBackendSymItem). + let it = nextBackendSymItem(program, x.module) + if it >= 0'i32: + return backendItemId(x.module, it) inc x.symId result = if x.backendMinted: backendItemId(x.module, x.symId) else: itemId(x.module, x.symId) diff --git a/compiler/ast2nif.nim b/compiler/ast2nif.nim index e1aad5e16b..48a0d4f20f 100644 --- a/compiler/ast2nif.nim +++ b/compiler/ast2nif.nim @@ -1716,6 +1716,27 @@ proc createDecodeContext*(config: ConfigRef; cache: IdentCache): DecodeContext = ## Supposed to be a global variable result = DecodeContext(infos: LineInfoWriter(config: config), cache: cache) +proc nextBackendSymItem*(c: var DecodeContext; module: int32): int32 = + ## Allocate the next backend-minted SYM item for `module` from the SAME + ## per-module counter the loader uses when it re-homes `@bk` syms loaded from + ## the module's `.t.bif` (loadSymStub/extractLocalSymsFromTree). The `lower` + ## stage serializes its lifted hooks/temps as `@bk` syms, and cg mints MORE + ## backend syms (RTTI destroy wrappers, ...) into the same module. Both are + ## keyed by `.id` (= `toId(itemId)`) in `declaredThings`/`declaredProtos`, so + ## if the two id producers (the loader's `symCounter` and cg's idgen) ran + ## independently they could mint the same item: e.g. a `rttiDestroy` wrapper + ## and the very `=destroy` hook it wraps both land on backend item 21 -> one + ## masks the other in `declaredThings` -> the hook's body is never emitted -> + ## "undefined reference" at link. Drawing every backend sym from this one + ## counter keeps them disjoint. Returns -1 if the module is not loaded yet + ## (then the caller falls back to the idgen's own counter — only reachable + ## for sem-time `@bk` minting, whose module is never loaded in that process). + let fi = module.FileIndex + if not c.mods.hasKey(fi): return -1'i32 + let p = addr c.mods[fi].symCounter + inc p[] + result = p[] + proc setMainModule*(c: var DecodeContext; fileIdx: FileIndex) = ## Records the module that is being compiled fresh so that re-exports of its ## own symbols by dependencies are not turned into duplicate stubs. diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 3cf6c87b39..1ebb8f3291 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1480,7 +1480,12 @@ proc genProcLvl3*(m: BModule, prc: PSym) = # process (owned by nobody → undefined at link). So inject ONLY when the body # was re-derived in this process (`wasLoaded == false`). Capture before # `transformBody`, which returns the cached body (non-nil) when it was loaded. - let wasLoaded = prc.transformedBody != nil + # ONLY under IC: in a normal `nim c` build `transformedBody` is the ordinary + # transform cache (set whenever `transformBody` already ran for `prc`, e.g. a + # CT-evaluated or earlier-referenced routine), NOT a `.t.bif` load — gating on + # it there would WRONGLY skip destructor injection and miscompile (orc + # decref-on-freed). The `.t.bif`-loaded-body concept exists only under cmdNifC. + let wasLoaded = m.config.cmd == cmdNifC and prc.transformedBody != nil var procBody = transformBody(m.g.graph, m.idgen, prc, {}) if sfInjectDestructors in prc.flags and not wasLoaded: procBody = injectDestructorCalls(m.g.graph, m.idgen, prc, procBody)