Skip to content

Commit d0ec7bd

Browse files
committedJun 25, 2018
[ASTImporter] Import the whole redecl chain of functions
Summary: With this patch when any `FunctionDecl` of a redeclaration chain is imported then we bring in the whole declaration chain. This involves functions and function template specializations. Also friend functions are affected. The chain is imported as it is in the "from" tu, the order of the redeclarations are kept. I also changed the lookup logic in order to find friends, but first making them visible in their declaration context. We may have long redeclaration chains if all TU contains the same prototype, but our measurements shows no degradation in time of CTU analysis (Tmux, Xerces, Bitcoin, Protobuf). Also, as further work we could squash redundant prototypes, but first ensure that functionality is working properly; then should we optimize. This may seem like a huge patch, sorry about that. But, most of the changes are new tests, changes in the production code is not that much. I also tried to create a smaller patch which does not affect specializations, but that patch failed to pass some of the `clang-import-test`s because there we import function specializations. Also very importantly, we can't just change the import of `FunctionDecl`s without changing the import of function template specializations because they are handled as `FunctionDecl`s. Reviewers: a.sidorin, r.stahl, xazax.hun, balazske Subscribers: rnkovacs, dkrupp, cfe-commits Differential Revision: https://reviews.llvm.org/D47532 llvm-svn: 335480
1 parent 871b059 commit d0ec7bd

File tree

5 files changed

+787
-111
lines changed

5 files changed

+787
-111
lines changed
 

‎clang/include/clang/AST/ASTImporter.h

+9
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,15 @@ class TagDecl;
4343
class TypeSourceInfo;
4444
class Attr;
4545

