From 056eeeae3042d7db195fc7da9926db2568cdab15 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 25 Jun 2026 16:19:13 +0800 Subject: [PATCH] fixes #25931; type N {.importc: "const void *".} = pointer creates order-dependent compilation failure (#25936) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fixes #25931 This fix addresses bug #25931 — a signature hash collision with importc-aliased pointer/cstring types. The problem: In compiler/sighashes.nim, the hashType procedure canonicalizes types for signature hashing. Integral types (tyInt..tyUInt64, etc.) were excluded from canonicalization so that types like pid_t (an importc alias) keep their backend spelling. But pointer and cstring types with importc annotations were not excluded — meaning a type like: type N {.importc: "const void *".} = pointer ...would be collapsed to just pointer in the signature hash, causing collisions when N and pointer are both used in procedure types within the same compilation unit. The fix (compiler/sighashes.nim:193): Adds tyPointer and tyCstring to the list of types that skip canonicalization, right alongside the integral types. The comment is updated to clarify this applies to "builtin scalar-ish / pointer-like types". The test (tests/ccgbugs/tsighash_typename_regression.nim:33-42): Adds a regression test exercising the exact scenario — an importc pointer alias used in both an object field type and a proc parameter type. --- compiler/sighashes.nim | 8 ++++---- tests/ccgbugs/tsighash_typename_regression.nim | 10 ++++++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index d6db0b0fa0..7705ff62d9 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -190,9 +190,10 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi if CoConsiderOwned in flags: c &= char(t.kind) c.hashType t.skipModifier, flags, conf - of tyBool, tyChar, tyInt..tyUInt64: - # no canonicalization for integral types, so that e.g. ``pid_t`` is - # produced instead of ``NI``: + of tyBool, tyChar, tyPointer, tyCstring, tyInt..tyUInt64: + # no canonicalization for builtin scalar-ish / pointer-like types, so + # that e.g. ``pid_t`` or an imported ``pointer`` alias keep their + # backend spelling instead of collapsing into the generic Nim builtin: c &= char(t.kind) if t.sym != nil and {sfImportc, sfExportc} * t.sym.flags != {}: c.hashSym(t.sym) @@ -533,4 +534,3 @@ proc idOrSig*(s: PSym, currentModule: string, if counter != 0: result.add "_" & rope(counter+1) sigCollisions.inc(sig) - diff --git a/tests/ccgbugs/tsighash_typename_regression.nim b/tests/ccgbugs/tsighash_typename_regression.nim index b93eebd204..e9aa7df712 100644 --- a/tests/ccgbugs/tsighash_typename_regression.nim +++ b/tests/ccgbugs/tsighash_typename_regression.nim @@ -30,3 +30,13 @@ block: var x = M(x: 1) doAssert(x.x == 1) + +block: # bug #25931 + type + N {.importc: "const void *".} = pointer + S = object + f: proc (_: N) {.cdecl.} + #var _: proc (_: pointer) {.cdecl.} + proc d(_: proc (_: pointer) {.cdecl.}) = discard + discard S() + d(proc (_: pointer) {.cdecl.} = discard)