Skip to content

Commit 73ed00f

Browse files
committed
[modules] Handle friend function that was a definition but became only a declaration during AST deserialization (llvm#132214)
Fix for regression llvm#130917, changes in llvm#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 58df0ef commit 73ed00f

10 files changed

+98
-5
lines changed

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
///

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,

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.
@@ -2375,6 +2379,8 @@ class ASTReader
23752379

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

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

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) {}

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) {

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

+7-5
Original file line numberDiff line numberDiff line change
@@ -2175,11 +2175,13 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
21752175
// Friend function defined withing class template may stop being function
21762176
// definition during AST merges from different modules, in this case decl
21772177
// with function body should be used for instantiation.
2178-
if (isFriend) {
2179-
const FunctionDecl *Defn = nullptr;
2180-
if (D->hasBody(Defn)) {
2181-
D = const_cast<FunctionDecl *>(Defn);
2182-
FunctionTemplate = Defn->getDescribedFunctionTemplate();
2178+
if (ExternalASTSource *Source = SemaRef.Context.getExternalSource()) {
2179+
if (isFriend && Source->wasThisDeclarationADefinition(D)) {
2180+
const FunctionDecl *Defn = nullptr;
2181+
if (D->hasBody(Defn)) {
2182+
D = const_cast<FunctionDecl *>(Defn);
2183+
FunctionTemplate = Defn->getDescribedFunctionTemplate();
2184+
}
21832185
}
21842186
}
21852187

clang/lib/Serialization/ASTReader.cpp

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

9667+
bool ASTReader::wasThisDeclarationADefinition(const FunctionDecl *FD) {
9668+
return ThisDeclarationWasADefinitionSet.contains(FD);
9669+
}
9670+
96679671
Selector ASTReader::getLocalSelector(ModuleFile &M, unsigned LocalID) {
96689672
return DecodeSelector(getGlobalSelectorID(M, LocalID));
96699673
}

clang/lib/Serialization/ASTReaderDecl.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,9 @@ void ASTDeclReader::ReadFunctionDefinition(FunctionDecl *FD) {
520520
}
521521
// Store the offset of the body so we can lazily load it later.
522522
Reader.PendingBodies[FD] = GetCurrentCursorOffset();
523+
// For now remember ThisDeclarationWasADefinition only for friend functions.
524+
if (FD->getFriendObjectKind())
525+
Reader.ThisDeclarationWasADefinitionSet.insert(FD);
523526
}
524527

525528
void ASTDeclReader::Visit(Decl *D) {
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+
}
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)