Skip to content

Commit a2efa7a

Browse files
authored
[C++20][Modules] Fix merging of anonymous members of class templates. (#155948)
1 parent da71e3c commit a2efa7a

File tree

5 files changed

+126
-1
lines changed

5 files changed

+126
-1
lines changed

clang/include/clang/AST/Decl.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4521,6 +4521,23 @@ class RecordDecl : public TagDecl {
45214521
return field_begin() == field_end();
45224522
}
45234523

4524+
/// noload_fields - Iterate over the fields stored in this record
4525+
/// that are currently loaded; don't attempt to retrieve anything
4526+
/// from an external source.
4527+
field_range noload_fields() const {
4528+
return field_range(noload_field_begin(), noload_field_end());
4529+
}
4530+
4531+
field_iterator noload_field_begin() const;
4532+
field_iterator noload_field_end() const {
4533+
return field_iterator(decl_iterator());
4534+
}
4535+
4536+
// Whether there are any fields (non-static data members) in this record.
4537+
bool noload_field_empty() const {
4538+
return noload_field_begin() == noload_field_end();
4539+
}
4540+
45244541
/// Note that the definition of this type is now complete.
45254542
virtual void completeDefinition();
45264543

clang/lib/AST/Decl.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5161,6 +5161,10 @@ RecordDecl::field_iterator RecordDecl::field_begin() const {
51615161
return field_iterator(decl_iterator(FirstDecl));
51625162
}
51635163

5164+
RecordDecl::field_iterator RecordDecl::noload_field_begin() const {
5165+
return field_iterator(decl_iterator(getDefinitionOrSelf()->FirstDecl));
5166+
}
5167+
51645168
/// completeDefinition - Notes that the definition of this type is now
51655169
/// complete.
51665170
void RecordDecl::completeDefinition() {

clang/lib/Serialization/ASTReaderDecl.cpp

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3432,7 +3432,19 @@ NamedDecl *ASTDeclReader::getAnonymousDeclForMerging(ASTReader &Reader,
34323432
// If this is the first time, but we have parsed a declaration of the context,
34333433
// build the anonymous declaration list from the parsed declaration.
34343434
auto *PrimaryDC = getPrimaryDCForAnonymousDecl(DC);
3435-
if (PrimaryDC && !cast<Decl>(PrimaryDC)->isFromASTFile()) {
3435+
auto needToNumberAnonymousDeclsWithin = [](Decl *D) {
3436+
if (!D->isFromASTFile())
3437+
return true;
3438+
// If this is a class template specialization from an AST file, has at least
3439+
// one field, but none of the fields have been loaded from external storage,
3440+
// this is a situation where the class template specialization decl
3441+
// was imported but the definition was instantiated within the source.
3442+
// In such a case, we still need to number the anonymous decls.
3443+
auto *CTSD = dyn_cast<ClassTemplateSpecializationDecl>(D);
3444+
return CTSD && !CTSD->noload_field_empty() &&
3445+
!CTSD->hasLoadedFieldsFromExternalStorage();
3446+
};
3447+
if (PrimaryDC && needToNumberAnonymousDeclsWithin(cast<Decl>(PrimaryDC))) {
34363448
numberAnonymousDeclsWithin(PrimaryDC, [&](NamedDecl *ND, unsigned Number) {
34373449
if (Previous.size() == Number)
34383450
Previous.push_back(cast<NamedDecl>(ND->getCanonicalDecl()));
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// RUN: rm -rf %t
2+
// RUN: mkdir -p %t
3+
// RUN: split-file %s %t
4+
5+
// RUN: %clang_cc1 -std=c++20 -fmodule-name=hu-01 -emit-header-unit -xc++-user-header %t/hu-01.h \
6+
// RUN: -o %t/hu-01.pcm
7+
8+
// RUN: %clang_cc1 -std=c++20 -fmodule-name=hu-02 -emit-header-unit -xc++-user-header %t/hu-02.h \
9+
// RUN: -Wno-experimental-header-units \
10+
// RUN: -fmodule-map-file=%t/hu-01.map -fmodule-file=hu-01=%t/hu-01.pcm \
11+
// RUN: -o %t/hu-02.pcm
12+
13+
// RUN: %clang_cc1 -std=c++20 -emit-obj %t/main.cpp \
14+
// RUN: -Wno-experimental-header-units \
15+
// RUN: -fmodule-map-file=%t/hu-01.map -fmodule-file=hu-01=%t/hu-01.pcm \
16+
// RUN: -fmodule-map-file=%t/hu-02.map -fmodule-file=hu-02=%t/hu-02.pcm
17+
18+
//--- hu-01.map
19+
module "hu-01" {
20+
header "hu-01.h"
21+
export *
22+
}
23+
24+
//--- hu-02.map
25+
module "hu-02" {
26+
header "hu-02.h"
27+
export *
28+
}
29+
30+
//--- hu-01.h
31+
template <typename T>
32+
struct S { union { T x; }; };
33+
34+
using SI = S<int>;
35+
36+
//--- hu-02.h
37+
template <typename T>
38+
struct S { union { T x; }; };
39+
40+
inline void f(S<int> s = {}) { s.x; }
41+
42+
//--- main.cpp
43+
import "hu-01.h";
44+
void g(S<int>) {}
45+
46+
import "hu-02.h";
47+
void h() { f(); }
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// RUN: rm -rf %t
2+
// RUN: mkdir -p %t
3+
// RUN: split-file %s %t
4+
5+
// RUN: %clang_cc1 -std=c++20 -fmodule-name=hu-01 -emit-header-unit -xc++-user-header %t/hu-01.h \
6+
// RUN: -o %t/hu-01.pcm
7+
8+
// RUN: %clang_cc1 -std=c++20 -fmodule-name=hu-02 -emit-header-unit -xc++-user-header %t/hu-02.h \
9+
// RUN: -Wno-experimental-header-units \
10+
// RUN: -fmodule-map-file=%t/hu-01.map -fmodule-file=hu-01=%t/hu-01.pcm \
11+
// RUN: -o %t/hu-02.pcm
12+
13+
// RUN: %clang_cc1 -std=c++20 -emit-obj %t/main.cpp \
14+
// RUN: -Wno-experimental-header-units \
15+
// RUN: -fmodule-map-file=%t/hu-01.map -fmodule-file=hu-01=%t/hu-01.pcm \
16+
// RUN: -fmodule-map-file=%t/hu-02.map -fmodule-file=hu-02=%t/hu-02.pcm
17+
18+
//--- hu-01.map
19+
module "hu-01" {
20+
header "hu-01.h"
21+
export *
22+
}
23+
24+
//--- hu-02.map
25+
module "hu-02" {
26+
header "hu-02.h"
27+
export *
28+
}
29+
30+
//--- hu-01.h
31+
template <typename T>
32+
struct S { union { T x; }; };
33+
34+
using SI = S<int>;
35+
36+
//--- hu-02.h
37+
import "hu-01.h";
38+
inline void f(S<int> s = {}) { s.x; }
39+
40+
//--- main.cpp
41+
import "hu-01.h";
42+
void g(S<int>) {}
43+
44+
import "hu-02.h";
45+
void h() { f(); }

0 commit comments

Comments
 (0)