46+
// \brief Returns with a list of declarations started from the canonical decl
47+
// then followed by subsequent decls in the translation unit.
48+
// This gives a canonical list for each entry in the redecl chain.
49+
// `Decl::redecls()` gives a list of decls which always start from the
50+
// previous decl and the next item is actually the previous item in the order
51+
// of source locations. Thus, `Decl::redecls()` gives different lists for
52+
// the different entries in a given redecl chain.
53+
llvm::SmallVector<Decl*, 2> getCanonicalForwardRedeclChain(Decl* D);
54+
4655
/// Imports selected nodes from one AST context into another context,
4756
/// merging AST nodes where appropriate.
4857
class ASTImporter {

‎clang/lib/AST/ASTImporter.cpp

+128-37
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,25 @@
7171

7272
namespace clang {
7373

74+
template <class T>
75+
SmallVector<Decl*, 2>
76+
getCanonicalForwardRedeclChain(Redeclarable<T>* D) {
77+
SmallVector<Decl*, 2> Redecls;
78+
for (auto *R : D->getFirstDecl()->redecls()) {
79+
if (R != D->getFirstDecl())
80+
Redecls.push_back(R);
81+
}
82+
Redecls.push_back(D->getFirstDecl());
83+
std::reverse(Redecls.begin(), Redecls.end());
84+
return Redecls;
85+
}
86+
87+
SmallVector<Decl*, 2> getCanonicalForwardRedeclChain(Decl* D) {
88+
// Currently only FunctionDecl is supported
89+
auto FD = cast<FunctionDecl>(D);
90+
return getCanonicalForwardRedeclChain<FunctionDecl>(FD);
91+
}
92+
7493
class ASTNodeImporter : public TypeVisitor<ASTNodeImporter, QualType>,
7594
public DeclVisitor<ASTNodeImporter, Decl *>,
7695
public StmtVisitor<ASTNodeImporter, Stmt *> {
@@ -195,6 +214,12 @@ namespace clang {
195214
const InContainerTy &Container,
196215
TemplateArgumentListInfo &Result);
197216

217+
using TemplateArgsTy = SmallVector<TemplateArgument, 8>;
218+
using OptionalTemplateArgsTy = Optional<TemplateArgsTy>;
219+
std::tuple<FunctionTemplateDecl *, OptionalTemplateArgsTy>
220+
ImportFunctionTemplateWithTemplateArgsFromSpecialization(
221+
FunctionDecl *FromFD);
222+
198223
bool ImportTemplateInformation(FunctionDecl *FromFD, FunctionDecl *ToFD);
199224

200225
bool IsStructuralMatch(RecordDecl *FromRecord, RecordDecl *ToRecord,
@@ -408,6 +433,8 @@ namespace clang {
408433

409434
// Importing overrides.
410435
void ImportOverrides(CXXMethodDecl *ToMethod, CXXMethodDecl *FromMethod);
436+
437+
FunctionDecl *FindFunctionTemplateSpecialization(FunctionDecl *FromFD);
411438
};
412439

413440
template <typename InContainerTy>
@@ -437,6 +464,25 @@ bool ASTNodeImporter::ImportTemplateArgumentListInfo<
437464
From.arguments(), Result);
438465
}
439466

467+
std::tuple<FunctionTemplateDecl *, ASTNodeImporter::OptionalTemplateArgsTy>
468+
ASTNodeImporter::ImportFunctionTemplateWithTemplateArgsFromSpecialization(
469+
FunctionDecl *FromFD) {
470+
assert(FromFD->getTemplatedKind() ==
471+
FunctionDecl::TK_FunctionTemplateSpecialization);
472+
auto *FTSInfo = FromFD->getTemplateSpecializationInfo();
473+
auto *Template = cast_or_null<FunctionTemplateDecl>(
474+
Importer.Import(FTSInfo->getTemplate()));
475+
476+
// Import template arguments.
477+
auto TemplArgs = FTSInfo->TemplateArguments->asArray();
478+
TemplateArgsTy ToTemplArgs;
479+
if (ImportTemplateArguments(TemplArgs.data(), TemplArgs.size(),
480+
ToTemplArgs)) // Error during import.
481+
return std::make_tuple(Template, OptionalTemplateArgsTy());
482+
483+
return std::make_tuple(Template, ToTemplArgs);
484+
}
485+
440486
} // namespace clang
441487

442488
//----------------------------------------------------------------------------
@@ -2252,23 +2298,17 @@ bool ASTNodeImporter::ImportTemplateInformation(FunctionDecl *FromFD,
22522298
}
22532299

22542300
case FunctionDecl::TK_FunctionTemplateSpecialization: {
2255-
auto *FTSInfo = FromFD->getTemplateSpecializationInfo();
2256-
auto *Template = cast_or_null<FunctionTemplateDecl>(
2257-
Importer.Import(FTSInfo->getTemplate()));
2258-
if (!Template)
2259-
return true;
2260-
TemplateSpecializationKind TSK = FTSInfo->getTemplateSpecializationKind();
2261-
2262-
// Import template arguments.
2263-
auto TemplArgs = FTSInfo->TemplateArguments->asArray();
2264-
SmallVector<TemplateArgument, 8> ToTemplArgs;
2265-
if (ImportTemplateArguments(TemplArgs.data(), TemplArgs.size(),
2266-
ToTemplArgs))
2301+
FunctionTemplateDecl* Template;
2302+
OptionalTemplateArgsTy ToTemplArgs;
2303+
std::tie(Template, ToTemplArgs) =
2304+
ImportFunctionTemplateWithTemplateArgsFromSpecialization(FromFD);
2305+
if (!Template || !ToTemplArgs)
22672306
return true;
22682307

22692308
TemplateArgumentList *ToTAList = TemplateArgumentList::CreateCopy(
2270-
Importer.getToContext(), ToTemplArgs);
2309+
Importer.getToContext(), *ToTemplArgs);
22712310

2311+
auto *FTSInfo = FromFD->getTemplateSpecializationInfo();
22722312
TemplateArgumentListInfo ToTAInfo;
22732313
const auto *FromTAArgsAsWritten = FTSInfo->TemplateArgumentsAsWritten;
22742314
if (FromTAArgsAsWritten)
@@ -2277,6 +2317,7 @@ bool ASTNodeImporter::ImportTemplateInformation(FunctionDecl *FromFD,
22772317

22782318
SourceLocation POI = Importer.Import(FTSInfo->getPointOfInstantiation());
22792319

2320+
TemplateSpecializationKind TSK = FTSInfo->getTemplateSpecializationKind();
22802321
ToFD->setFunctionTemplateSpecialization(
22812322
Template, ToTAList, /* InsertPos= */ nullptr,
22822323
TSK, FromTAArgsAsWritten ? &ToTAInfo : nullptr, POI);
@@ -2312,7 +2353,31 @@ bool ASTNodeImporter::ImportTemplateInformation(FunctionDecl *FromFD,
23122353
llvm_unreachable("All cases should be covered!");
23132354
}
23142355

2356+
FunctionDecl *
2357+
ASTNodeImporter::FindFunctionTemplateSpecialization(FunctionDecl *FromFD) {
2358+
FunctionTemplateDecl* Template;
2359+
OptionalTemplateArgsTy ToTemplArgs;
2360+
std::tie(Template, ToTemplArgs) =
2361+
ImportFunctionTemplateWithTemplateArgsFromSpecialization(FromFD);
2362+
if (!Template || !ToTemplArgs)
2363+
return nullptr;
2364+
2365+
void *InsertPos = nullptr;
2366+
auto *FoundSpec = Template->findSpecialization(*ToTemplArgs, InsertPos);
2367+
return FoundSpec;
2368+
}
2369+
23152370
Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
2371+
2372+
SmallVector<Decl*, 2> Redecls = getCanonicalForwardRedeclChain(D);
2373+
auto RedeclIt = Redecls.begin();
2374+
// Import the first part of the decl chain. I.e. import all previous
2375+
// declarations starting from the canonical decl.
2376+
for (; RedeclIt != Redecls.end() && *RedeclIt != D; ++RedeclIt)
2377+
if (!Importer.Import(*RedeclIt))
2378+
return nullptr;
2379+
assert(*RedeclIt == D);
2380+
23162381
// Import the major distinguishing characteristics of this function.
23172382
DeclContext *DC, *LexicalDC;
23182383
DeclarationName Name;
@@ -2323,13 +2388,27 @@ Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
23232388
if (ToD)
23242389
return ToD;
23252390

2326-
const FunctionDecl *FoundWithoutBody = nullptr;
2327-
2391+
const FunctionDecl *FoundByLookup = nullptr;
2392+
2393+
// If this is a function template specialization, then try to find the same
2394+
// existing specialization in the "to" context. The localUncachedLookup
2395+
// below will not find any specialization, but would find the primary
2396+
// template; thus, we have to skip normal lookup in case of specializations.
2397+
// FIXME handle member function templates (TK_MemberSpecialization) similarly?
2398+
if (D->getTemplatedKind() ==
2399+
FunctionDecl::TK_FunctionTemplateSpecialization) {
2400+
if (FunctionDecl *FoundFunction = FindFunctionTemplateSpecialization(D)) {
2401+
if (D->doesThisDeclarationHaveABody() &&
2402+
FoundFunction->hasBody())
2403+
return Importer.Imported(D, FoundFunction);
2404+
FoundByLookup = FoundFunction;
2405+
}
2406+
}
23282407
// Try to find a function in our own ("to") context with the same name, same
23292408
// type, and in the same context as the function we're importing.
2330-
if (!LexicalDC->isFunctionOrMethod()) {
2409+
else if (!LexicalDC->isFunctionOrMethod()) {
23312410
SmallVector<NamedDecl *, 4> ConflictingDecls;
2332-
unsigned IDNS = Decl::IDNS_Ordinary;
2411+
unsigned IDNS = Decl::IDNS_Ordinary | Decl::IDNS_OrdinaryFriend;
23332412
SmallVector<NamedDecl *, 2> FoundDecls;
23342413
DC->getRedeclContext()->localUncachedLookup(Name, FoundDecls);
23352414
for (auto *FoundDecl : FoundDecls) {
@@ -2341,15 +2420,11 @@ Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
23412420
D->hasExternalFormalLinkage()) {
23422421
if (Importer.IsStructurallyEquivalent(D->getType(),
23432422
FoundFunction->getType())) {
2344-
// FIXME: Actually try to merge the body and other attributes.
2345-
const FunctionDecl *FromBodyDecl = nullptr;
2346-
D->hasBody(FromBodyDecl);
2347-
if (D == FromBodyDecl && !FoundFunction->hasBody()) {
2348-
// This function is needed to merge completely.
2349-
FoundWithoutBody = FoundFunction;
2423+
if (D->doesThisDeclarationHaveABody() &&
2424+
FoundFunction->hasBody())
2425+
return Importer.Imported(D, FoundFunction);
2426+
FoundByLookup = FoundFunction;
23502427
break;
2351-
}
2352-
return Importer.Imported(D, FoundFunction);
23532428
}
23542429

23552430
// FIXME: Check for overloading more carefully, e.g., by boosting
@@ -2499,9 +2574,9 @@ Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
24992574
}
25002575
ToFunction->setParams(Parameters);
25012576

2502-
if (FoundWithoutBody) {
2577+
if (FoundByLookup) {
25032578
auto *Recent = const_cast<FunctionDecl *>(
2504-
FoundWithoutBody->getMostRecentDecl());
2579+
FoundByLookup->getMostRecentDecl());
25052580
ToFunction->setPreviousDecl(Recent);
25062581
}
25072582

@@ -2523,10 +2598,11 @@ Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
25232598
ToFunction->setType(T);
25242599
}
25252600

2526-
// Import the body, if any.
2527-
if (Stmt *FromBody = D->getBody()) {
2528-
if (Stmt *ToBody = Importer.Import(FromBody)) {
2529-
ToFunction->setBody(ToBody);
2601+
if (D->doesThisDeclarationHaveABody()) {
2602+
if (Stmt *FromBody = D->getBody()) {
2603+
if (Stmt *ToBody = Importer.Import(FromBody)) {
2604+
ToFunction->setBody(ToBody);
2605+
}
25302606
}
25312607
}
25322608

@@ -2536,14 +2612,29 @@ Decl *ASTNodeImporter::VisitFunctionDecl(FunctionDecl *D) {
25362612
if (ImportTemplateInformation(D, ToFunction))
25372613
return nullptr;
25382614

2539-
// Add this function to the lexical context.
2540-
// NOTE: If the function is templated declaration, it should be not added into
2541-
// LexicalDC. But described template is imported during import of
2542-
// FunctionTemplateDecl (it happens later). So, we use source declaration
2543-
// to determine if we should add the result function.
2544-
if (!D->getDescribedFunctionTemplate())
2615+
bool IsFriend = D->isInIdentifierNamespace(Decl::IDNS_OrdinaryFriend);
2616+
2617+
// TODO Can we generalize this approach to other AST nodes as well?
2618+
if (D->getDeclContext()->containsDecl(D))
2619+
DC->addDeclInternal(ToFunction);
2620+
if (DC != LexicalDC && D->getLexicalDeclContext()->containsDecl(D))
25452621
LexicalDC->addDeclInternal(ToFunction);
25462622

2623+
// Friend declaration's lexical context is the befriending class, but the
2624+
// semantic context is the enclosing scope of the befriending class.
2625+
// We want the friend functions to be found in the semantic context by lookup.
2626+
// FIXME should we handle this generically in VisitFriendDecl?
2627+
// In Other cases when LexicalDC != DC we don't want it to be added,
2628+
// e.g out-of-class definitions like void B::f() {} .
2629+
if (LexicalDC != DC && IsFriend) {
2630+
DC->makeDeclVisibleInContext(ToFunction);
2631+
}
2632+
2633+
// Import the rest of the chain. I.e. import all subsequent declarations.
2634+
for (++RedeclIt; RedeclIt != Redecls.end(); ++RedeclIt)
2635+
if (!Importer.Import(*RedeclIt))
2636+
return nullptr;
2637+
25472638
if (auto *FromCXXMethod = dyn_cast<CXXMethodDecl>(D))
25482639
ImportOverrides(cast<CXXMethodDecl>(ToFunction), FromCXXMethod);
25492640

‎clang/lib/AST/DeclBase.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -1343,6 +1343,8 @@ bool DeclContext::decls_empty() const {
13431343
}
13441344

13451345
bool DeclContext::containsDecl(Decl *D) const {
1346+
if (hasExternalLexicalStorage())
1347+
LoadLexicalDeclsFromExternalStorage();
13461348
return (D->getLexicalDeclContext() == this &&
13471349
(D->NextInContextAndBits.getPointer() || D == LastDecl));
13481350
}

‎clang/test/ASTMerge/class/test.cpp

+4-4
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@
1313
// CHECK: class1.cpp:19:3: note: enumerator 'b' with value 1 here
1414
// CHECK: class2.cpp:12:3: note: enumerator 'a' with value 0 here
1515

16-
// CHECK: class1.cpp:36:8: warning: type 'F2' has incompatible definitions in different translation units
17-
// CHECK: class1.cpp:39:3: note: friend declared here
18-
// CHECK: class2.cpp:30:8: note: no corresponding friend here
19-
2016
// CHECK: class1.cpp:43:8: warning: type 'F3' has incompatible definitions in different translation units
2117
// CHECK: class1.cpp:46:3: note: friend declared here
2218
// CHECK: class2.cpp:36:8: note: no corresponding friend here
2319

20+
// CHECK: class1.cpp:36:8: warning: type 'F2' has incompatible definitions in different translation units
21+
// CHECK: class1.cpp:39:3: note: friend declared here
22+
// CHECK: class2.cpp:30:8: note: no corresponding friend here
23+
2424
// CHECK: 4 warnings generated.

‎clang/unittests/AST/ASTImporterTest.cpp

+644-70
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)
Please sign in to comment.