From 2c28dcba32caa34bf93c0944c479329aa97ad733 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 2 Jul 2026 14:30:24 +0200 Subject: [PATCH] IC: more refinements --- compiler/ast2nif.nim | 2 +- compiler/deps.nim | 2 +- compiler/icmodnames.nim | 55 +++++++++++++++++++++++++++++++++++++++++ compiler/nifbackend.nim | 20 +++++++++++---- compiler/nifgen.nim | 2 +- compiler/typekeys.nim | 2 +- 6 files changed, 74 insertions(+), 9 deletions(-) create mode 100644 compiler/icmodnames.nim diff --git a/compiler/ast2nif.nim b/compiler/ast2nif.nim index 5f6af3ad93..22af49a4de 100644 --- a/compiler/ast2nif.nim +++ b/compiler/ast2nif.nim @@ -29,7 +29,7 @@ import "../dist/nimony/src/lib" / [bitabs, nifstreams, lineinfos, # uses; the reader reaches pools via `symName(c)`/`strVal(c)` etc. import "../dist/nimony/src/lib/nifcore" except pool from "../dist/nimony/src/lib" / bif import load, BifModule -import "../dist/nimony/src/gear2" / modnames +import icmodnames import "../dist/nimony/src/models" / nifindex_tags import typekeys import icnifcore diff --git a/compiler/deps.nim b/compiler/deps.nim index 7071af13cf..3e09a5e40f 100644 --- a/compiler/deps.nim +++ b/compiler/deps.nim @@ -15,7 +15,7 @@ import options, msgs, lineinfos, pathutils, condsyms, modulepaths, extccomp, cnif, platform import "../dist/nimony/src/lib" / [nifstreams, bitabs, nifreader, nifbuilder] -import "../dist/nimony/src/gear2" / modnames +import icmodnames import icnifcore type diff --git a/compiler/icmodnames.nim b/compiler/icmodnames.nim new file mode 100644 index 0000000000..a162da834e --- /dev/null +++ b/compiler/icmodnames.nim @@ -0,0 +1,55 @@ +# +# +# The Nim Compiler +# (c) Copyright 2026 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Nim's OWN module-suffix, replacing nimony's `gear2/modnames.moduleSuffix`. +## +## nimony's version hashes a path made RELATIVE to `getCurrentDir()` (or the +## shortest search-path-relative form), so the produced suffix depends on the +## current working directory AND the searchPath set. Under `nim ic` the +## DISCOVERY pass (`deps.nim`, in the driver process) and the COMPILE pass +## (`nifgen`/`typekeys`, in a child `nim m` process) can run with different CWDs +## or `--path` sets, so the SAME file hashes to two different suffixes: e.g. +## `std/staticos` became `sta5rk8sn1` at discovery but `sta4c0qxk` at compile, so +## every importer waited forever for a `.s.bif` that was actually written under +## the other name — a cold `nim ic` build (of anything pulling in `std/os`, whose +## `oscommon` does `from std/staticos import PathComponent`) never converged. +## +## Hashing the CANONICAL ABSOLUTE path makes the suffix a pure function of the +## file, identical across every process and call site. The base-name prefix + +## base-36 `uhash` layout is kept byte-for-byte compatible with the old scheme so +## nothing but the hashed string changes. + +import std/os +import "../dist/nimony/src/lib" / tinyhashes + +const + PrefixLen = 3 # keep it short: the suffix ends up in every mangled C name + Base36 = "0123456789abcdefghijklmnopqrstuvwxyz" + +proc moduleSuffix*(path: string; searchPaths: openArray[string]): string = + ## `searchPaths` is accepted for signature-compatibility with the replaced + ## `modnames.moduleSuffix` but is deliberately IGNORED — the suffix must not + ## depend on the search-path set or the CWD (see the module doc). + # Absolute inputs (the norm at every call site: `toFullPath`/`projectFull`) + # pass straight through `normalizedPath` with no `getCurrentDir` involvement; + # a stray relative path is made absolute against the CWD only as a fallback. + var f = path + if not isAbsolute(f): + try: f = absolutePath(f) + except CatchableError: discard + f = normalizedPath(f) + let m = splitFile(f).name + var id = uhash(f) + result = newStringOfCap(10) + for i in 0 ..< min(m.len, PrefixLen): + result.add m[i] + # base-36 of the hash, low digit first (order is irrelevant for identity). + while id > 0'u32: + result.add Base36[int(id mod 36'u32)] + id = id div 36'u32 diff --git a/compiler/nifbackend.nim b/compiler/nifbackend.nim index 5fe36a1314..0dc218fc07 100644 --- a/compiler/nifbackend.nim +++ b/compiler/nifbackend.nim @@ -24,12 +24,22 @@ when defined(nimPreviewSlimSystem): import ast, options, lineinfos, modulegraphs, cgendata, cgen, pathutils, extccomp, msgs, modulepaths, idents, types, ast2nif, typekeys, - cnif + cnif, icmodnames from cgmeth import generateIfMethodDispatchers from transf import transformBody from injectdestructors import injectDestructorCalls import ic / replayer +proc systemNifSuffix(conf: ConfigRef): string = + ## The system module's NIF suffix, derived from `system.nim`'s path EXACTLY as + ## the frontend derives it (deps.nim's `toPair` on `libpath/system.nim`), so the + ## backend loads the very `.s.bif` the frontend wrote. It must NOT be a constant: + ## `moduleSuffix` (icmodnames) now hashes the absolute path, so the system suffix + ## is install-dependent (was hardcoded `sysma2dyk`, valid only for the old + ## relative-path scheme where `system.nim` always relativized to `system.nim`). + moduleSuffix((conf.libpath / RelativeFile"system.nim").string, + cast[seq[string]](conf.searchPaths)) + proc loadModuleDependencies(g: ModuleGraph; mainFileIdx: FileIndex; nifFiles: var seq[string]; depFlags: set[LoadFlag] = {LoadFullAst}): seq[PrecompiledModule] = @@ -253,7 +263,7 @@ proc loadBackendModules(g: ModuleGraph; mainFileIdx: FileIndex): ## and only needs each module's `(replay ...)` directives, which load anyway. resetForBackend(g) var isKnownFile = false - let systemFileIdx = registerNifSuffix(g.config, "sysma2dyk", isKnownFile) + let systemFileIdx = registerNifSuffix(g.config, systemNifSuffix(g.config), isKnownFile) g.config.m.systemFileIdx = systemFileIdx var precompSys = moduleFromNifFile(g, systemFileIdx, {AlwaysLoadInterface}) g.systemModule = precompSys.module @@ -276,7 +286,7 @@ proc loadBackendModules(g: ModuleGraph; mainFileIdx: FileIndex): # closure here too — otherwise `findTargetModule` cannot resolve their suffix. block: var visited = initHashSet[string]() - visited.incl "sysma2dyk" + visited.incl systemNifSuffix(g.config) for m in modules: visited.incl cachedModuleSuffix(g.config, FileIndex m.module.position) var stack: seq[ModuleSuffix] = @[] @@ -319,14 +329,14 @@ proc loadDepClosure(g: ModuleGraph; targetSuffix: string): ## dispatchers, runs essentially alone since every other `.c.nif` precedes it). resetForBackend(g) var isKnownFile = false - let systemFileIdx = registerNifSuffix(g.config, "sysma2dyk", isKnownFile) + let systemFileIdx = registerNifSuffix(g.config, systemNifSuffix(g.config), isKnownFile) g.config.m.systemFileIdx = systemFileIdx let precompSys = moduleFromNifFile(g, systemFileIdx, {AlwaysLoadInterface}) g.systemModule = precompSys.module var modules: seq[PrecompiledModule] = @[] var visited = initHashSet[string]() - visited.incl "sysma2dyk" + visited.incl systemNifSuffix(g.config) # Only the target is codegen'd, so only it needs its full AST; the closure is # loaded interface-only (demanded bodies come lazily from the kept-open diff --git a/compiler/nifgen.nim b/compiler/nifgen.nim index 8467a93473..76e32e59c3 100644 --- a/compiler/nifgen.nim +++ b/compiler/nifgen.nim @@ -16,7 +16,7 @@ import import "../dist/nimony/src/lib" / nifbuilder import "../dist/nimony/src/models" / nifler_tags -import "../dist/nimony/src/gear2" / modnames +import icmodnames ## This was copied from Nifler's bridge.nim. However, this code will evolve ## in a different direction as it needs to translate the semchecked AST which diff --git a/compiler/typekeys.nim b/compiler/typekeys.nim index d5e0b52e09..54092eb055 100644 --- a/compiler/typekeys.nim +++ b/compiler/typekeys.nim @@ -13,7 +13,7 @@ import std/[assertions, sets] import "../dist/nimony/src/lib" / [treemangler] -import "../dist/nimony/src/gear2" / modnames +import icmodnames import astdef, idents, options, lineinfos, msgs import ic / [enum2nif]