diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index c24d4124d9fc5..d85d04d2a4d53 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -4521,6 +4521,23 @@ class RecordDecl : public TagDecl { return field_begin() == field_end(); } + /// noload_fields - Iterate over the fields stored in this record + /// that are currently loaded; don't attempt to retrieve anything + /// from an external source. + field_range noload_fields() const { + return field_range(noload_field_begin(), noload_field_end()); + } + + field_iterator noload_field_begin() const; + field_iterator noload_field_end() const { + return field_iterator(decl_iterator()); + } + + // Whether there are any fields (non-static data members) in this record. + bool noload_field_empty() const { + return noload_field_begin() == noload_field_end(); + } + /// Note that the definition of this type is now complete. virtual void completeDefinition(); diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index d8dffb7f5dc43..cd8e495e82c80 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -5161,6 +5161,10 @@ RecordDecl::field_iterator RecordDecl::field_begin() const { return field_iterator(decl_iterator(FirstDecl)); } +RecordDecl::field_iterator RecordDecl::noload_field_begin() const { + return field_iterator(decl_iterator(getDefinitionOrSelf()->FirstDecl)); +} + /// completeDefinition - Notes that the definition of this type is now /// complete. void RecordDecl::completeDefinition() { diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 4356f2b734fb0..cf32d4f56b7c2 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -3432,7 +3432,19 @@ NamedDecl *ASTDeclReader::getAnonymousDeclForMerging(ASTReader &Reader, // If this is the first time, but we have parsed a declaration of the context, // build the anonymous declaration list from the parsed declaration. auto *PrimaryDC = getPrimaryDCForAnonymousDecl(DC); - if (PrimaryDC && !cast(PrimaryDC)->isFromASTFile()) { + auto needToNumberAnonymousDeclsWithin = [](Decl *D) { + if (!D->isFromASTFile()) + return true; + // If this is a class template specialization from an AST file, has at least + // one field, but none of the fields have been loaded from external storage, + // this is a situation where the class template specialization decl + // was imported but the definition was instantiated within the source. + // In such a case, we still need to number the anonymous decls. + auto *CTSD = dyn_cast(D); + return CTSD && !CTSD->noload_field_empty() && + !CTSD->hasLoadedFieldsFromExternalStorage(); + }; + if (PrimaryDC && needToNumberAnonymousDeclsWithin(cast(PrimaryDC))) { numberAnonymousDeclsWithin(PrimaryDC, [&](NamedDecl *ND, unsigned Number) { if (Previous.size() == Number) Previous.push_back(cast(ND->getCanonicalDecl())); diff --git a/clang/test/Modules/merge-anon-in-template-2.cpp b/clang/test/Modules/merge-anon-in-template-2.cpp new file mode 100644 index 0000000000000..15852f0120798 --- /dev/null +++ b/clang/test/Modules/merge-anon-in-template-2.cpp @@ -0,0 +1,47 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t + +// RUN: %clang_cc1 -std=c++20 -fmodule-name=hu-01 -emit-header-unit -xc++-user-header %t/hu-01.h \ +// RUN: -o %t/hu-01.pcm + +// RUN: %clang_cc1 -std=c++20 -fmodule-name=hu-02 -emit-header-unit -xc++-user-header %t/hu-02.h \ +// RUN: -Wno-experimental-header-units \ +// RUN: -fmodule-map-file=%t/hu-01.map -fmodule-file=hu-01=%t/hu-01.pcm \ +// RUN: -o %t/hu-02.pcm + +// RUN: %clang_cc1 -std=c++20 -emit-obj %t/main.cpp \ +// RUN: -Wno-experimental-header-units \ +// RUN: -fmodule-map-file=%t/hu-01.map -fmodule-file=hu-01=%t/hu-01.pcm \ +// RUN: -fmodule-map-file=%t/hu-02.map -fmodule-file=hu-02=%t/hu-02.pcm + +//--- hu-01.map +module "hu-01" { + header "hu-01.h" + export * +} + +//--- hu-02.map +module "hu-02" { + header "hu-02.h" + export * +} + +//--- hu-01.h +template +struct S { union { T x; }; }; + +using SI = S; + +//--- hu-02.h +template +struct S { union { T x; }; }; + +inline void f(S s = {}) { s.x; } + +//--- main.cpp +import "hu-01.h"; +void g(S) {} + +import "hu-02.h"; +void h() { f(); } diff --git a/clang/test/Modules/merge-anon-in-template-3.cpp b/clang/test/Modules/merge-anon-in-template-3.cpp new file mode 100644 index 0000000000000..1ee447e3e524d --- /dev/null +++ b/clang/test/Modules/merge-anon-in-template-3.cpp @@ -0,0 +1,45 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t + +// RUN: %clang_cc1 -std=c++20 -fmodule-name=hu-01 -emit-header-unit -xc++-user-header %t/hu-01.h \ +// RUN: -o %t/hu-01.pcm + +// RUN: %clang_cc1 -std=c++20 -fmodule-name=hu-02 -emit-header-unit -xc++-user-header %t/hu-02.h \ +// RUN: -Wno-experimental-header-units \ +// RUN: -fmodule-map-file=%t/hu-01.map -fmodule-file=hu-01=%t/hu-01.pcm \ +// RUN: -o %t/hu-02.pcm + +// RUN: %clang_cc1 -std=c++20 -emit-obj %t/main.cpp \ +// RUN: -Wno-experimental-header-units \ +// RUN: -fmodule-map-file=%t/hu-01.map -fmodule-file=hu-01=%t/hu-01.pcm \ +// RUN: -fmodule-map-file=%t/hu-02.map -fmodule-file=hu-02=%t/hu-02.pcm + +//--- hu-01.map +module "hu-01" { + header "hu-01.h" + export * +} + +//--- hu-02.map +module "hu-02" { + header "hu-02.h" + export * +} + +//--- hu-01.h +template +struct S { union { T x; }; }; + +using SI = S; + +//--- hu-02.h +import "hu-01.h"; +inline void f(S s = {}) { s.x; } + +//--- main.cpp +import "hu-01.h"; +void g(S) {} + +import "hu-02.h"; +void h() { f(); }