From b0e9b27f5b7ad1c7261c68ad63c2b8463d74649b Mon Sep 17 00:00:00 2001 From: Dennis Korpel Date: Fri, 28 Aug 2026 13:17:11 +0200 Subject: [PATCH] Fix #23731 - undefined __xopEquals/__xtoHash for instantiated struct 786dc71ad7 stopped emitting the special TypeInfo members alongside the TypeInfo of a template-instantiated struct whose own codegen was elided, assuming they are always emitted with the StructDeclaration elsewhere. That does not hold when `ti.minst` is null (a speculative instance whose TypeInfo was requested anyway) or a non-root module that is not part of the compilation: nothing emits `TemplateInstance.toObjFile()`, so the TypeInfo emitted here is left referencing symbols no object file defines. Restore emitting them next to the TypeInfo, as before 2.113. Co-Authored-By: Claude Opus 5 --- compiler/src/dmd/glue/todt.d | 19 +++++++++++++++++++ compiler/test/runnable/imports/test23731a.d | 8 ++++++++ compiler/test/runnable/test23731.d | 12 ++++++++++++ 3 files changed, 39 insertions(+) create mode 100644 compiler/test/runnable/imports/test23731a.d create mode 100644 compiler/test/runnable/test23731.d diff --git a/compiler/src/dmd/glue/todt.d b/compiler/src/dmd/glue/todt.d index 14f797eac16f..38c48e7cfd4d 100644 --- a/compiler/src/dmd/glue/todt.d +++ b/compiler/src/dmd/glue/todt.d @@ -1471,6 +1471,25 @@ private extern (C++) class TypeInfoDtVisitor : Visitor semanticTypeInfoMembers(sd); } + if (TemplateInstance ti = sd.isInstantiated()) + { + if (!ti.needsCodegen()) + { + if (sd.xeq && sd.xeq != StructDeclaration.xerreq) + toObjFile(sd.xeq, global.params.multiobj); + if (sd.xcmp && sd.xcmp != StructDeclaration.xerrcmp) + toObjFile(sd.xcmp, global.params.multiobj); + if (FuncDeclaration ftostr = search_toString(sd)) + toObjFile(ftostr, global.params.multiobj); + if (sd.xhash) + toObjFile(sd.xhash, global.params.multiobj); + if (sd.postblit) + toObjFile(sd.postblit, global.params.multiobj); + if (sd.dtor) + toObjFile(sd.dtor, global.params.multiobj); + } + } + /* Put out: * char[] mangledName; * void[] init; diff --git a/compiler/test/runnable/imports/test23731a.d b/compiler/test/runnable/imports/test23731a.d new file mode 100644 index 000000000000..c0386f30a8dc --- /dev/null +++ b/compiler/test/runnable/imports/test23731a.d @@ -0,0 +1,8 @@ +module imports.test23731a; + +struct Wrapper(T) +{ + T[] values; +} + +enum wrapperIntSize = Wrapper!int.sizeof; diff --git a/compiler/test/runnable/test23731.d b/compiler/test/runnable/test23731.d new file mode 100644 index 000000000000..8ed17ebb80f2 --- /dev/null +++ b/compiler/test/runnable/test23731.d @@ -0,0 +1,12 @@ +// https://github.com/dlang/dmd/issues/23731 + +import imports.test23731a; + +void main() +{ + auto ti = typeid(Wrapper!int); + auto a = Wrapper!int([1, 2]); + auto b = Wrapper!int([1, 2]); + assert(ti.equals(&a, &b)); + assert(ti.getHash(&a) == ti.getHash(&b)); +}