From 645d755d3e40bfb57f19ab7c474b832b71f3b095 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 7 Feb 2013 03:37:08 +0000 Subject: [PATCH] Fix handling of module imports adding names to a DeclContext after qualified name lookup has been performed in that context (this probably only happens in C++). 1) Whenever we add names to a context, set a flag on it, and if we perform lookup and discover that the context has had a lookup table built but has the flag set, update all entries in the lookup table with additional names from the external source. 2) When marking a DeclContext as having external visible decls, mark the context in which lookup is performed, not the one we are adding. These won't be the same if we're adding another copy of a pre-existing namespace. llvm-svn: 174577 --- clang/include/clang/AST/DeclBase.h | 27 ++++++++++----- clang/include/clang/AST/DeclContextInternals.h | 18 +++++++++- clang/lib/AST/DeclBase.cpp | 47 ++++++++++++++++++++------ clang/lib/Serialization/ASTReaderDecl.cpp | 15 +++++--- clang/test/Modules/Inputs/namespaces-left.h | 7 ++++ clang/test/Modules/Inputs/namespaces-right.h | 7 ++++ clang/test/Modules/namespaces.cpp | 25 ++++++++++---- 7 files changed, 117 insertions(+), 29 deletions(-) diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index efac2fb..8d9d4de 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -928,19 +928,26 @@ class DeclContext { /// \brief Whether this declaration context also has some external /// storage that contains additional declarations that are lexically /// part of this context. - mutable unsigned ExternalLexicalStorage : 1; + mutable bool ExternalLexicalStorage : 1; /// \brief Whether this declaration context also has some external /// storage that contains additional declarations that are visible /// in this context. - mutable unsigned ExternalVisibleStorage : 1; + mutable bool ExternalVisibleStorage : 1; + + /// \brief Whether this declaration context has had external visible + /// storage added since the last lookup. In this case, \c LookupPtr's + /// invariant may not hold and needs to be fixed before we perform + /// another lookup. + mutable bool NeedToReconcileExternalVisibleStorage : 1; /// \brief Pointer to the data structure used to lookup declarations /// within this context (or a DependentStoredDeclsMap if this is a /// dependent context), and a bool indicating whether we have lazily /// omitted any declarations from the map. We maintain the invariant - /// that, if the map contains an entry for a DeclarationName, then it - /// contains all relevant entries for that name. + /// that, if the map contains an entry for a DeclarationName (and we + /// haven't lazily omitted anything), then it contains all relevant + /// entries for that name. mutable llvm::PointerIntPair LookupPtr; protected: @@ -963,10 +970,11 @@ protected: static std::pair BuildDeclChain(ArrayRef Decls, bool FieldsAlreadyLoaded); - DeclContext(Decl::Kind K) - : DeclKind(K), ExternalLexicalStorage(false), - ExternalVisibleStorage(false), LookupPtr(0, false), FirstDecl(0), - LastDecl(0) { } + DeclContext(Decl::Kind K) + : DeclKind(K), ExternalLexicalStorage(false), + ExternalVisibleStorage(false), + NeedToReconcileExternalVisibleStorage(false), LookupPtr(0, false), + FirstDecl(0), LastDecl(0) {} public: ~DeclContext(); @@ -1497,6 +1505,8 @@ public: /// declarations visible in this context. void setHasExternalVisibleStorage(bool ES = true) { ExternalVisibleStorage = ES; + if (ES) + NeedToReconcileExternalVisibleStorage = true; } /// \brief Determine whether the given declaration is stored in the list of @@ -1512,6 +1522,7 @@ public: LLVM_ATTRIBUTE_USED void dumpDeclContext() const; private: + void reconcileExternalVisibleStorage(); void LoadLexicalDeclsFromExternalStorage() const; /// @brief Makes a declaration visible within this context, but diff --git a/clang/include/clang/AST/DeclContextInternals.h b/clang/include/clang/AST/DeclContextInternals.h index 6acee7d..84f3698 100644 --- a/clang/include/clang/AST/DeclContextInternals.h +++ b/clang/include/clang/AST/DeclContextInternals.h @@ -97,6 +97,22 @@ public: == Vec.end() && "list still contains decl"); } + /// \brief Remove any declarations which were imported from an external + /// AST source. + void removeExternalDecls() { + if (isNull()) { + // Nothing to do. + } else if (NamedDecl *Singleton = getAsDecl()) { + if (Singleton->isFromASTFile()) + *this = StoredDeclsList(); + } else { + DeclsTy &Vec = *getAsVector(); + Vec.erase(std::remove_if(Vec.begin(), Vec.end(), + std::mem_fun(&Decl::isFromASTFile)), + Vec.end()); + } + } + /// getLookupResult - Return an array of all the decls that this list /// represents. DeclContext::lookup_result getLookupResult() { @@ -186,7 +202,7 @@ public: // All other declarations go at the end of the list, but before any // tag declarations. But we can be clever about tag declarations // because there can only ever be one in a scope. - } else if (Vec.back()->hasTagIdentifierNamespace()) { + } else if (!Vec.empty() && Vec.back()->hasTagIdentifierNamespace()) { NamedDecl *TagD = Vec.back(); Vec.back() = D; Vec.push_back(TagD); diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index 52ecdeb..3039c95 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -913,6 +913,24 @@ DeclContext::BuildDeclChain(ArrayRef Decls, return std::make_pair(FirstNewDecl, PrevDecl); } +/// \brief We have just acquired external visible storage, and we already have +/// built a lookup map. For every name in the map, pull in the new names from +/// the external storage. +void DeclContext::reconcileExternalVisibleStorage() { + assert(NeedToReconcileExternalVisibleStorage); + if (!LookupPtr.getPointer()) + return; + + NeedToReconcileExternalVisibleStorage = false; + + StoredDeclsMap &Map = *LookupPtr.getPointer(); + ExternalASTSource *Source = getParentASTContext().getExternalSource(); + for (StoredDeclsMap::iterator I = Map.begin(); I != Map.end(); ++I) { + I->second.removeExternalDecls(); + Source->FindExternalVisibleDeclsByName(this, I->first); + } +} + /// \brief Load the declarations within this lexical storage from an /// external source. void @@ -963,9 +981,8 @@ ExternalASTSource::SetNoExternalVisibleDeclsForName(const DeclContext *DC, if (!(Map = DC->LookupPtr.getPointer())) Map = DC->CreateStoredDeclsMap(Context); - StoredDeclsList &List = (*Map)[Name]; - assert(List.isNull()); - (void) List; + // Add an entry to the map for this name, if it's not already present. + (*Map)[Name]; return DeclContext::lookup_result(); } @@ -975,7 +992,6 @@ ExternalASTSource::SetExternalVisibleDeclsForName(const DeclContext *DC, DeclarationName Name, ArrayRef Decls) { ASTContext &Context = DC->getParentASTContext(); - StoredDeclsMap *Map; if (!(Map = DC->LookupPtr.getPointer())) Map = DC->CreateStoredDeclsMap(Context); @@ -986,6 +1002,7 @@ ExternalASTSource::SetExternalVisibleDeclsForName(const DeclContext *DC, if (List.isNull()) List.setOnlyValue(*I); else + // FIXME: Need declarationReplaces handling for redeclarations in modules. List.AddSubsequentDecl(*I); } @@ -1145,6 +1162,10 @@ StoredDeclsMap *DeclContext::buildLookup() { /// DeclContext, a DeclContext linked to it, or a transparent context /// nested within it. void DeclContext::buildLookupImpl(DeclContext *DCtx) { + // FIXME: If buildLookup is supposed to return a complete map, we should not + // bail out in buildLookup if hasExternalVisibleStorage. If it is not required + // to include names from PCH and modules, we should use the noload_ iterators + // here. for (decl_iterator I = DCtx->decls_begin(), E = DCtx->decls_end(); I != E; ++I) { Decl *D = *I; @@ -1175,11 +1196,17 @@ DeclContext::lookup(DeclarationName Name) { return PrimaryContext->lookup(Name); if (hasExternalVisibleStorage()) { - // If a PCH has a result for this name, and we have a local declaration, we - // will have imported the PCH result when adding the local declaration. - // FIXME: For modules, we could have had more declarations added by module - // imoprts since we saw the declaration of the local name. - if (StoredDeclsMap *Map = LookupPtr.getPointer()) { + if (NeedToReconcileExternalVisibleStorage) + reconcileExternalVisibleStorage(); + + StoredDeclsMap *Map = LookupPtr.getPointer(); + if (LookupPtr.getInt()) + Map = buildLookup(); + + // If a PCH/module has a result for this name, and we have a local + // declaration, we will have imported the PCH/module result when adding the + // local declaration or when reconciling the module. + if (Map) { StoredDeclsMap::iterator I = Map->find(Name); if (I != Map->end()) return I->second.getLookupResult(); @@ -1224,7 +1251,7 @@ void DeclContext::localUncachedLookup(DeclarationName Name, } // If we have a lookup table, check there first. Maybe we'll get lucky. - if (Name) { + if (Name && !LookupPtr.getInt()) { if (StoredDeclsMap *Map = LookupPtr.getPointer()) { StoredDeclsMap::iterator Pos = Map->find(Name); if (Pos != Map->end()) { diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 469b393..c2ace30 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -2122,12 +2122,18 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) { // If this declaration is also a declaration context, get the // offsets for its tables of lexical and visible declarations. if (DeclContext *DC = dyn_cast(D)) { + // FIXME: This should really be + // DeclContext *LookupDC = DC->getPrimaryContext(); + // but that can walk the redeclaration chain, which might not work yet. + DeclContext *LookupDC = DC; + if (isa(DC)) + LookupDC = DC->getPrimaryContext(); std::pair Offsets = Reader.VisitDeclContext(DC); if (Offsets.first || Offsets.second) { if (Offsets.first != 0) DC->setHasExternalLexicalStorage(true); if (Offsets.second != 0) - DC->setHasExternalVisibleStorage(true); + LookupDC->setHasExternalVisibleStorage(true); if (ReadDeclContextStorage(*Loc.F, DeclsCursor, Offsets, Loc.F->DeclContextInfos[DC])) return 0; @@ -2139,7 +2145,7 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) { if (I != PendingVisibleUpdates.end()) { // There are updates. This means the context has external visible // storage, even if the original stored version didn't. - DC->setHasExternalVisibleStorage(true); + LookupDC->setHasExternalVisibleStorage(true); DeclContextVisibleUpdates &U = I->second; for (DeclContextVisibleUpdates::iterator UI = U.begin(), UE = U.end(); UI != UE; ++UI) { @@ -2150,8 +2156,9 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) { PendingVisibleUpdates.erase(I); } - if (!DC->hasExternalVisibleStorage() && DC->hasExternalLexicalStorage()) - DC->setMustBuildLookupTable(); + if (!LookupDC->hasExternalVisibleStorage() && + DC->hasExternalLexicalStorage()) + LookupDC->setMustBuildLookupTable(); } assert(Idx == Record.size()); diff --git a/clang/test/Modules/Inputs/namespaces-left.h b/clang/test/Modules/Inputs/namespaces-left.h index 7e9002a..bd192af 100644 --- a/clang/test/Modules/Inputs/namespaces-left.h +++ b/clang/test/Modules/Inputs/namespaces-left.h @@ -1,5 +1,12 @@ @import namespaces_top; +float &global(float); +float &global2(float); + +namespace LookupBeforeImport { + float &f(float); +} + namespace N1 { } namespace N1 { diff --git a/clang/test/Modules/Inputs/namespaces-right.h b/clang/test/Modules/Inputs/namespaces-right.h index b18aeb4..77f54ea 100644 --- a/clang/test/Modules/Inputs/namespaces-right.h +++ b/clang/test/Modules/Inputs/namespaces-right.h @@ -1,5 +1,12 @@ @import namespaces_top; +double &global(double); +double &global2(double); + +namespace LookupBeforeImport { + double &f(double); +} + namespace N2 { } namespace N2 { } diff --git a/clang/test/Modules/namespaces.cpp b/clang/test/Modules/namespaces.cpp index 871ae79..151e7ea 100644 --- a/clang/test/Modules/namespaces.cpp +++ b/clang/test/Modules/namespaces.cpp @@ -1,9 +1,8 @@ // RUN: rm -rf %t // RUN: %clang_cc1 -x objective-c++ -fmodules -fmodule-cache-path %t -I %S/Inputs %s -verify -// Importing modules which add declarations to a pre-existing non-imported -// overload set does not currently work. -// XFAIL: * +int &global(int); +int &global2(int); namespace N6 { char &f(char); @@ -11,6 +10,13 @@ namespace N6 { namespace N8 { } +namespace LookupBeforeImport { + int &f(int); +} +void testEarly() { + int &r = LookupBeforeImport::f(1); +} + @import namespaces_left; @import namespaces_right; @@ -18,10 +24,18 @@ void test() { int &ir1 = N1::f(1); int &ir2 = N2::f(1); int &ir3 = N3::f(1); + int &ir4 = global(1); + int &ir5 = ::global2(1); float &fr1 = N1::f(1.0f); float &fr2 = N2::f(1.0f); + float &fr3 = global(1.0f); + float &fr4 = ::global2(1.0f); + float &fr5 = LookupBeforeImport::f(1.0f); double &dr1 = N2::f(1.0); double &dr2 = N3::f(1.0); + double &dr3 = global(1.0); + double &dr4 = ::global2(1.0); + double &dr5 = LookupBeforeImport::f(1.0); } // Test namespaces merged without a common first declaration. @@ -54,11 +68,10 @@ void testMergedMerged() { // Test merging when using anonymous namespaces, which does not // actually perform any merging. -// other file: expected-note{{passing argument to parameter here}} void testAnonymousNotMerged() { N11::consumeFoo(N11::getFoo()); // expected-error{{cannot initialize a parameter of type 'N11::::Foo *' with an rvalue of type 'N11::::Foo *'}} N12::consumeFoo(N12::getFoo()); // expected-error{{cannot initialize a parameter of type 'N12::::Foo *' with an rvalue of type 'N12::::Foo *'}} } - -// other file: expected-note{{passing argument to parameter here}} +// namespaces-right.h: expected-note@60 {{passing argument to parameter here}} +// namespaces-right.h: expected-note@67 {{passing argument to parameter here}} -- 2.7.4