]> granicus.if.org Git - clang/commitdiff
Fix handling of module imports adding names to a DeclContext after qualified
authorRichard Smith <richard-llvm@metafoo.co.uk>
Thu, 7 Feb 2013 03:37:08 +0000 (03:37 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Thu, 7 Feb 2013 03:37:08 +0000 (03:37 +0000)
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.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@174577 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/DeclBase.h
include/clang/AST/DeclContextInternals.h
lib/AST/DeclBase.cpp
lib/Serialization/ASTReaderDecl.cpp
test/Modules/Inputs/namespaces-left.h
test/Modules/Inputs/namespaces-right.h
test/Modules/namespaces.cpp

index efac2fba647bea9da948b52619e6623abe8be047..8d9d4ded543f62f365455a560efdddb460473268 100644 (file)
@@ -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<StoredDeclsMap*, 1, bool> LookupPtr;
 
 protected:
@@ -963,10 +970,11 @@ protected:
   static std::pair<Decl *, Decl *>
   BuildDeclChain(ArrayRef<Decl*> 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
index 6acee7d85ce45e156d85beea806eb1839a7f87f4..84f3698d6b585facb82d152dbaf7337ab2c5d380 100644 (file)
@@ -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);
index 52ecdeb3183a00082cf7012dcfe4d1477c6cdbb0..3039c95462672c80624c060d9582b1ae77cd4e4e 100644 (file)
@@ -913,6 +913,24 @@ DeclContext::BuildDeclChain(ArrayRef<Decl*> 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<NamedDecl*> 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()) {
index 469b3938ee5a35297862bc97451ae563b808d024..c2ace3078298f3c43074ebc3269748611313335f 100644 (file)
@@ -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<DeclContext>(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<NamespaceDecl>(DC))
+      LookupDC = DC->getPrimaryContext();
     std::pair<uint64_t, uint64_t> 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());
 
index 7e9002aea8cdd2c0b50d8c6c531749cdaff0fd79..bd192afd2e89764e21fbc56dfff5a3bbb3400bdb 100644 (file)
@@ -1,5 +1,12 @@
 @import namespaces_top;
 
+float &global(float);
+float &global2(float);
+
+namespace LookupBeforeImport {
+  float &f(float);
+}
+
 namespace N1 { }
 
 namespace N1 { 
index b18aeb44786b5a5f7582e0d55b7aa88e99678d10..77f54ead65ab3425097399380d887343afd71dd4 100644 (file)
@@ -1,5 +1,12 @@
 @import namespaces_top;
 
+double &global(double);
+double &global2(double);
+
+namespace LookupBeforeImport {
+  double &f(double);
+}
+
 namespace N2 { }
 
 namespace N2 { }
index 871ae793d316d8a9d5c6233104c73ef328862ee6..151e7ea10145109f14292776410c36d9c9ea45c4 100644 (file)
@@ -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::<anonymous>::Foo *' with an rvalue of type 'N11::<anonymous>::Foo *'}}
   N12::consumeFoo(N12::getFoo()); // expected-error{{cannot initialize a parameter of type 'N12::<anonymous>::Foo *' with an rvalue of type 'N12::<anonymous>::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}}