Skip to content

Commit 38b3d87

Browse files
authored
[C++20][Modules] Load function body from the module that gives canonical decl (#111992)
Summary: Fix crash from reproducer provided in #109167 (comment) Also fix issues with merged inline friend functions merged during deserialization. Test Plan: check-clang
1 parent 671095b commit 38b3d87

10 files changed

+284
-61
lines changed

clang/include/clang/Serialization/ASTBitCodes.h

+3-5
Original file line numberDiff line numberDiff line change
@@ -724,11 +724,9 @@ enum ASTRecordTypes {
724724
/// Record code for vtables to emit.
725725
VTABLES_TO_EMIT = 70,
726726

727-
/// Record code for the FunctionDecl to lambdas mapping. These lambdas have to
728-
/// be loaded right after the function they belong to. It is required to have
729-
/// canonical declaration for the lambda class from the same module as
730-
/// enclosing function.
731-
FUNCTION_DECL_TO_LAMBDAS_MAP = 71,
727+
/// Record code for related declarations that have to be deserialized together
728+
/// from the same module.
729+
RELATED_DECLS_MAP = 71,
732730

733731
/// Record code for Sema's vector of functions/blocks with effects to
734732
/// be verified.

clang/include/clang/Serialization/ASTReader.h

+8-11
Original file line numberDiff line numberDiff line change
@@ -537,17 +537,14 @@ class ASTReader
537537
/// namespace as if it is not delayed.
538538
DelayedNamespaceOffsetMapTy DelayedNamespaceOffsetMap;
539539

540-
/// Mapping from FunctionDecl IDs to the corresponding lambda IDs.
541-
///
542-
/// These lambdas have to be loaded right after the function they belong to.
543-
/// It is required to have canonical declaration for lambda class from the
544-
/// same module as enclosing function. This is required to correctly resolve
545-
/// captured variables in the lambda. Without this, due to lazy
546-
/// deserialization, canonical declarations for the function and lambdas can
547-
/// be selected from different modules and DeclRefExprs may refer to the AST
548-
/// nodes that don't exist in the function.
549-
llvm::DenseMap<GlobalDeclID, SmallVector<GlobalDeclID, 4>>
550-
FunctionToLambdasMap;
540+
/// Mapping from main decl ID to the related decls IDs.
541+
///
542+
/// These related decls have to be loaded right after the main decl.
543+
/// It is required to have canonical declaration for related decls from the
544+
/// same module as the enclosing main decl. Without this, due to lazy
545+
/// deserialization, canonical declarations for the main decl and related can
546+
/// be selected from different modules.
547+
llvm::DenseMap<GlobalDeclID, SmallVector<GlobalDeclID, 4>> RelatedDeclsMap;
551548

552549
struct PendingUpdateRecord {
553550
Decl *D;

clang/include/clang/Serialization/ASTWriter.h

+5-6
Original file line numberDiff line numberDiff line change
@@ -230,13 +230,12 @@ class ASTWriter : public ASTDeserializationListener,
230230
/// instead of comparing the result of `getDeclID()` or `GetDeclRef()`.
231231
llvm::SmallPtrSet<const Decl *, 32> PredefinedDecls;
232232

233-
/// Mapping from FunctionDecl ID to the list of lambda IDs inside the
234-
/// function.
233+
/// Mapping from the main decl to related decls inside the main decls.
235234
///
236-
/// These lambdas have to be loaded right after the function they belong to.
237-
/// In order to have canonical declaration for lambda class from the same
238-
/// module as enclosing function during deserialization.
239-
llvm::DenseMap<LocalDeclID, SmallVector<LocalDeclID, 4>> FunctionToLambdasMap;
235+
/// These related decls have to be loaded right after the main decl they
236+
/// belong to. In order to have canonical declaration for related decls from
237+
/// the same module as the main decl during deserialization.
238+
llvm::DenseMap<LocalDeclID, SmallVector<LocalDeclID, 4>> RelatedDeclsMap;
240239

241240
/// Offset of each declaration in the bitstream, indexed by
242241
/// the declaration's ID.

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

+17-6
Original file line numberDiff line numberDiff line change
@@ -2146,6 +2146,23 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
21462146
// Check whether there is already a function template specialization for
21472147
// this declaration.
21482148
FunctionTemplateDecl *FunctionTemplate = D->getDescribedFunctionTemplate();
2149+
bool isFriend;
2150+
if (FunctionTemplate)
2151+
isFriend = (FunctionTemplate->getFriendObjectKind() != Decl::FOK_None);
2152+
else
2153+
isFriend = (D->getFriendObjectKind() != Decl::FOK_None);
2154+
2155+
// Friend function defined withing class template may stop being function
2156+
// definition during AST merges from different modules, in this case decl
2157+
// with function body should be used for instantiation.
2158+
if (isFriend) {
2159+
const FunctionDecl *Defn = nullptr;
2160+
if (D->hasBody(Defn)) {
2161+
D = const_cast<FunctionDecl *>(Defn);
2162+
FunctionTemplate = Defn->getDescribedFunctionTemplate();
2163+
}
2164+
}
2165+
21492166
if (FunctionTemplate && !TemplateParams) {
21502167
ArrayRef<TemplateArgument> Innermost = TemplateArgs.getInnermost();
21512168

@@ -2158,12 +2175,6 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(
21582175
return SpecFunc;
21592176
}
21602177

2161-
bool isFriend;
2162-
if (FunctionTemplate)
2163-
isFriend = (FunctionTemplate->getFriendObjectKind() != Decl::FOK_None);
2164-
else
2165-
isFriend = (D->getFriendObjectKind() != Decl::FOK_None);
2166-
21672178
bool MergeWithParentScope = (TemplateParams != nullptr) ||
21682179
Owner->isFunctionOrMethod() ||
21692180
!(isa<Decl>(Owner) &&

clang/lib/Serialization/ASTReader.cpp

+4-17
Original file line numberDiff line numberDiff line change
@@ -3963,14 +3963,14 @@ llvm::Error ASTReader::ReadASTBlock(ModuleFile &F,
39633963
break;
39643964
}
39653965

3966-
case FUNCTION_DECL_TO_LAMBDAS_MAP:
3966+
case RELATED_DECLS_MAP:
39673967
for (unsigned I = 0, N = Record.size(); I != N; /*in loop*/) {
39683968
GlobalDeclID ID = ReadDeclID(F, Record, I);
3969-
auto &Lambdas = FunctionToLambdasMap[ID];
3969+
auto &RelatedDecls = RelatedDeclsMap[ID];
39703970
unsigned NN = Record[I++];
3971-
Lambdas.reserve(NN);
3971+
RelatedDecls.reserve(NN);
39723972
for (unsigned II = 0; II < NN; II++)
3973-
Lambdas.push_back(ReadDeclID(F, Record, I));
3973+
RelatedDecls.push_back(ReadDeclID(F, Record, I));
39743974
}
39753975
break;
39763976

@@ -10278,19 +10278,6 @@ void ASTReader::finishPendingActions() {
1027810278
PBEnd = PendingBodies.end();
1027910279
PB != PBEnd; ++PB) {
1028010280
if (FunctionDecl *FD = dyn_cast<FunctionDecl>(PB->first)) {
10281-
// For a function defined inline within a class template, force the
10282-
// canonical definition to be the one inside the canonical definition of
10283-
// the template. This ensures that we instantiate from a correct view
10284-
// of the template.
10285-
//
10286-
// Sadly we can't do this more generally: we can't be sure that all
10287-
// copies of an arbitrary class definition will have the same members
10288-
// defined (eg, some member functions may not be instantiated, and some
10289-
// special members may or may not have been implicitly defined).
10290-
if (auto *RD = dyn_cast<CXXRecordDecl>(FD->getLexicalParent()))
10291-
if (RD->isDependentContext() && !RD->isThisDeclarationADefinition())
10292-
continue;
10293-
1029410281
// FIXME: Check for =delete/=default?
1029510282
const FunctionDecl *Defn = nullptr;
1029610283
if (!getContext().getLangOpts().Modules || !FD->hasBody(Defn)) {

clang/lib/Serialization/ASTReaderDecl.cpp

+4-5
Original file line numberDiff line numberDiff line change
@@ -4329,13 +4329,12 @@ void ASTReader::loadDeclUpdateRecords(PendingUpdateRecord &Record) {
43294329
DC->setHasExternalVisibleStorage(true);
43304330
}
43314331

4332-
// Load any pending lambdas for the function.
4333-
if (auto *FD = dyn_cast<FunctionDecl>(D); FD && FD->isCanonicalDecl()) {
4334-
if (auto IT = FunctionToLambdasMap.find(ID);
4335-
IT != FunctionToLambdasMap.end()) {
4332+
// Load any pending related decls.
4333+
if (D->isCanonicalDecl()) {
4334+
if (auto IT = RelatedDeclsMap.find(ID); IT != RelatedDeclsMap.end()) {
43364335
for (auto LID : IT->second)
43374336
GetDecl(LID);
4338-
FunctionToLambdasMap.erase(IT);
4337+
RelatedDeclsMap.erase(IT);
43394338
}
43404339
}
43414340

clang/lib/Serialization/ASTWriter.cpp

+10-10
Original file line numberDiff line numberDiff line change
@@ -941,7 +941,7 @@ void ASTWriter::WriteBlockInfoBlock() {
941941
RECORD(PENDING_IMPLICIT_INSTANTIATIONS);
942942
RECORD(UPDATE_VISIBLE);
943943
RECORD(DELAYED_NAMESPACE_LEXICAL_VISIBLE_RECORD);
944-
RECORD(FUNCTION_DECL_TO_LAMBDAS_MAP);
944+
RECORD(RELATED_DECLS_MAP);
945945
RECORD(DECL_UPDATE_OFFSETS);
946946
RECORD(DECL_UPDATES);
947947
RECORD(CUDA_SPECIAL_DECL_REFS);
@@ -5972,23 +5972,23 @@ void ASTWriter::WriteDeclAndTypes(ASTContext &Context) {
59725972
Stream.EmitRecord(DELAYED_NAMESPACE_LEXICAL_VISIBLE_RECORD,
59735973
DelayedNamespaceRecord);
59745974

5975-
if (!FunctionToLambdasMap.empty()) {
5976-
// TODO: on disk hash table for function to lambda mapping might be more
5975+
if (!RelatedDeclsMap.empty()) {
5976+
// TODO: on disk hash table for related decls mapping might be more
59775977
// efficent becuase it allows lazy deserialization.
5978-
RecordData FunctionToLambdasMapRecord;
5979-
for (const auto &Pair : FunctionToLambdasMap) {
5980-
FunctionToLambdasMapRecord.push_back(Pair.first.getRawValue());
5981-
FunctionToLambdasMapRecord.push_back(Pair.second.size());
5978+
RecordData RelatedDeclsMapRecord;
5979+
for (const auto &Pair : RelatedDeclsMap) {
5980+
RelatedDeclsMapRecord.push_back(Pair.first.getRawValue());
5981+
RelatedDeclsMapRecord.push_back(Pair.second.size());
59825982
for (const auto &Lambda : Pair.second)
5983-
FunctionToLambdasMapRecord.push_back(Lambda.getRawValue());
5983+
RelatedDeclsMapRecord.push_back(Lambda.getRawValue());
59845984
}
59855985

59865986
auto Abv = std::make_shared<llvm::BitCodeAbbrev>();
5987-
Abv->Add(llvm::BitCodeAbbrevOp(FUNCTION_DECL_TO_LAMBDAS_MAP));
5987+
Abv->Add(llvm::BitCodeAbbrevOp(RELATED_DECLS_MAP));
59885988
Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::Array));
59895989
Abv->Add(llvm::BitCodeAbbrevOp(llvm::BitCodeAbbrevOp::VBR, 6));
59905990
unsigned FunctionToLambdaMapAbbrev = Stream.EmitAbbrev(std::move(Abv));
5991-
Stream.EmitRecord(FUNCTION_DECL_TO_LAMBDAS_MAP, FunctionToLambdasMapRecord,
5991+
Stream.EmitRecord(RELATED_DECLS_MAP, RelatedDeclsMapRecord,
59925992
FunctionToLambdaMapAbbrev);
59935993
}
59945994

clang/lib/Serialization/ASTWriterDecl.cpp

+12-1
Original file line numberDiff line numberDiff line change
@@ -798,6 +798,17 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) {
798798
}
799799
}
800800

801+
if (D->getFriendObjectKind()) {
802+
// For a function defined inline within a class template, we have to force
803+
// the canonical definition to be the one inside the canonical definition of
804+
// the template. Remember this relation to deserialize them together.
805+
if (auto *RD = dyn_cast<CXXRecordDecl>(D->getLexicalParent()))
806+
if (RD->isDependentContext() && RD->isThisDeclarationADefinition()) {
807+
Writer.RelatedDeclsMap[Writer.GetDeclRef(RD)].push_back(
808+
Writer.GetDeclRef(D));
809+
}
810+
}
811+
801812
Record.push_back(D->param_size());
802813
for (auto *P : D->parameters())
803814
Record.AddDeclRef(P);
@@ -1563,7 +1574,7 @@ void ASTDeclWriter::VisitCXXRecordDecl(CXXRecordDecl *D) {
15631574
// For lambdas inside canonical FunctionDecl remember the mapping.
15641575
if (auto FD = llvm::dyn_cast_or_null<FunctionDecl>(D->getDeclContext());
15651576
FD && FD->isCanonicalDecl()) {
1566-
Writer.FunctionToLambdasMap[Writer.GetDeclRef(FD)].push_back(
1577+
Writer.RelatedDeclsMap[Writer.GetDeclRef(FD)].push_back(
15671578
Writer.GetDeclRef(D));
15681579
}
15691580
} else {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// RUN: rm -fR %t
2+
// RUN: split-file %s %t
3+
// RUN: cd %t
4+
// RUN: %clang_cc1 -verify -std=c++20 -x c++ -fmodule-map-file=modules.map -fmodule-name=foo1 -emit-module modules.map -o foo1.pcm
5+
// RUN: %clang_cc1 -verify -std=c++20 -x c++ -fmodule-map-file=modules.map -fmodule-name=foo2 -emit-module modules.map -o foo2.pcm
6+
// RUN: %clang_cc1 -verify -std=c++20 -x c++ -fmodule-map-file=modules.map -fmodule-file=foo1.pcm -fmodule-file=foo2.pcm server.cc
7+
8+
//--- functional
9+
#pragma once
10+
11+
namespace std {
12+
template <class> class function {};
13+
} // namespace std
14+
15+
//--- foo.h
16+
#pragma once
17+
18+
class MethodHandler {
19+
public:
20+
virtual ~MethodHandler() {}
21+
struct HandlerParameter {
22+
HandlerParameter();
23+
};
24+
virtual void RunHandler(const HandlerParameter &param);
25+
};
26+
27+
template <class RequestType, class ResponseType>
28+
class CallbackUnaryHandler : public MethodHandler {
29+
public:
30+
explicit CallbackUnaryHandler();
31+
32+
void RunHandler(const HandlerParameter &param) final {
33+
void *call = nullptr;
34+
(void)[call](bool){};
35+
}
36+
};
37+
38+
//--- foo1.h
39+
// expected-no-diagnostics
40+
#pragma once
41+
42+
#include "functional"
43+
44+
#include "foo.h"
45+
46+
class A;
47+
48+
class ClientAsyncResponseReaderHelper {
49+
public:
50+
using t = std::function<void(A)>;
51+
static void SetupRequest(t finish);
52+
};
53+
54+
//--- foo2.h
55+
// expected-no-diagnostics
56+
#pragma once
57+
58+
#include "foo.h"
59+
60+
template <class BaseClass>
61+
class a : public BaseClass {
62+
public:
63+
a() { [[maybe_unused]] CallbackUnaryHandler<int, int> a; }
64+
};
65+
66+
//--- modules.map
67+
module "foo" {
68+
export *
69+
module "foo.h" {
70+
export *
71+
textual header "foo.h"
72+
}
73+
}
74+
75+
module "foo1" {
76+
export *
77+
module "foo1.h" {
78+
export *
79+
header "foo1.h"
80+
}
81+
82+
use "foo"
83+
}
84+
85+
module "foo2" {
86+
export *
87+
module "foo2.h" {
88+
export *
89+
header "foo2.h"
90+
}
91+
92+
use "foo"
93+
}
94+
95+
//--- server.cc
96+
// expected-no-diagnostics
97+
#include "functional"
98+
99+
#include "foo1.h"
100+
#include "foo2.h"
101+
102+
std::function<void()> on_emit;
103+
104+
template <class RequestType, class ResponseType>
105+
class CallbackUnaryHandler;
106+
107+
class s {};
108+
class hs final : public a<s> {
109+
explicit hs() {}
110+
};

0 commit comments

Comments
 (0)