From 88e7adfcb78cd547668bde5485707db460e6587c Mon Sep 17 00:00:00 2001 From: Tomohiro Date: Sun, 1 Feb 2026 15:01:55 +0900 Subject: [PATCH] fixes #25459; `hashType` returns different hash from instantiated generics with distinct types (#25471) `hashType` proc returned the same hash value from different instanced generics types like `D[int64]` and `D[F]`. That caused the struct type with wrong field types. object/tuple type size check code is generated when it is compiled with `-d:checkAbi` option. --- compiler/ccgtypes.nim | 8 +++++++- compiler/sighashes.nim | 2 +- compiler/types.nim | 2 +- tests/ccgbugs2/m25459/g.nim | 11 +++++++++++ tests/ccgbugs2/m25459/h.nim | 8 ++++++++ tests/ccgbugs2/t25459.nim | 10 ++++++++++ tests/ccgbugs2/t25459b.nim | 31 +++++++++++++++++++++++++++++++ 7 files changed, 69 insertions(+), 3 deletions(-) create mode 100644 tests/ccgbugs2/m25459/g.nim create mode 100644 tests/ccgbugs2/m25459/h.nim create mode 100644 tests/ccgbugs2/t25459.nim create mode 100644 tests/ccgbugs2/t25459b.nim diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 2e619c8065..11fe701c18 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -294,7 +294,12 @@ proc cacheGetType(tab: TypeCache; sig: SigHash): Rope = result = tab.getOrDefault(sig) proc addAbiCheck(m: BModule; t: PType, name: Rope) = - if isDefined(m.config, "checkAbi") and (let size = getSize(m.config, t); size != szUnknownSize): + if isDefined(m.config, "checkAbi") and (let size = getSize(m.config, t); size != szUnknownSize) and + not (t.kind == tyObject and searchTypeFor(t, proc (t: PType): bool {.nimcall.} = t.kind == tyUncheckedArray)): + # `UncheckedArray`, not `ptr UncheckedArray` type field in object types is a flexible array. + # `sizeof` in C and Nim doesn't always return the same value for object types containing it. + # making `getSize` in Nim always returns the same value as `sizeof` in C from flexible arrays seems hard. + # See `SEQ_DECL_SIZE` in lib/nimbase.h var msg = "backend & Nim disagree on size for: " msg.addTypeHeader(m.config, t) var msg2 = "" @@ -1067,6 +1072,7 @@ proc getTypeDescAux(m: BModule; origTyp: PType, check: var IntSet; kind: TypeDes else: getTupleDesc(m, t, result, check) if not isImportedType(t): m.s[cfsTypes].add(recdesc) + addAbiCheck(m, t, result) elif tfIncompleteStruct notin t.flags: discard # addAbiCheck(m, t, result) # already handled elsewhere of tySet: diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index 5d6d0e9a5b..a4f1e00880 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -154,7 +154,7 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: Confi assert inst.kind == tyGenericInst c.hashType inst.genericHead, flags, conf for _, a in inst.genericInstParams: - c.hashType a, flags, conf + c.hashType a, flags+{CoDistinct}, conf t.typeInstImpl = inst return c &= char(t.kind) diff --git a/compiler/types.nim b/compiler/types.nim index 08c0c92dab..e18f97ff36 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -143,7 +143,7 @@ proc getFloatValue*(n: PNode): BiggestFloat = proc addTypeHeader*(result: var string, conf: ConfigRef; typ: PType; prefer: TPreferedDesc = preferMixed; getDeclarationPath = true) = result.add typeToString(typ, prefer) - if getDeclarationPath: result.addDeclaredLoc(conf, typ.sym) + if getDeclarationPath and typ.sym != nil: result.addDeclaredLoc(conf, typ.sym) proc getProcHeader*(conf: ConfigRef; sym: PSym; prefer: TPreferedDesc = preferName; getDeclarationPath = true): string = assert sym != nil diff --git a/tests/ccgbugs2/m25459/g.nim b/tests/ccgbugs2/m25459/g.nim new file mode 100644 index 0000000000..b97d4c73a4 --- /dev/null +++ b/tests/ccgbugs2/m25459/g.nim @@ -0,0 +1,11 @@ +proc v[T](_: typedesc[T]): int = + if T is int64: 6 else: 4 + +type + D*[T] = object + c*: seq[T] + k*: array[v(T), int] + F = distinct int64 + W* = object + y: D[F] + j*: D[int64] diff --git a/tests/ccgbugs2/m25459/h.nim b/tests/ccgbugs2/m25459/h.nim new file mode 100644 index 0000000000..45cf527f00 --- /dev/null +++ b/tests/ccgbugs2/m25459/h.nim @@ -0,0 +1,8 @@ +import ./g +export g + +proc a*(): W = + var e = D[int64]() + e.c.setLen(8) + e.k[1] = 0 + result = W(j: e) diff --git a/tests/ccgbugs2/t25459.nim b/tests/ccgbugs2/t25459.nim new file mode 100644 index 0000000000..a31c3c836b --- /dev/null +++ b/tests/ccgbugs2/t25459.nim @@ -0,0 +1,10 @@ +discard """ + targets: "c cpp" + matrix: "-d:checkAbi" +""" + +import ./m25459/h + +for _ in 0 ..< 500: + let u = new W + u[] = a() diff --git a/tests/ccgbugs2/t25459b.nim b/tests/ccgbugs2/t25459b.nim new file mode 100644 index 0000000000..127b4b3cc3 --- /dev/null +++ b/tests/ccgbugs2/t25459b.nim @@ -0,0 +1,31 @@ +discard """ + targets: "c cpp" + matrix: "-d:checkAbi" +""" + +proc v[T](_: typedesc[T]): int = + if T is int64: 2 else: 1 + +type + D[T] = object + k: array[v(T), int] + E[T] = object + k: array[v(T), int] + F = distinct int64 + W = object + a: D[int64] + b: D[F] + +proc csizeof[T](x {.bycopy.} : T): cint {.importc: "sizeof", nodecl.} + +var w: W +assert sizeof(w) == csizeof(w) + +var + e0: E[F] + e1: E[int64] +assert sizeof(e0) == csizeof(e0) +assert sizeof(e1) == csizeof(e1) + +var tup: (E[F], E[int64]) +assert sizeof(tup) == csizeof(tup)