From c6c8e0ec96bb64f1b9f543d7c8317c6090f80a30 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Mon, 9 Jan 2012 17:30:44 +0000 Subject: [PATCH] Implement redeclaration merging for namespaces defined in distinct modules. Teach name lookup into namespaces to search in each of the merged DeclContexts as well as the (now-primary) DeclContext. This supports the common case where two different modules put something into the same namespace. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@147778 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/AST/DeclBase.h | 17 ++++++++++- lib/AST/DeclBase.cpp | 13 +++++--- lib/Serialization/ASTReader.cpp | 42 +++++++++++++++++++++----- lib/Serialization/ASTReaderDecl.cpp | 23 ++++++++++++-- test/Modules/Inputs/namespaces-left.h | 16 ++++++++++ test/Modules/Inputs/namespaces-right.h | 11 +++++++ test/Modules/namespaces.cpp | 21 +++++++++++++ 7 files changed, 127 insertions(+), 16 deletions(-) diff --git a/include/clang/AST/DeclBase.h b/include/clang/AST/DeclBase.h index b84ac85082..2964d8cf0a 100644 --- a/include/clang/AST/DeclBase.h +++ b/include/clang/AST/DeclBase.h @@ -514,6 +514,12 @@ protected: NextInContextAndBits.setInt(Bits); } + /// \brief Set the owning module ID. + void setOwningModuleID(unsigned ID) { + assert(isFromASTFile() && "Only works on a deserialized declaration"); + *((unsigned*)this - 2) = ID; + } + public: /// \brief Determine the availability of the given declaration. @@ -573,7 +579,16 @@ public: return *((const unsigned*)this - 1); return 0; } - + + /// \brief Retrieve the global ID of the module that owns this particular + /// declaration. + unsigned getOwningModuleID() const { + if (isFromASTFile()) + return *((const unsigned*)this - 2); + + return 0; + } + unsigned getIdentifierNamespace() const { return IdentifierNamespace; } diff --git a/lib/AST/DeclBase.cpp b/lib/AST/DeclBase.cpp index 0312cfbc4a..6a508dfcbe 100644 --- a/lib/AST/DeclBase.cpp +++ b/lib/AST/DeclBase.cpp @@ -45,14 +45,17 @@ void *Decl::AllocateDeserializedDecl(const ASTContext &Context, unsigned ID, unsigned Size) { // Allocate an extra 8 bytes worth of storage, which ensures that the - // resulting pointer will still be 8-byte aligned. At present, we're only - // using the latter 4 bytes of this storage. + // resulting pointer will still be 8-byte aligned. void *Start = Context.Allocate(Size + 8); void *Result = (char*)Start + 8; - // Store the global declaration ID - unsigned *IDPtr = (unsigned*)Result - 1; - *IDPtr = ID; + unsigned *PrefixPtr = (unsigned *)Result - 2; + + // Zero out the first 4 bytes; this is used to store the owning module ID. + PrefixPtr[0] = 0; + + // Store the global declaration ID in the second 4 bytes. + PrefixPtr[1] = ID; return Result; } diff --git a/lib/Serialization/ASTReader.cpp b/lib/Serialization/ASTReader.cpp index df158599de..fa21bf5fdb 100644 --- a/lib/Serialization/ASTReader.cpp +++ b/lib/Serialization/ASTReader.cpp @@ -4808,15 +4808,17 @@ namespace { /// declaration context. class DeclContextNameLookupVisitor { ASTReader &Reader; + llvm::SmallVectorImpl &Contexts; const DeclContext *DC; DeclarationName Name; SmallVectorImpl &Decls; public: DeclContextNameLookupVisitor(ASTReader &Reader, - const DeclContext *DC, DeclarationName Name, + SmallVectorImpl &Contexts, + DeclarationName Name, SmallVectorImpl &Decls) - : Reader(Reader), DC(DC), Name(Name), Decls(Decls) { } + : Reader(Reader), Contexts(Contexts), Name(Name), Decls(Decls) { } static bool visit(ModuleFile &M, void *UserData) { DeclContextNameLookupVisitor *This @@ -4824,11 +4826,20 @@ namespace { // Check whether we have any visible declaration information for // this context in this module. - ModuleFile::DeclContextInfosMap::iterator Info - = M.DeclContextInfos.find(This->DC); - if (Info == M.DeclContextInfos.end() || !Info->second.NameLookupTableData) - return false; + ModuleFile::DeclContextInfosMap::iterator Info; + bool FoundInfo = false; + for (unsigned I = 0, N = This->Contexts.size(); I != N; ++I) { + Info = M.DeclContextInfos.find(This->Contexts[I]); + if (Info != M.DeclContextInfos.end() && + Info->second.NameLookupTableData) { + FoundInfo = true; + break; + } + } + if (!FoundInfo) + return false; + // Look for this name within this module. ASTDeclContextNameLookupTable *LookupTable = (ASTDeclContextNameLookupTable*)Info->second.NameLookupTableData; @@ -4870,7 +4881,24 @@ ASTReader::FindExternalVisibleDeclsByName(const DeclContext *DC, DeclContext::lookup_iterator(0)); SmallVector Decls; - DeclContextNameLookupVisitor Visitor(*this, DC, Name, Decls); + + // Compute the declaration contexts we need to look into. Multiple such + // declaration contexts occur when two declaration contexts from disjoint + // modules get merged, e.g., when two namespaces with the same name are + // independently defined in separate modules. + SmallVector Contexts; + Contexts.push_back(DC); + + if (DC->isNamespace()) { + MergedDeclsMap::iterator Merged + = MergedDecls.find(const_cast(cast(DC))); + if (Merged != MergedDecls.end()) { + for (unsigned I = 0, N = Merged->second.size(); I != N; ++I) + Contexts.push_back(cast(GetDecl(Merged->second[I]))); + } + } + + DeclContextNameLookupVisitor Visitor(*this, Contexts, Name, Decls); ModuleMgr.visit(&DeclContextNameLookupVisitor::visit, &Visitor); ++NumVisibleDeclContextsRead; SetExternalVisibleDeclsForName(DC, Name, Decls); diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index 56e2d085e3..ffd768a94d 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -369,6 +369,9 @@ void ASTDeclReader::VisitDecl(Decl *D) { // Determine whether this declaration is part of a (sub)module. If so, it // may not yet be visible. if (unsigned SubmoduleID = readSubmoduleID(Record, Idx)) { + // Store the owning submodule ID in the declaration. + D->setOwningModuleID(SubmoduleID); + // Module-private declarations are never visible, so there is no work to do. if (!D->isModulePrivate()) { if (Module *Owner = Reader.getSubmodule(SubmoduleID)) { @@ -972,7 +975,8 @@ void ASTDeclReader::VisitNamespaceDecl(NamespaceDecl *D) { D->setInline(Record[Idx++]); D->LocStart = ReadSourceLocation(Record, Idx); D->RBraceLoc = ReadSourceLocation(Record, Idx); - + mergeRedeclarable(D, Redecl); + if (Redecl.getFirstID() == ThisDeclID) { // FIXME: If there's already an anonymous namespace, do we merge it with // this one? Or do we, when loading modules, just forget about anonymous @@ -1232,7 +1236,7 @@ ASTDeclReader::VisitRedeclarableTemplateDecl(RedeclarableTemplateDecl *D) { RedeclKind Kind = (RedeclKind)Record[Idx++]; // Determine the first declaration ID. - DeclID FirstDeclID; + DeclID FirstDeclID = 0; switch (Kind) { case FirstDeclaration: { FirstDeclID = ThisDeclID; @@ -1489,7 +1493,7 @@ ASTDeclReader::VisitRedeclarable(Redeclarable *D) { enum RedeclKind { FirstDeclaration = 0, FirstInFile, PointsToPrevious }; RedeclKind Kind = (RedeclKind)Record[Idx++]; - DeclID FirstDeclID; + DeclID FirstDeclID = 0; switch (Kind) { case FirstDeclaration: FirstDeclID = ThisDeclID; @@ -1545,6 +1549,13 @@ void ASTDeclReader::mergeRedeclarable(Redeclarable *D, D->RedeclLink = typename Redeclarable::PreviousDeclLink(ExistingCanon); + // When we merge a namespace, update its pointer to the first namespace. + if (NamespaceDecl *Namespace + = dyn_cast(static_cast(D))) { + Namespace->AnonOrFirstNamespaceAndInline.setPointer( + static_cast(static_cast(ExistingCanon))); + } + // Don't introduce DCanon into the set of pending declaration chains. Redecl.suppress(); @@ -1719,6 +1730,12 @@ static bool isSameEntity(NamedDecl *X, NamedDecl *Y) { VarX->getASTContext().hasSameType(VarX->getType(), VarY->getType()); } + // Namespaces with the same name and inlinedness match. + if (NamespaceDecl *NamespaceX = dyn_cast(X)) { + NamespaceDecl *NamespaceY = cast(Y); + return NamespaceX->isInline() == NamespaceY->isInline(); + } + // FIXME: Many other cases to implement. return false; } diff --git a/test/Modules/Inputs/namespaces-left.h b/test/Modules/Inputs/namespaces-left.h index 85e6d7dc60..6835cda0c4 100644 --- a/test/Modules/Inputs/namespaces-left.h +++ b/test/Modules/Inputs/namespaces-left.h @@ -9,3 +9,19 @@ namespace N1 { namespace N2 { float& f(float); } + + + + + +namespace N5 { + int &f(int); +} + +namespace N6 { + int &f(int); +} + +namespace N7 { + int &f(int); +} diff --git a/test/Modules/Inputs/namespaces-right.h b/test/Modules/Inputs/namespaces-right.h index 23c88bdbe9..0afef073c3 100644 --- a/test/Modules/Inputs/namespaces-right.h +++ b/test/Modules/Inputs/namespaces-right.h @@ -16,3 +16,14 @@ namespace N3 { double& f(double); } +namespace N5 { + double &f(double); +} + +namespace N6 { + double &f(double); +} + +namespace N7 { + double &f(double); +} diff --git a/test/Modules/namespaces.cpp b/test/Modules/namespaces.cpp index 75557ba4c1..e1a8d6e8b0 100644 --- a/test/Modules/namespaces.cpp +++ b/test/Modules/namespaces.cpp @@ -1,6 +1,10 @@ // RUN: rm -rf %t // RUN: %clang_cc1 -x objective-c++ -fmodules -fmodule-cache-path %t -I %S/Inputs %s -verify +namespace N6 { + char &f(char); +} + @import namespaces_left; @import namespaces_right; @@ -13,3 +17,20 @@ void test() { double &dr1 = N2::f(1.0); double &dr2 = N3::f(1.0); } + +// Test namespaces merged without a common first declaration. +namespace N5 { + char &f(char); +} + +void testMerged() { + int &ir1 = N5::f(17); + int &ir2 = N6::f(17); + int &ir3 = N7::f(17); + double &fr1 = N5::f(1.0); + double &fr2 = N6::f(1.0); + double &fr3 = N7::f(1.0); + char &cr1 = N5::f('a'); + char &cr2 = N6::f('b'); +} + -- 2.40.0