diff --git a/compiler/cgen.nim b/compiler/cgen.nim index a1b36c2db1..37b55899b9 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1379,6 +1379,13 @@ proc genProcBody(p: BProc; procBody: PNode) = proc genProcLvl3*(m: BModule, prc: PSym) = if m.config.cmd == cmdNifC: fillBackendName(m, prc) + if (prc.disamb and (InstanceDisambBit or HookDisambBit)) != 0'i32 and + containsOrIncl(m.emittedContentDefs, stripCnifMarks(prc.loc.snippet)): + # A different symbol already emitted a body under this content-addressed + # C name in this TU (same generic instance / hook minted in two source + # modules, both loaded here). Emitting a second body is a C redefinition; + # a prototype was already produced for it, so just stop. + return if sfDispatcher in prc.flags and sfMainModule notin m.module.flags: # A method dispatcher enumerates the whole program's method set: its # body is synthesized by `generateIfMethodDispatchers` only after all @@ -2533,6 +2540,7 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: AbsoluteFile): BModule result.headerFiles = @[] result.declaredThings = initIntSet() result.declaredProtos = initIntSet() + result.emittedContentDefs = initHashSet[string]() result.icImplMods = initIntSet() result.cfilename = filename result.filename = filename diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index ca34f8e870..8e096d4f00 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -158,6 +158,12 @@ type forwTypeCache*: TypeCache # cache for forward declarations of types declaredThings*: IntSet # things we have declared in this .c file declaredProtos*: IntSet # prototypes we have declared in this .c file + emittedContentDefs*: HashSet[string] + # cmdNifC per-module backend: content-addressed C names (generic + # instances and synthesized hooks) whose body this TU already emitted. + # Distinct symbols (minted in different source modules) can share one + # `_i` name; `declaredThings` keys on symbol id and lets the + # second one through, so we dedup the body by name here instead. queue*: seq[PSym] # queue of procs to generate alive*: IntSet # symbol IDs of alive data as computed by `dce.nim` headerFiles*: seq[string] # needed headers to include diff --git a/compiler/deps.nim b/compiler/deps.nim index 07e4652673..3ec648283f 100644 --- a/compiler/deps.nim +++ b/compiler/deps.nim @@ -34,6 +34,9 @@ type processedModules: Table[string, int] # modname -> node index includeStack: seq[string] systemNodeId: int # ID of the system.nim node + scanningMain: bool # currently scanning the project main module's deps; + # makes `when isMainModule` conditions evaluate true + # only there (every other module is imported) proc toPair(c: DepContext; f: string): FilePair = FilePair(nimFile: f, modname: moduleSuffix(f, cast[seq[string]](c.config.searchPaths))) @@ -256,6 +259,14 @@ proc evalCondIdent(c: DepContext; v: string): bool = # defined(gcHooks)`; guards mmdisp.nim's `include "system/gc"` whose # transitive imports (sharedlist, locks) an orc compile never produces. isDefined(c.config, "gcDestructors") or isDefined(c.config, "gcHooks") + of "isMainModule": + # Only the project main module is compiled with `isMainModule` true; an + # imported module's `when isMainModule` blocks are dead. The conservative + # `true` would schedule main-only imports (e.g. parser.nim's + # `tools/grammar_nanny`, a node that gets a cg rule but is never linked, + # so the merge stage can pick it as a shared def's owner -> undefined + # symbols at link). + c.scanningMain else: true proc evalCondExpr(c: DepContext; s: var Stream): bool = @@ -494,6 +505,12 @@ proc readDepsFile(c: var DepContext; pair: FilePair; current: Node) = if not fileExists(depsPath): return + # `current.id == 0` is the project main (rootNode); restored on exit so the + # flag is correct for each parent frame between its child recursions. + let prevScanningMain = c.scanningMain + c.scanningMain = current.id == 0 + defer: c.scanningMain = prevScanningMain + var s = nifstreams.open(depsPath) defer: nifstreams.close(s) discard processDirectives(s.r) diff --git a/compiler/nifbackend.nim b/compiler/nifbackend.nim index 62a873393f..dae0ea67f1 100644 --- a/compiler/nifbackend.nim +++ b/compiler/nifbackend.nim @@ -154,6 +154,14 @@ proc signatureHasMetaType(t: PType; depth: int = 0): bool = ## element case, hence the explicit scan. result = false if t == nil or depth > 8: return false + if t.kind == tyGenericBody: + # The uninstantiated template carried as a `tyGenericInst`'s first child + # always mentions its `tyGenericParam` placeholders, but the instance + # itself is fully concrete (e.g. `var CountTable[SigHash]`). Descending + # here would wrongly flag every routine with a generic-instance parameter + # as meta and drop it from the owned-routine seeding -> undefined symbols + # at link (its only definer never emits it). + return false if t.kind in {tyTyped, tyUntyped, tyTypeDesc, tyStatic, tyGenericParam, tyAnything, tyFromExpr, tyError}: return true