summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2018-07-04 02:25:38 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2018-07-04 02:25:38 +0000
commitef356db5c03a0f052c09ebd0a964b9eda3852847 (patch)
tree28fbb44d59ddb9c84297007d9191b53710a1b53f
parent2926189c9bf4982cd6a0e9cc00d785837112d457 (diff)
PR33924: merge local declarations that have linkage of some kind within
merged function definitions; also merge functions with deduced return types. This seems like two independent fixes, but unfortunately they are hard to separate because it's challenging to reliably test either one of them without also testing the other. A complication arises with deduced return type support: we need the type of the function in order to know how to merge it, but we can't load the actual type of the function because it might reference an entity declared within the function (and we need to have already merged the function to correctly merge that entity, which we would need to do to determine if the function types match). So we instead compare the declared function type when merging functions, and defer loading the actual type of a function with a deduced type until we've finished loading and merging the function. This reverts r336175, reinstating r336021, with one change (for PR38015): we look at the TypeSourceInfo of the first-so-far declaration of each function when considering whether to merge two functions. This works around a problem where the calling convention in the TypeSourceInfo for subsequent redeclarations may not match if it was implicitly adjusted. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@336240 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/Serialization/ASTReader.h2
-rw-r--r--lib/Serialization/ASTCommon.cpp16
-rw-r--r--lib/Serialization/ASTReaderDecl.cpp108
-rw-r--r--test/Modules/cxx-dtor.cpp1
-rw-r--r--test/Modules/friend-definition-2.cpp1
-rw-r--r--test/Modules/merge-deduced-return.cpp33
-rw-r--r--test/Modules/merge-lambdas.cpp48
-rw-r--r--test/Modules/merge-static-locals.cpp27
-rw-r--r--test/Modules/pr27401.cpp1
9 files changed, 211 insertions, 26 deletions
diff --git a/include/clang/Serialization/ASTReader.h b/include/clang/Serialization/ASTReader.h
index bf9ed38a15..b33f317765 100644
--- a/include/clang/Serialization/ASTReader.h
+++ b/include/clang/Serialization/ASTReader.h
@@ -546,7 +546,7 @@ private:
/// Mergeable declaration contexts that have anonymous declarations
/// within them, and those anonymous declarations.
- llvm::DenseMap<DeclContext*, llvm::SmallVector<NamedDecl*, 2>>
+ llvm::DenseMap<Decl*, llvm::SmallVector<NamedDecl*, 2>>
AnonymousDeclarationsForMerging;
struct FileDeclsInfo {
diff --git a/lib/Serialization/ASTCommon.cpp b/lib/Serialization/ASTCommon.cpp
index fcebe5982f..da482717f4 100644
--- a/lib/Serialization/ASTCommon.cpp
+++ b/lib/Serialization/ASTCommon.cpp
@@ -419,9 +419,21 @@ bool serialization::needsAnonymousDeclarationNumber(const NamedDecl *D) {
return true;
}
- // Otherwise, we only care about anonymous class members.
+ // At block scope, we number everything that we need to deduplicate, since we
+ // can't just use name matching to keep things lined up.
+ // FIXME: This is only necessary for an inline function or a template or
+ // similar.
+ if (D->getLexicalDeclContext()->isFunctionOrMethod()) {
+ if (auto *VD = dyn_cast<VarDecl>(D))
+ return VD->isStaticLocal();
+ // FIXME: What about CapturedDecls (and declarations nested within them)?
+ return isa<TagDecl>(D) || isa<BlockDecl>(D);
+ }
+
+ // Otherwise, we only care about anonymous class members / block-scope decls.
+ // FIXME: We need to handle lambdas and blocks within inline / templated
+ // variables too.
if (D->getDeclName() || !isa<CXXRecordDecl>(D->getLexicalDeclContext()))
return false;
return isa<TagDecl>(D) || isa<FieldDecl>(D);
}
-
diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp
index b0c7bbec20..b33b88c271 100644
--- a/lib/Serialization/ASTReaderDecl.cpp
+++ b/lib/Serialization/ASTReaderDecl.cpp
@@ -87,7 +87,7 @@ namespace clang {
using RecordData = ASTReader::RecordData;
- TypeID TypeIDForTypeDecl = 0;
+ TypeID DeferredTypeID = 0;
unsigned AnonymousDeclNumber;
GlobalDeclID NamedDeclForTagDecl = 0;
IdentifierInfo *TypedefNameForLinkage = nullptr;
@@ -177,6 +177,8 @@ namespace clang {
void MergeDefinitionData(ObjCProtocolDecl *D,
struct ObjCProtocolDecl::DefinitionData &&NewDD);
+ static DeclContext *getPrimaryDCForAnonymousDecl(DeclContext *LexicalDC);
+
static NamedDecl *getAnonymousDeclForMerging(ASTReader &Reader,
DeclContext *DC,
unsigned Index);
@@ -528,7 +530,7 @@ void ASTDeclReader::Visit(Decl *D) {
if (auto *TD = dyn_cast<TypeDecl>(D)) {
// We have a fully initialized TypeDecl. Read its type now.
- TD->setTypeForDecl(Reader.GetType(TypeIDForTypeDecl).getTypePtrOrNull());
+ TD->setTypeForDecl(Reader.GetType(DeferredTypeID).getTypePtrOrNull());
// If this is a tag declaration with a typedef name for linkage, it's safe
// to load that typedef now.
@@ -537,8 +539,11 @@ void ASTDeclReader::Visit(Decl *D) {
cast<TypedefNameDecl>(Reader.GetDecl(NamedDeclForTagDecl));
} else if (auto *ID = dyn_cast<ObjCInterfaceDecl>(D)) {
// if we have a fully initialized TypeDecl, we can safely read its type now.
- ID->TypeForDecl = Reader.GetType(TypeIDForTypeDecl).getTypePtrOrNull();
+ ID->TypeForDecl = Reader.GetType(DeferredTypeID).getTypePtrOrNull();
} else if (auto *FD = dyn_cast<FunctionDecl>(D)) {
+ if (DeferredTypeID)
+ FD->setType(Reader.GetType(DeferredTypeID));
+
// FunctionDecl's body was written last after all other Stmts/Exprs.
// We only read it if FD doesn't already have a body (e.g., from another
// module).
@@ -658,7 +663,7 @@ void ASTDeclReader::VisitTypeDecl(TypeDecl *TD) {
VisitNamedDecl(TD);
TD->setLocStart(ReadSourceLocation());
// Delay type reading until after we have fully initialized the decl.
- TypeIDForTypeDecl = Record.getGlobalTypeID(Record.readInt());
+ DeferredTypeID = Record.getGlobalTypeID(Record.readInt());
}
ASTDeclReader::RedeclarableResult
@@ -791,7 +796,13 @@ ASTDeclReader::VisitRecordDeclImpl(RecordDecl *RD) {
void ASTDeclReader::VisitValueDecl(ValueDecl *VD) {
VisitNamedDecl(VD);
- VD->setType(Record.readType());
+ // For function declarations, defer reading the type in case the function has
+ // a deduced return type that references an entity declared within the
+ // function.
+ if (isa<FunctionDecl>(VD))
+ DeferredTypeID = Record.getGlobalTypeID(Record.readInt());
+ else
+ VD->setType(Record.readType());
}
void ASTDeclReader::VisitEnumConstantDecl(EnumConstantDecl *ECD) {
@@ -820,6 +831,19 @@ void ASTDeclReader::VisitFunctionDecl(FunctionDecl *FD) {
RedeclarableResult Redecl = VisitRedeclarable(FD);
VisitDeclaratorDecl(FD);
+ // Attach a type to this function. Use the real type if possible, but fall
+ // back to the type as written if it involves a deduced return type.
+ if (FD->getTypeSourceInfo() &&
+ FD->getTypeSourceInfo()->getType()->castAs<FunctionType>()
+ ->getReturnType()->getContainedAutoType()) {
+ // We'll set up the real type in Visit, once we've finished loading the
+ // function.
+ FD->setType(FD->getTypeSourceInfo()->getType());
+ } else {
+ FD->setType(Reader.GetType(DeferredTypeID));
+ DeferredTypeID = 0;
+ }
+
ReadDeclarationNameLoc(FD->DNLoc, FD->getDeclName());
FD->IdentifierNamespace = Record.readInt();
@@ -1084,7 +1108,7 @@ void ASTDeclReader::MergeDefinitionData(ObjCInterfaceDecl *D,
void ASTDeclReader::VisitObjCInterfaceDecl(ObjCInterfaceDecl *ID) {
RedeclarableResult Redecl = VisitRedeclarable(ID);
VisitObjCContainerDecl(ID);
- TypeIDForTypeDecl = Record.getGlobalTypeID(Record.readInt());
+ DeferredTypeID = Record.getGlobalTypeID(Record.readInt());
mergeRedeclarable(ID, Redecl);
ID->TypeParamList = ReadObjCTypeParamList();
@@ -1890,7 +1914,7 @@ ASTDeclReader::VisitCXXRecordDeclImpl(CXXRecordDecl *D) {
//
// Beware: we do not yet know our canonical declaration, and may still
// get merged once the surrounding class template has got off the ground.
- TypeIDForTypeDecl = 0;
+ DeferredTypeID = 0;
}
break;
}
@@ -2843,8 +2867,12 @@ static bool isSameEntity(NamedDecl *X, NamedDecl *Y) {
return true;
// Must be in the same context.
- if (!X->getDeclContext()->getRedeclContext()->Equals(
- Y->getDeclContext()->getRedeclContext()))
+ //
+ // Note that we can't use DeclContext::Equals here, because the DeclContexts
+ // could be two different declarations of the same function. (We will fix the
+ // semantic DC to refer to the primary definition after merging.)
+ if (!declaresSameEntity(cast<Decl>(X->getDeclContext()->getRedeclContext()),
+ cast<Decl>(Y->getDeclContext()->getRedeclContext())))
return false;
// Two typedefs refer to the same entity if they have the same underlying
@@ -2906,18 +2934,26 @@ static bool isSameEntity(NamedDecl *X, NamedDecl *Y) {
}
ASTContext &C = FuncX->getASTContext();
- if (!C.hasSameType(FuncX->getType(), FuncY->getType())) {
+ auto GetTypeAsWritten = [](const FunctionDecl *FD) {
+ // Map to the first declaration that we've already merged into this one.
+ // The TSI of redeclarations might not match (due to calling conventions
+ // being inherited onto the type but not the TSI), but the TSI type of
+ // the first declaration of the function should match across modules.
+ FD = FD->getCanonicalDecl();
+ return FD->getTypeSourceInfo() ? FD->getTypeSourceInfo()->getType()
+ : FD->getType();
+ };
+ QualType XT = GetTypeAsWritten(FuncX), YT = GetTypeAsWritten(FuncY);
+ if (!C.hasSameType(XT, YT)) {
// We can get functions with different types on the redecl chain in C++17
// if they have differing exception specifications and at least one of
// the excpetion specs is unresolved.
- // FIXME: Do we need to check for C++14 deduced return types here too?
- auto *XFPT = FuncX->getType()->getAs<FunctionProtoType>();
- auto *YFPT = FuncY->getType()->getAs<FunctionProtoType>();
+ auto *XFPT = XT->getAs<FunctionProtoType>();
+ auto *YFPT = YT->getAs<FunctionProtoType>();
if (C.getLangOpts().CPlusPlus17 && XFPT && YFPT &&
(isUnresolvedExceptionSpec(XFPT->getExceptionSpecType()) ||
isUnresolvedExceptionSpec(YFPT->getExceptionSpecType())) &&
- C.hasSameFunctionTypeIgnoringExceptionSpec(FuncX->getType(),
- FuncY->getType()))
+ C.hasSameFunctionTypeIgnoringExceptionSpec(XT, YT))
return true;
return false;
}
@@ -3109,23 +3145,50 @@ static NamedDecl *getDeclForMerging(NamedDecl *Found,
return nullptr;
}
+/// Find the declaration to use to populate the anonymous declaration table
+/// for the given lexical DeclContext. We only care about finding local
+/// definitions of the context; we'll merge imported ones as we go.
+DeclContext *
+ASTDeclReader::getPrimaryDCForAnonymousDecl(DeclContext *LexicalDC) {
+ // For classes, we track the definition as we merge.
+ if (auto *RD = dyn_cast<CXXRecordDecl>(LexicalDC)) {
+ auto *DD = RD->getCanonicalDecl()->DefinitionData;
+ return DD ? DD->Definition : nullptr;
+ }
+
+ // For anything else, walk its merged redeclarations looking for a definition.
+ // Note that we can't just call getDefinition here because the redeclaration
+ // chain isn't wired up.
+ for (auto *D : merged_redecls(cast<Decl>(LexicalDC))) {
+ if (auto *FD = dyn_cast<FunctionDecl>(D))
+ if (FD->isThisDeclarationADefinition())
+ return FD;
+ if (auto *MD = dyn_cast<ObjCMethodDecl>(D))
+ if (MD->isThisDeclarationADefinition())
+ return MD;
+ }
+
+ // No merged definition yet.
+ return nullptr;
+}
+
NamedDecl *ASTDeclReader::getAnonymousDeclForMerging(ASTReader &Reader,
DeclContext *DC,
unsigned Index) {
// If the lexical context has been merged, look into the now-canonical
// definition.
- if (auto *Merged = Reader.MergedDeclContexts.lookup(DC))
- DC = Merged;
+ auto *CanonDC = cast<Decl>(DC)->getCanonicalDecl();
// If we've seen this before, return the canonical declaration.
- auto &Previous = Reader.AnonymousDeclarationsForMerging[DC];
+ auto &Previous = Reader.AnonymousDeclarationsForMerging[CanonDC];
if (Index < Previous.size() && Previous[Index])
return Previous[Index];
// If this is the first time, but we have parsed a declaration of the context,
// build the anonymous declaration list from the parsed declaration.
- if (!cast<Decl>(DC)->isFromASTFile()) {
- numberAnonymousDeclsWithin(DC, [&](NamedDecl *ND, unsigned Number) {
+ auto *PrimaryDC = getPrimaryDCForAnonymousDecl(DC);
+ if (PrimaryDC && !cast<Decl>(PrimaryDC)->isFromASTFile()) {
+ numberAnonymousDeclsWithin(PrimaryDC, [&](NamedDecl *ND, unsigned Number) {
if (Previous.size() == Number)
Previous.push_back(cast<NamedDecl>(ND->getCanonicalDecl()));
else
@@ -3139,10 +3202,9 @@ NamedDecl *ASTDeclReader::getAnonymousDeclForMerging(ASTReader &Reader,
void ASTDeclReader::setAnonymousDeclForMerging(ASTReader &Reader,
DeclContext *DC, unsigned Index,
NamedDecl *D) {
- if (auto *Merged = Reader.MergedDeclContexts.lookup(DC))
- DC = Merged;
+ auto *CanonDC = cast<Decl>(DC)->getCanonicalDecl();
- auto &Previous = Reader.AnonymousDeclarationsForMerging[DC];
+ auto &Previous = Reader.AnonymousDeclarationsForMerging[CanonDC];
if (Index >= Previous.size())
Previous.resize(Index + 1);
if (!Previous[Index])
diff --git a/test/Modules/cxx-dtor.cpp b/test/Modules/cxx-dtor.cpp
index 63427ee0af..d685b69d51 100644
--- a/test/Modules/cxx-dtor.cpp
+++ b/test/Modules/cxx-dtor.cpp
@@ -1,3 +1,4 @@
// RUN: rm -rf %t
// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -x c++ -std=c++11 -fmodules-cache-path=%t -I %S/Inputs/cxx-dtor -emit-llvm-only %s
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -x c++ -std=c++11 -fmodules-cache-path=%t -I %S/Inputs/cxx-dtor -emit-llvm-only %s -triple i686-windows
#include "b.h"
diff --git a/test/Modules/friend-definition-2.cpp b/test/Modules/friend-definition-2.cpp
index 7f876b762a..b226b5c0d1 100644
--- a/test/Modules/friend-definition-2.cpp
+++ b/test/Modules/friend-definition-2.cpp
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -fmodules %s -verify
+// RUN: %clang_cc1 -fmodules %s -verify -triple i686-windows
// expected-no-diagnostics
#pragma clang module build A
module A {}
diff --git a/test/Modules/merge-deduced-return.cpp b/test/Modules/merge-deduced-return.cpp
new file mode 100644
index 0000000000..0a4de7b975
--- /dev/null
+++ b/test/Modules/merge-deduced-return.cpp
@@ -0,0 +1,33 @@
+// RUN: %clang_cc1 -fmodules -std=c++17 -verify %s
+// RUN: %clang_cc1 -fmodules -std=c++17 -verify %s -DLOCAL
+// expected-no-diagnostics
+
+#pragma clang module build A
+module A {}
+#pragma clang module contents
+#pragma clang module begin A
+inline auto f() { struct X {}; return X(); }
+inline auto a = f();
+#pragma clang module end
+#pragma clang module endbuild
+
+#pragma clang module build B
+module B {}
+#pragma clang module contents
+#pragma clang module begin B
+inline auto f() { struct X {}; return X(); }
+inline auto b = f();
+#pragma clang module end
+#pragma clang module endbuild
+
+#ifdef LOCAL
+inline auto f() { struct X {}; return X(); }
+inline auto b = f();
+#else
+#pragma clang module import B
+#endif
+
+#pragma clang module import A
+
+using T = decltype(a);
+using T = decltype(b);
diff --git a/test/Modules/merge-lambdas.cpp b/test/Modules/merge-lambdas.cpp
new file mode 100644
index 0000000000..d14483aa3a
--- /dev/null
+++ b/test/Modules/merge-lambdas.cpp
@@ -0,0 +1,48 @@
+// RUN: %clang_cc1 -fmodules -verify %s
+// expected-no-diagnostics
+
+#pragma clang module build A
+module A {}
+#pragma clang module contents
+#pragma clang module begin A
+template<typename T> auto f() { return []{}; }
+#pragma clang module end
+#pragma clang module endbuild
+
+#pragma clang module build B
+module B {}
+#pragma clang module contents
+#pragma clang module begin B
+#pragma clang module import A
+inline auto x1() { return f<int>(); }
+inline auto z() { return []{}; }
+inline auto x2() { return z(); }
+#pragma clang module end
+#pragma clang module endbuild
+
+#pragma clang module build C
+module C {}
+#pragma clang module contents
+#pragma clang module begin C
+#pragma clang module import A
+inline auto y1() { return f<int>(); }
+inline auto z() { return []{}; }
+inline auto y2() { return z(); }
+inline auto q() { return []{}; }
+inline auto y3() { return q(); }
+#pragma clang module end
+#pragma clang module endbuild
+
+inline auto q() { return []{}; }
+inline auto x3() { return q(); }
+
+#pragma clang module import B
+#pragma clang module import C
+using T = decltype(x1);
+using T = decltype(y1);
+
+using U = decltype(x2);
+using U = decltype(y2);
+
+using V = decltype(x3);
+using V = decltype(y3);
diff --git a/test/Modules/merge-static-locals.cpp b/test/Modules/merge-static-locals.cpp
new file mode 100644
index 0000000000..37ae22ee38
--- /dev/null
+++ b/test/Modules/merge-static-locals.cpp
@@ -0,0 +1,27 @@
+// RUN: %clang_cc1 -std=c++17 -fmodules -verify %s
+// expected-no-diagnostics
+
+#pragma clang module build A
+module A {}
+#pragma clang module contents
+#pragma clang module begin A
+template<int*> struct X {};
+auto get() { static int n; return X<&n>(); }
+using A = decltype(get());
+#pragma clang module end
+#pragma clang module endbuild
+
+#pragma clang module build B
+module B {}
+#pragma clang module contents
+#pragma clang module begin B
+template<int*> struct X {};
+auto get() { static int n; return X<&n>(); }
+using B = decltype(get());
+#pragma clang module end
+#pragma clang module endbuild
+
+#pragma clang module import A
+#pragma clang module import B
+using T = A;
+using T = B;
diff --git a/test/Modules/pr27401.cpp b/test/Modules/pr27401.cpp
index 7d5479cb92..8db3bbb009 100644
--- a/test/Modules/pr27401.cpp
+++ b/test/Modules/pr27401.cpp
@@ -1,6 +1,7 @@
// RUN: rm -rf %t
// RUN: %clang_cc1 -std=c++11 -I%S/Inputs/PR27401 -verify %s
// RUN: %clang_cc1 -std=c++11 -fmodules -fmodule-map-file=%S/Inputs/PR27401/module.modulemap -fmodules-cache-path=%t -I%S/Inputs/PR27401 -verify %s
+// RUN: %clang_cc1 -std=c++11 -fmodules -fmodule-map-file=%S/Inputs/PR27401/module.modulemap -fmodules-cache-path=%t -I%S/Inputs/PR27401 -verify %s -triple i686-windows
#include "a.h"
#define _LIBCPP_VECTOR