https://github.com/ipopov updated https://github.com/llvm/llvm-project/pull/202248
>From d7ccaf5c75dcba9e1094eea1c7d80b072d7065d0 Mon Sep 17 00:00:00 2001 From: ipopov <[email protected]> Date: Fri, 26 Jun 2026 15:28:02 -0700 Subject: [PATCH] [Modules] Skip anonymous declaration numbering for local tags in dependent contexts Local tag declarations (classes, structs, enums, and lambdas) defined within function template bodies or class templates do not require ODR merging at the local declaration level across different modules. ODR consistency is already guaranteed because the instantiator only walks the canonical template definition body, which instantiates its own copy of the local class. Merging them across different modules causes their member definitions (like methods or call operators) to be canonicalized to one module, while the instantiator walks the template body from another module. Since local variables within template bodies are not merged, this mismatch leads to assertion crashes during template instantiation in `LocalInstantiationScope::findInstantiationOf`. Fixes https://github.com/llvm/llvm-project/issues/206203. --- clang/lib/Serialization/ASTCommon.cpp | 8 +- .../modules-lambda-dependent-crash.cppm | 73 +++++++++++++++++++ .../modules-local-class-dependent-crash.cppm | 70 ++++++++++++++++++ 3 files changed, 149 insertions(+), 2 deletions(-) create mode 100644 clang/test/Modules/modules-lambda-dependent-crash.cppm create mode 100644 clang/test/Modules/modules-local-class-dependent-crash.cppm diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp index 49e6fe8004cec..9b454428457e2 100644 --- a/clang/lib/Serialization/ASTCommon.cpp +++ b/clang/lib/Serialization/ASTCommon.cpp @@ -493,9 +493,13 @@ bool serialization::needsAnonymousDeclarationNumber(const NamedDecl *D) { // At block scope, we number everything that we need to deduplicate, since we // can't just use name matching to keep things lined up. - // FIXME: This is only necessary for an inline function or a template or - // similar. + // FIXME: This is only necessary for an inline function or + // a template specialization or similar. if (D->getLexicalDeclContext()->isFunctionOrMethod()) { + // An uninstantiated template pattern is never emitted; only its + // instantiations are numbered, so its decls need no cross-module number. + if (D->getLexicalDeclContext()->isDependentContext()) + return false; if (auto *VD = dyn_cast<VarDecl>(D)) return VD->isStaticLocal(); // FIXME: What about CapturedDecls (and declarations nested within them)? diff --git a/clang/test/Modules/modules-lambda-dependent-crash.cppm b/clang/test/Modules/modules-lambda-dependent-crash.cppm new file mode 100644 index 0000000000000..a0701b54c1989 --- /dev/null +++ b/clang/test/Modules/modules-lambda-dependent-crash.cppm @@ -0,0 +1,73 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// RUN: cd %t +// +// RUN: %clang_cc1 -std=c++20 -I. m_template.cppm -emit-reduced-module-interface \ +// RUN: -o m_template.pcm +// +// RUN: %clang_cc1 -std=c++20 -I. m_wrapper.cppm \ +// RUN: -emit-reduced-module-interface \ +// RUN: -fmodule-file=m_template=m_template.pcm \ +// RUN: -o m_wrapper.pcm +// +// RUN: %clang_cc1 -std=c++20 -I. main.cpp \ +// RUN: -fmodule-file=m_template=m_template.pcm \ +// RUN: -fmodule-file=m_wrapper=m_wrapper.pcm \ +// RUN: -fsyntax-only -verify + +//--- template_class.h +#pragma once + +class TemplateClass { + public: + template <typename U> + auto f1(U u); +}; + +template <typename U> +auto TemplateClass::f1(U u) { + U z = u; + return [=](auto) { + (void)z; + }; +} + +template <typename U> +auto trigger_ast_deserialization(U u) { + return []() {}; +} + +//--- m_template.cppm +module; +#include "template_class.h" +export module m_template; +export using ::TemplateClass; +export using ::trigger_ast_deserialization; + +//--- m_wrapper.cppm +module; +#include "template_class.h" +export module m_wrapper; +import m_template; + +export inline void TriggerInstantiation() { + TemplateClass tc; + tc.f1(0); + trigger_ast_deserialization(0); +} + +//--- main.cpp +// expected-no-diagnostics +import m_template; +import m_wrapper; +#include "template_class.h" + +struct Token {}; + +int main() { + TriggerInstantiation(); + trigger_ast_deserialization(Token{}); + TemplateClass tc; + tc.f1(Token{}); +} diff --git a/clang/test/Modules/modules-local-class-dependent-crash.cppm b/clang/test/Modules/modules-local-class-dependent-crash.cppm new file mode 100644 index 0000000000000..8516175a2b063 --- /dev/null +++ b/clang/test/Modules/modules-local-class-dependent-crash.cppm @@ -0,0 +1,70 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// RUN: cd %t +// +// RUN: %clang_cc1 -std=c++20 -I. m_template.cppm -emit-reduced-module-interface \ +// RUN: -o m_template.pcm +// +// RUN: %clang_cc1 -std=c++20 -I. m_wrapper.cppm \ +// RUN: -emit-reduced-module-interface \ +// RUN: -fmodule-file=m_template=m_template.pcm \ +// RUN: -o m_wrapper.pcm +// +// RUN: %clang_cc1 -std=c++20 -I. main.cpp \ +// RUN: -fmodule-file=m_template=m_template.pcm \ +// RUN: -fmodule-file=m_wrapper=m_wrapper.pcm \ +// RUN: -fsyntax-only -verify + +//--- local_class.h +#pragma once + +template <typename U> +auto make(U u) { + static int counter; + struct Box { + U value; + int id() const { return counter; } // references the enclosing static local + U unwrap() const { return value; } + }; + return Box{u}; +} + +template <typename U> +auto trigger_ast_deserialization(U u) { + return []() {}; +} + +//--- m_template.cppm +module; +#include "local_class.h" +export module m_template; +export using ::make; +export using ::trigger_ast_deserialization; + +//--- m_wrapper.cppm +module; +#include "local_class.h" +export module m_wrapper; +import m_template; + +export inline void TriggerInstantiation() { + make(0); + trigger_ast_deserialization(0); +} + +//--- main.cpp +// expected-no-diagnostics +import m_template; +import m_wrapper; +#include "local_class.h" + +struct Token {}; + +int main() { + TriggerInstantiation(); + trigger_ast_deserialization(Token{}); + auto b = make(Token{}); + (void)b.unwrap(); + (void)b.id(); +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
