mirror of
https://github.com/nim-lang/Nim.git
synced 2026-06-14 15:43:45 +00:00
IC: bugfixes
This commit is contained in:
@@ -284,7 +284,14 @@ proc writeTypeDef(w: var Writer; dest: var TokenBuf; typ: PType) =
|
||||
proc writeType(w: var Writer; dest: var TokenBuf; typ: PType) =
|
||||
if typ == nil:
|
||||
dest.addDotToken()
|
||||
elif typ.itemId.module == w.currentModule and typ.state == Complete:
|
||||
elif typ.uniqueId.module == w.currentModule and typ.state == Complete:
|
||||
# Ownership for serialization is decided by `uniqueId`, not `itemId`: the NIF
|
||||
# name (`typeToNifSym`) and the loader (`createTypeStub`) both key off
|
||||
# `uniqueId`, so the module that *created* the type (uniqueId.module) must be
|
||||
# the one that emits its definition. `itemId.module` can be reassigned and
|
||||
# diverge from `uniqueId.module`; gating on it filed the def in the wrong
|
||||
# module (or nowhere), leaving dangling references (e.g. `symbol has no
|
||||
# offset` for a `pointer` type whose itemId.module drifted away).
|
||||
typ.state = Sealed
|
||||
writeTypeDef(w, dest, typ)
|
||||
else:
|
||||
|
||||
@@ -290,9 +290,22 @@ proc setAttachedOp*(g: ModuleGraph; module: int; t: PType; op: TTypeAttachedOp;
|
||||
# Use key-based deduplication for opsLog because different type objects
|
||||
# (e.g. canon vs orig) can have different itemIds but same structural key
|
||||
if key notin g.loadedOps[op]:
|
||||
# Hooks should be written to the module where the type is defined,
|
||||
# not the module that triggered the registration
|
||||
let ownerModule = if t.sym != nil: t.sym.itemId.module.int else: module
|
||||
# For a *nominal*, non-instantiated type the hook belongs to the module
|
||||
# that defines the type (so it is emitted once there). But for a generic
|
||||
# instance or a structural type (ref/ptr/seq/... over an instance) there is
|
||||
# no honest definition site: the generic's home module is upstream of the
|
||||
# type arguments and structurally blind to the instance, so it can never
|
||||
# realize the hook. Such hooks can only be produced by the *instantiating*
|
||||
# module — which is exactly the one running now (`module`). Stamping them
|
||||
# with the type's def module produced a `LogEntry` that no module ever
|
||||
# writes (the def module never instantiates it; the instantiating module's
|
||||
# writer skips it because `op.module != thisModule`), so the hook was lost
|
||||
# and codegen failed with "'=destroy' operator not found". Duplicate
|
||||
# registrations across instantiating modules are reconciled deterministically
|
||||
# at load time (see the HookEntry replay in `replayStateChanges`).
|
||||
let nominal = t.sym != nil and t.kind in {tyObject, tyEnum, tyDistinct} and
|
||||
tfFromGeneric notin t.flags
|
||||
let ownerModule = if nominal: t.sym.itemId.module.int else: module
|
||||
g.opsLog.add LogEntry(kind: HookEntry, op: op, module: ownerModule, key: key, sym: value)
|
||||
g.loadedOps[op][key] = value
|
||||
g.attachedOps[op][t.itemId] = value
|
||||
@@ -691,7 +704,16 @@ when not defined(nimKochBootstrap):
|
||||
for x in result.logOps:
|
||||
case x.kind
|
||||
of HookEntry:
|
||||
g.loadedOps[x.op][x.key] = x.sym
|
||||
# The same structural hook may be serialized by several instantiating
|
||||
# modules (a generic/structural instance has no single def site, so each
|
||||
# using module owns its copy). Pick one deterministic program-wide winner
|
||||
# by the smaller owning-module name, so every lookup resolves to the same
|
||||
# sym regardless of module load order.
|
||||
let existing = g.loadedOps[x.op].getOrDefault(x.key)
|
||||
if existing == nil or
|
||||
cachedModuleSuffix(g.config, x.sym.itemId.module.FileIndex) <
|
||||
cachedModuleSuffix(g.config, existing.itemId.module.FileIndex):
|
||||
g.loadedOps[x.op][x.key] = x.sym
|
||||
of ConverterEntry:
|
||||
g.ifaces[fileIdx.int].converters.add x.sym
|
||||
of MethodEntry:
|
||||
|
||||
@@ -446,6 +446,12 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
|
||||
header[i] = x
|
||||
propagateToOwner(header, x)
|
||||
else:
|
||||
# Honor the same copy-before-mutate invariant as the branch above: never
|
||||
# mutate the original invocation type `t` in place. Besides being cleaner,
|
||||
# under IC `t` may be a loaded dep type (Sealed/immutable), and mutating it
|
||||
# would assert. The flags propagated here end up on `header`, which is what
|
||||
# is used downstream (`result.flags = header.flags`).
|
||||
if header == t: header = instCopyType(cl, t)
|
||||
propagateToOwner(header, x)
|
||||
|
||||
if header != t:
|
||||
|
||||
@@ -147,7 +147,14 @@ proc typeKey(c: var Context; t: PType; flags: set[ConsiderFlag]; conf: ConfigRef
|
||||
else:
|
||||
symKey(c, t.symImpl, conf)
|
||||
of tyGenericInst:
|
||||
if sfInfixCall in t.sonsImpl[0].symImpl.flagsImpl:
|
||||
# The generic head (son[0]) may be a lazily-loaded stub under IC; ensure it
|
||||
# is materialised before peeking at its symbol. A nil sym means this is not
|
||||
# an imported C++ generic, so fall through to the normal `skipModifierB`.
|
||||
var base = t.sonsImpl[0]
|
||||
if base.state == Partial:
|
||||
assert c.tl != nil
|
||||
c.tl(base)
|
||||
if base.symImpl != nil and sfInfixCall in base.symImpl.flagsImpl:
|
||||
# This is an imported C++ generic type.
|
||||
# We cannot trust the `lastSon` to hold a properly populated and unique
|
||||
# value for each instantiation, so we hash the generic parameters here:
|
||||
|
||||
Reference in New Issue
Block a user