better type keys

This commit is contained in:
Araq
2026-06-20 09:53:35 +02:00
parent b71d06e2f5
commit 8e5ba13fe3
3 changed files with 28 additions and 55 deletions

View File

@@ -2236,60 +2236,6 @@ proc sealLoadedBackendEntities*(c: var DecodeContext) =
for _, v in c.types:
if v[0] != nil and v[0].state == Complete: v[0].state = Sealed
proc preloadLoweredDefs(c: var DecodeContext; n: var Cursor; thisModule: string;
localSyms: var Table[string, PSym]) =
## One pre-scan over a `.t.nif` body that fully loads EVERY inline def it
## carries before the body is decoded: suffix-less locals into `localSyms`,
## backend-minted `@bk` syms into `c.syms`, `@bk` types into `c.types`. These
## transform-created entities have NO module index entry, so a later reference
## — in the body, or inside a loaded type that `typeKey`/codegen later walks
## (where the index-based force-load would assert) — must find them already
## loaded. The writer emits defs before uses, so a forward walk that loads each
## in place suffices.
if n.kind != ParLe:
inc n
return
var depth = 0
while true:
if n.kind == ParLe:
if n.tagId == sdefTag:
let nm = n.firstSon
if nm.kind == SymbolDef:
let symName = pool.syms[nm.symId]
let sn = parseSymName(symName)
if sn.module.len == 0:
if symName notin localSyms:
let module = moduleId(c, thisModule)
let val = addr c.mods[module].symCounter
inc val[]
let sym = PSym(itemId: itemId(module.int32, val[]), kindImpl: skStub,
name: c.cache.getIdent(sn.name), disamb: sn.count.int32,
state: Complete)
localSyms[symName] = sym
inc n
loadSymFromCursor(c, sym, n, thisModule, localSyms)
sym.state = c.loadedState
continue
elif sn.module.endsWith(BackendLocalMarker):
let sym = c.loadSymStub(nm.symId, thisModule, localSyms)
if sym.state == Partial:
sym.state = c.loadedState
inc n
loadSymFromCursor(c, sym, n, thisModule, localSyms)
continue
elif n.tagId == tdefTag:
let nm = n.firstSon
if nm.kind == SymbolDef and pool.syms[nm.symId].endsWith(BackendLocalMarker):
discard loadTypeStub(c, n, localSyms)
continue
inc depth
elif n.kind == ParRi:
dec depth
if depth == 0:
inc n
break
inc n
proc resolveHookSym*(c: var DecodeContext; symId: nifstreams.SymId): PSym
proc repTagToOp(tagId: TagId): (bool, TTypeAttachedOp) =

View File

@@ -29,7 +29,7 @@ const
nimEnableCovariance* = defined(nimEnableCovariance)
icFormatVersion* = "16"
icFormatVersion* = "17"
## Version of the IC cache format (the sem-NIF module layout written by
## ast2nif.nim plus the iface/impl/edges side files). Bump it whenever
## that layout changes: `commandIc` wipes a nimcache whose `ic.version`

View File

@@ -139,6 +139,25 @@ proc maybeImported(c: var Context; s: PSym; conf: ConfigRef) {.inline.} =
if s != nil and {sfImportc, sfExportc} * s.flagsImpl != {}:
c.symKey(s, conf)
proc backendTypeName(t: PType; conf: ConfigRef): string =
## Stable cross-module identity of a backend-minted (lower-stage) type: its
## serialized `@bk` NIF name (mirrors ast2nif.nifTypeName). A closure-env
## object/ref minted by the `lower` stage has NO stable STRUCTURAL key — its
## captured-field types re-resolve to different modules in the producing vs the
## consuming process (e.g. field `x0` → `int` in the producer, → the consumer's
## alias in the consumer) — but this name (kind + item + home-module suffix) is
## identical in both, because the consumer loads the producer's name verbatim.
## Keying hooks by it makes producer `setAttachedOp` and consumer `getAttachedOp`
## agree. The trailing `@bk` (= ast2nif.BackendLocalMarker) keeps it disjoint
## from any normal type's structural key.
result = "`t"
result.addInt ord(t.kind)
result.add '.'
result.addInt t.uniqueId.item
result.add '.'
result.add modname(t.uniqueId.module, conf)
result.add "@bk"
proc typeKey(c: var Context; t: PType; flags: set[ConsiderFlag]; conf: ConfigRef) =
if t == nil:
c.m.addEmpty()
@@ -148,6 +167,14 @@ proc typeKey(c: var Context; t: PType; flags: set[ConsiderFlag]; conf: ConfigRef
assert c.tl != nil
c.tl(t)
if t.uniqueId.isBackendMinted:
# Backend-minted (lower-stage) closure-env types key by their stable NIF name,
# never by structure (which diverges across the NIF boundary). An env `ref`
# that is itself NOT backend-minted still keys stably: it recurses here and
# reaches its `@bk` object, which short-circuits to a stable name.
c.m.addSymbol backendTypeName(t, conf)
return
case t.kind
of tyGenericInvocation:
for a in t.sonsImpl: