Skip to content

Commit e1aaee7

Browse files
authored
[modules] Handle friend function that was a definition but became only a declaration during AST deserialization (#132214)
Fix for regression #130917, changes in #111992 were too broad. This change reduces scope of previous fix. Added `ExternalASTSource::wasThisDeclarationADefinition` to detect cases when FunctionDecl lost body due to declaration merges.
1 parent 73e1710 commit e1aaee7

10 files changed

+98
-5
lines changed

Diff for: clang/include/clang/AST/ExternalASTSource.h

+4
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,10 @@ class ExternalASTSource : public RefCountedBase<ExternalASTSource> {
191191

192192
virtual ExtKind hasExternalDefinitions(const Decl *D);
193193

194+
/// True if this function declaration was a definition before in its own
195+
/// module.
196+
virtual bool wasThisDeclarationADefinition(const FunctionDecl *FD);
197+
194198
/// Finds all declarations lexically contained within the given
195199
/// DeclContext, after applying an optional filter predicate.
196200
///

Diff for: clang/include/clang/Sema/MultiplexExternalSemaSource.h

+2
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,8 @@ class MultiplexExternalSemaSource : public ExternalSemaSource {
9292

9393
ExtKind hasExternalDefinitions(const Decl *D) override;
9494

95+
bool wasThisDeclarationADefinition(const FunctionDecl *FD) override;
96+
9597
/// Find all declarations with the given name in the
9698
/// given context.
9799
bool FindExternalVisibleDeclsByName(const DeclContext *DC,

Diff for: clang/include/clang/Serialization/ASTReader.h

+6
Original file line numberDiff line numberDiff line change
@@ -1392,6 +1392,10 @@ class ASTReader
13921392

13931393
llvm::DenseMap<const Decl *, bool> DefinitionSource;
13941394

1395+
/// Friend functions that were defined but might have had their bodies
1396+
/// removed.
1397+
llvm::DenseSet<const FunctionDecl *> ThisDeclarationWasADefinitionSet;
1398+
13951399
bool shouldDisableValidationForFile(const serialization::ModuleFile &M) const;
13961400

13971401
/// Reads a statement from the specified cursor.
@@ -2374,6 +2378,8 @@ class ASTReader
23742378

23752379
ExtKind hasExternalDefinitions(const Decl *D) override;
23762380

2381+
bool wasThisDeclarationADefinition(const FunctionDecl *FD) override;
2382+
23772383
/// Retrieve a selector from the given module with its local ID
23782384
/// number.
23792385
Selector getLocalSelector(ModuleFile &M, unsigned LocalID);

Diff for: clang/lib/AST/ExternalASTSource.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ ExternalASTSource::hasExternalDefinitions(const Decl *D) {
3838
return EK_ReplyHazy;
3939
}
4040

41+
bool ExternalASTSource::wasThisDeclarationADefinition(const FunctionDecl *FD) {
42+
return false;
43+
}
44+
4145
void ExternalASTSource::FindFileRegionDecls(FileID File, unsigned Offset,
4246
unsigned Length,
4347
SmallVectorImpl<Decl *> &Decls) {}

Diff for: clang/lib/Sema/MultiplexExternalSemaSource.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,14 @@ MultiplexExternalSemaSource::hasExternalDefinitions(const Decl *D) {
107107
return EK_ReplyHazy;
108108
}
109109

110+
bool MultiplexExternalSemaSource::wasThisDeclarationADefinition(
111+
const FunctionDecl *FD) {
112+
for (const auto &S : Sources)
113+
if (S->wasThisDeclarationADefinition(FD))
114+
return true;
115+
return false;
116+
}
117+
110118
bool MultiplexExternalSemaSource::FindExternalVisibleDeclsByName(
111119
const DeclContext *DC, DeclarationName Name,
112120
const DeclContext *OriginalDC) {

Diff for: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

+7-5
Original file line numberDiff line numberDiff line change
@@ -2604,11 +2604,13 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
26042604
// Friend function defined withing class template may stop being function
26052605
// definition during AST merges from different modules, in this case decl
26062606
// with function body should be used for instantiation.
2607-
if (isFriend) {
2608-
const FunctionDecl *Defn = nullptr;
2609-
if (D->hasBody(Defn)) {
2610-
D = const_cast<FunctionDecl *>(Defn);
2611-
FunctionTemplate = Defn->getDescribedFunctionTemplate();
2607+
if (ExternalASTSource *Source = SemaRef.Context.getExternalSource()) {
2608+
if (isFriend && Source->wasThisDeclarationADefinition(D)) {
2609+
const FunctionDecl *Defn = nullptr;
2610+
if (D->hasBody(Defn)) {
2611+
D = const_cast<FunctionDecl *>(Defn);
2612+
FunctionTemplate = Defn->getDescribedFunctionTemplate();
2613+
}
26122614
}
26132615
}
26142616

Diff for: clang/lib/Serialization/ASTReader.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -9661,6 +9661,10 @@ ExternalASTSource::ExtKind ASTReader::hasExternalDefinitions(const Decl *FD) {
96619661
return I->second ? EK_Never : EK_Always;
96629662
}
96639663

9664+
bool ASTReader::wasThisDeclarationADefinition(const FunctionDecl *FD) {
9665+
return ThisDeclarationWasADefinitionSet.contains(FD);
9666+
}
9667+
96649668
Selector ASTReader::getLocalSelector(ModuleFile &M, unsigned LocalID) {
96659669
return DecodeSelector(getGlobalSelectorID(M, LocalID));
96669670
}

Diff for: clang/lib/Serialization/ASTReaderDecl.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -523,6 +523,9 @@ void ASTDeclReader::ReadFunctionDefinition(FunctionDecl *FD) {
523523
}
524524
// Store the offset of the body so we can lazily load it later.
525525
Reader.PendingBodies[FD] = GetCurrentCursorOffset();
526+
// For now remember ThisDeclarationWasADefinition only for friend functions.
527+
if (FD->getFriendObjectKind())
528+
Reader.ThisDeclarationWasADefinitionSet.insert(FD);
526529
}
527530

528531
void ASTDeclReader::Visit(Decl *D) {
+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// RUN: rm -fR %t
2+
// RUN: split-file %s %t
3+
// RUN: cd %t
4+
// RUN: %clang_cc1 -std=c++20 -fmodule-map-file=modules.map -xc++ -emit-module -fmodule-name=foo modules.map -o foo.pcm
5+
// RUN: %clang_cc1 -std=c++20 -fmodule-map-file=modules.map -O1 -emit-obj main.cc -verify -fmodule-file=foo.pcm
6+
7+
//--- modules.map
8+
module "foo" {
9+
export *
10+
module "foo.h" {
11+
export *
12+
header "foo.h"
13+
}
14+
}
15+
16+
//--- foo.h
17+
#pragma once
18+
19+
template <int>
20+
void Create(const void* = nullptr);
21+
22+
template <int>
23+
struct ObjImpl {
24+
template <int>
25+
friend void ::Create(const void*);
26+
};
27+
28+
template <int I>
29+
void Create(const void*) {
30+
(void) ObjImpl<I>{};
31+
}
32+
33+
//--- main.cc
34+
// expected-no-diagnostics
35+
#include "foo.h"
36+
37+
int main() {
38+
Create<42>();
39+
}

Diff for: clang/test/SemaCXX/friend-default-parameters.cpp

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %clang_cc1 -std=c++20 -verify -emit-llvm-only %s
2+
3+
template <int>
4+
void Create(const void* = nullptr);
5+
6+
template <int>
7+
struct ObjImpl {
8+
template <int>
9+
friend void ::Create(const void*);
10+
};
11+
12+
template <int I>
13+
void Create(const void*) {
14+
(void) ObjImpl<I>{};
15+
}
16+
17+
int main() {
18+
Create<42>();
19+
}
20+
21+
// expected-no-diagnostics

0 commit comments

Comments
 (0)