mirror of
https://github.com/nim-lang/Nim.git
synced 2026-09-02 03:43:41 +00:00
The rule deciding which integer identifies a backend-minted symbol —
content-derived `disamb` for a lifted hook (`setHookDisamb`), `itemId.item`
otherwise — was written out at three sites: `mangleProcNameExt` and
`ccgutils.makeUnique` for the C name, `ast2nif.toNifSymName` for the NIF name,
each carrying its own copy of the ten-line rationale.
They drifted, which is exactly cce17461d: `toNifSymName` lacked the hook
exception, so the loader overwrote a content-derived value and two unrelated
`=destroy` hooks collided on one C name (C accepted the mistyped call, C++
rejected it). `astdef.backendMintedDisamb` is now the single definition and all
three call it. `globalName` still reads `disamb` directly — correct for a loaded
symbol, and the round-trip invariant that makes it agree is now stated in the
shared function instead of left implicit.
Pure de-duplication, verified as such: all 219 generated `.c` files of a
219-module corpus and the linked binary are byte-identical to the previous
commit. Plus 16/16 metamorphic IC tests, the 17-file `koch ic` suite, 13/13
differential edit checks against `nim c`, and the debug and release `bootic`
fixed points.
Note for the record: this started as an attempt to replace the per-process
`_c<itemId.item>` counter with a content hash. Instrumenting both mangling
sites showed that branch is never taken — 0 of 166 backend-minted manglings on
the corpus, 0 of 257 on the compiler, all going through the content-derived
path — so rewriting the scheme would have shifted every backend-minted C name
and forced an `icFormatVersion` bump for no demonstrable benefit.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
82 lines
3.0 KiB
Nim
82 lines
3.0 KiB
Nim
import std/strutils
|
|
import ast, modulegraphs
|
|
|
|
proc mangle*(name: string): string =
|
|
result = newStringOfCap(name.len)
|
|
var start = 0
|
|
if name[0] in Digits:
|
|
result.add("X" & name[0])
|
|
start = 1
|
|
var requiresUnderscore = false
|
|
template special(x) =
|
|
result.add x
|
|
requiresUnderscore = true
|
|
for i in start..<name.len:
|
|
let c = name[i]
|
|
case c
|
|
of 'a'..'z', '0'..'9', 'A'..'Z':
|
|
result.add(c)
|
|
of '_':
|
|
# we generate names like 'foo_9' for scope disambiguations and so
|
|
# disallow this here:
|
|
if i > 0 and i < name.len-1 and name[i+1] in Digits:
|
|
discard
|
|
else:
|
|
result.add(c)
|
|
of '$': special "dollar"
|
|
of '%': special "percent"
|
|
of '&': special "amp"
|
|
of '^': special "roof"
|
|
of '!': special "emark"
|
|
of '?': special "qmark"
|
|
of '*': special "star"
|
|
of '+': special "plus"
|
|
of '-': special "minus"
|
|
of '/': special "slash"
|
|
of '\\': special "backslash"
|
|
of '=': special "eq"
|
|
of '<': special "lt"
|
|
of '>': special "gt"
|
|
of '~': special "tilde"
|
|
of ':': special "colon"
|
|
of '.': special "dot"
|
|
of '@': special "at"
|
|
of '|': special "bar"
|
|
else:
|
|
result.add("X" & toHex(ord(c), 2))
|
|
requiresUnderscore = true
|
|
if requiresUnderscore:
|
|
result.add "_"
|
|
|
|
proc mangleParamExt*(s: PSym): string =
|
|
result = "_p"
|
|
result.addInt s.position
|
|
|
|
proc mangleProcNameExt*(graph: ModuleGraph, s: PSym): string =
|
|
# The disambiguator comes first and the module suffix LAST, so the suffix is
|
|
# a strippable trailing token: content-addressed cross-module merging chops
|
|
# everything from the final `__` to recover a mint-site-independent name.
|
|
if s.itemId.isBackendMinted:
|
|
# A symbol minted during IC codegen (`idGeneratorForBackend`): its idgen
|
|
# starts with an EMPTY per-name disamb table, so its `disamb` restarts at 0
|
|
# and collides with same-named sem-time symbols loaded from NIFs (two
|
|
# `=destroy` hooks both mangling to `_u2` → "conflicting types for ..." in
|
|
# the generated C). The `_c` marker keeps the namespace disjoint from
|
|
# `_u<disamb>`; `backendMintedDisamb` (astdef) is the ONE definition of which
|
|
# integer identifies such a symbol, shared with `ccgutils.makeUnique` and
|
|
# `ast2nif.toNifSymName` so the C name and the NIF name cannot drift apart.
|
|
result = "_c"
|
|
result.addInt backendMintedDisamb(s)
|
|
else:
|
|
result = "_u"
|
|
# Use `disamb` rather than `itemId.item`: under incremental compilation a
|
|
# symbol loaded from a NIF file gets a fresh, load-order-dependent `itemId.item`
|
|
# (from the per-module symbol counter), which is neither stable across the
|
|
# processes that compile vs. use a module nor guaranteed distinct from another
|
|
# loaded symbol's. `disamb` is assigned deterministically per (module, name)
|
|
# and, together with the already-prepended mangled name, yields a unique and
|
|
# stable C identifier.
|
|
result.addInt s.disamb
|
|
result.add "__"
|
|
result.add graph.ifaces[s.itemId.module].uniqueName
|