From 9d47a16f6c6750d09482bf630167bbfb246a33b8 Mon Sep 17 00:00:00 2001 From: gulugulubing <413153391@qq.com> Date: Wed, 1 Jul 2026 12:41:19 -0600 Subject: [PATCH 1/5] give a codegen chance to nested template --- compiler/src/dmd/templatesem.d | 19 +++++++++++++++++++ compiler/test/compilable/test23239.d | 21 +++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 compiler/test/compilable/test23239.d diff --git a/compiler/src/dmd/templatesem.d b/compiler/src/dmd/templatesem.d index 8221c2bed6fd..c4a7b69c846f 100644 --- a/compiler/src/dmd/templatesem.d +++ b/compiler/src/dmd/templatesem.d @@ -1147,6 +1147,25 @@ void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, ArgumentList scope v = new InstMemberWalker(tempinst.inst); tempinst.inst.accept(v); + // If there are speculative nested template instances whose + // enclosing chain leads to this now-root instance, update + // their minst so they get a chance at codegen. + // https://issues.dlang.org/show_bug.cgi?id=23239 + if (auto rootModule = tempinst.inst.minst) + { + foreach (i, ref s; *rootModule.members) + { + auto nested = s.isTemplateInstance(); + if (!nested || nested.minst) + continue; + if (auto enc = nested.tempdecl.isInstantiated()) + { + if (enc.inst is tempinst.inst) + nested.minst = rootModule; + } + } + } + if (!global.params.allInst && tempinst.minst) // if inst was not speculative... { diff --git a/compiler/test/compilable/test23239.d b/compiler/test/compilable/test23239.d new file mode 100644 index 000000000000..0e6747113015 --- /dev/null +++ b/compiler/test/compilable/test23239.d @@ -0,0 +1,21 @@ +// https://github.com/dlang/dmd/issues/23239 +// static assert instantiating a template with static this() +// should not eliminate the static this() as dead code +// when the template is also used at runtime. + +template Tmpl() +{ + int data; + template touch(T) + { + static this() { data = T.sizeof; } + enum touch = true; + } +} + +int main() +{ + static assert(Tmpl!().touch!int); // CTFE instantiation + assert(Tmpl!().data == 4); // runtime use, static this() must have run + return 0; +} From c356ffd975f420da68d52d6241d636ef20048446 Mon Sep 17 00:00:00 2001 From: gulugulubing <413153391@qq.com> Date: Wed, 1 Jul 2026 14:27:39 -0600 Subject: [PATCH 2/5] add more comments --- compiler/src/dmd/templatesem.d | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/compiler/src/dmd/templatesem.d b/compiler/src/dmd/templatesem.d index c4a7b69c846f..3cac7fde0dbd 100644 --- a/compiler/src/dmd/templatesem.d +++ b/compiler/src/dmd/templatesem.d @@ -1150,6 +1150,15 @@ void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, ArgumentList // If there are speculative nested template instances whose // enclosing chain leads to this now-root instance, update // their minst so they get a chance at codegen. + // + // We can not use nested.tinst to find the enclosing instance, + // because StaticAssert::semantic2() sets sc.tinst = null + // before evaluating the condition, which causes nested + // instances to lose their tinst relationship. Instead walk + // through nested.tempdecl.parent which reliably points to the + // outer TemplateInstance (the tempdecl is a copy placed inside + // the outer instance by arraySyntaxCopy). + // // https://issues.dlang.org/show_bug.cgi?id=23239 if (auto rootModule = tempinst.inst.minst) { From 10227ee2999d39d9d101c556484a7c5ac29cedea Mon Sep 17 00:00:00 2001 From: gulugulubing <413153391@qq.com> Date: Wed, 1 Jul 2026 16:18:51 -0600 Subject: [PATCH 3/5] add a null check --- compiler/src/dmd/templatesem.d | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/src/dmd/templatesem.d b/compiler/src/dmd/templatesem.d index 3cac7fde0dbd..5f4f46ab074e 100644 --- a/compiler/src/dmd/templatesem.d +++ b/compiler/src/dmd/templatesem.d @@ -1165,7 +1165,10 @@ void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, ArgumentList foreach (i, ref s; *rootModule.members) { auto nested = s.isTemplateInstance(); - if (!nested || nested.minst) + // TemplateMixin also passes isTemplateInstance() but may + // not have tempdecl set yet while semantic analysis is in + // progress. + if (!nested || nested.minst || !nested.tempdecl) continue; if (auto enc = nested.tempdecl.isInstantiated()) { From 655772e0a9001e8bf4a440b9f00452d54a592c52 Mon Sep 17 00:00:00 2001 From: gulugulubing <413153391@qq.com> Date: Wed, 1 Jul 2026 17:35:24 -0600 Subject: [PATCH 4/5] limit the fix only to templates with static ctor/dtor --- compiler/src/dmd/templatesem.d | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/compiler/src/dmd/templatesem.d b/compiler/src/dmd/templatesem.d index 5f4f46ab074e..fd935ce4df0e 100644 --- a/compiler/src/dmd/templatesem.d +++ b/compiler/src/dmd/templatesem.d @@ -1170,6 +1170,14 @@ void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, ArgumentList // progress. if (!nested || nested.minst || !nested.tempdecl) continue; + + // Only fix instances with static ctors/dtors: if such an + // instance remains speculative, its static ctor/dtor will + // be skipped during codegen and never get a second chance + // to be emitted (unlike regular functions, which can be + // codegen'd when re-instantiated from a root module). + if (!nested.hasStaticCtorOrDtor()) + continue; if (auto enc = nested.tempdecl.isInstantiated()) { if (enc.inst is tempinst.inst) From 37171fee96e628e36864bbf99ce1fb9ab24ba02d Mon Sep 17 00:00:00 2001 From: gulugulubing <413153391@qq.com> Date: Wed, 1 Jul 2026 17:58:25 -0600 Subject: [PATCH 5/5] fix in needsCodegen() --- compiler/src/dmd/templatesem.d | 54 ++++++++++------------------------ 1 file changed, 15 insertions(+), 39 deletions(-) diff --git a/compiler/src/dmd/templatesem.d b/compiler/src/dmd/templatesem.d index fd935ce4df0e..161b2d1cebdc 100644 --- a/compiler/src/dmd/templatesem.d +++ b/compiler/src/dmd/templatesem.d @@ -1147,45 +1147,6 @@ void templateInstanceSemantic(TemplateInstance tempinst, Scope* sc, ArgumentList scope v = new InstMemberWalker(tempinst.inst); tempinst.inst.accept(v); - // If there are speculative nested template instances whose - // enclosing chain leads to this now-root instance, update - // their minst so they get a chance at codegen. - // - // We can not use nested.tinst to find the enclosing instance, - // because StaticAssert::semantic2() sets sc.tinst = null - // before evaluating the condition, which causes nested - // instances to lose their tinst relationship. Instead walk - // through nested.tempdecl.parent which reliably points to the - // outer TemplateInstance (the tempdecl is a copy placed inside - // the outer instance by arraySyntaxCopy). - // - // https://issues.dlang.org/show_bug.cgi?id=23239 - if (auto rootModule = tempinst.inst.minst) - { - foreach (i, ref s; *rootModule.members) - { - auto nested = s.isTemplateInstance(); - // TemplateMixin also passes isTemplateInstance() but may - // not have tempdecl set yet while semantic analysis is in - // progress. - if (!nested || nested.minst || !nested.tempdecl) - continue; - - // Only fix instances with static ctors/dtors: if such an - // instance remains speculative, its static ctor/dtor will - // be skipped during codegen and never get a second chance - // to be emitted (unlike regular functions, which can be - // codegen'd when re-instantiated from a root module). - if (!nested.hasStaticCtorOrDtor()) - continue; - if (auto enc = nested.tempdecl.isInstantiated()) - { - if (enc.inst is tempinst.inst) - nested.minst = rootModule; - } - } - } - if (!global.params.allInst && tempinst.minst) // if inst was not speculative... { @@ -3401,6 +3362,21 @@ bool needsCodegen(TemplateInstance ti) return true; } + // A speculative instance that contains static ctors/dtors will + // lose them if we skip codegen. Normally tinst propagates minst + // from parent to child, but tinst can be null when the instance + // was created inside StaticAssert (which clears sc.tinst). + // Walk the tempdecl parent chain as a fallback. + // https://issues.dlang.org/show_bug.cgi?id=23239 + if (!ti.minst && ti.hasStaticCtorOrDtor() && ti.tempdecl) + { + if (auto enc = ti.tempdecl.isInstantiated()) + { + if (enc.inst && enc.inst.needsCodegen()) + ti.minst = enc.inst.minst; + } + } + if (global.params.allInst) { // Do codegen if there is an instantiation from a root module, to maximize link-ability.