]> granicus.if.org Git - clang/commitdiff
Fix PR10447: lazily building name lookup tables for DeclContexts was broken.
authorRichard Smith <richard-llvm@metafoo.co.uk>
Tue, 13 Mar 2012 03:12:56 +0000 (03:12 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Tue, 13 Mar 2012 03:12:56 +0000 (03:12 +0000)
The deferred lookup table building step couldn't accurately tell which Decls
should be included in the lookup table, and consequently built different tables
in some cases.

Fix this by removing lazy building of DeclContext name lookup tables. In
practice, the laziness was frequently not worthwhile in C++, because we
performed lookup into most DeclContexts. In C, it had a bit more value,
since there is no qualified lookup.

In the place of lazy lookup table building, we simply don't build lookup tables
for function DeclContexts at all. Such name lookup tables are not useful, since
they don't capture the scoping information required to correctly perform name
lookup in a function scope.

The resulting performance delta is within the noise on my testing, but appears
to be a very slight win for C++ and a very slight loss for C. The C performance
can probably be recovered (if it is a measurable problem) by avoiding building
the lookup table for the translation unit.

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

12 files changed:
include/clang/AST/DeclBase.h
lib/AST/DeclBase.cpp
lib/Sema/SemaDecl.cpp
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaDeclObjC.cpp
lib/Sema/SemaLookup.cpp
lib/Sema/SemaObjCProperty.cpp
lib/Sema/SemaTemplate.cpp
lib/Sema/SemaTemplateInstantiateDecl.cpp
test/CXX/dcl.dcl/basic.namespace/namespace.udecl/p11.cpp
test/Modules/namespaces.cpp
test/SemaCXX/PR10447.cpp [new file with mode: 0644]

index 351f77cef104842837494a1a93a476e386cebecf..28dc1b4fe95543ef808f36c14243909289ea9868 100644 (file)
@@ -1432,11 +1432,7 @@ public:
   /// visible from this context, as determined by
   /// NamedDecl::declarationReplaces, the previous declaration will be
   /// replaced with D.
-  ///
-  /// @param Recoverable true if it's okay to not add this decl to
-  /// the lookup tables because it can be easily recovered by walking
-  /// the declaration chains.
-  void makeDeclVisibleInContext(NamedDecl *D, bool Recoverable = true);
+  void makeDeclVisibleInContext(NamedDecl *D);
 
   /// udir_iterator - Iterates through the using-directives stored
   /// within this context.
@@ -1509,15 +1505,12 @@ private:
   ///
   /// Analogous to makeDeclVisibleInContext, but for the exclusive
   /// use of addDeclInternal().
-  void makeDeclVisibleInContextInternal(NamedDecl *D,
-                                        bool Recoverable = true);
+  void makeDeclVisibleInContextInternal(NamedDecl *D);
 
   friend class DependentDiagnostic;
   StoredDeclsMap *CreateStoredDeclsMap(ASTContext &C) const;
 
-  void buildLookup(DeclContext *DCtx);
-  void makeDeclVisibleInContextWithFlags(NamedDecl *D, bool Internal,
-                                         bool Recoverable);
+  void makeDeclVisibleInContextWithFlags(NamedDecl *D, bool Internal);
   void makeDeclVisibleInContextImpl(NamedDecl *D, bool Internal);
 };
 
index baba97d815158fbdd6a7a7e34f1afde4c1aa1d76..9171d2792c0e8f126f6a3d670f2f6724ce3064b8 100644 (file)
@@ -1082,64 +1082,28 @@ void DeclContext::addDeclInternal(Decl *D) {
     ND->getDeclContext()->makeDeclVisibleInContextInternal(ND);
 }
 
-/// buildLookup - Build the lookup data structure with all of the
-/// declarations in DCtx (and any other contexts linked to it or
-/// transparent contexts nested within it).
-void DeclContext::buildLookup(DeclContext *DCtx) {
-  llvm::SmallVector<DeclContext *, 2> Contexts;
-  DCtx->collectAllContexts(Contexts);
-  for (unsigned I = 0, N = Contexts.size(); I != N; ++I) {
-    for (decl_iterator D = Contexts[I]->decls_begin(),
-                    DEnd = Contexts[I]->decls_end();
-         D != DEnd; ++D) {
-      // Insert this declaration into the lookup structure, but only
-      // if it's semantically in its decl context.  During non-lazy
-      // lookup building, this is implicitly enforced by addDecl.
-      if (NamedDecl *ND = dyn_cast<NamedDecl>(*D))
-        if (D->getDeclContext() == Contexts[I])
-          makeDeclVisibleInContextImpl(ND, false);
-
-      // If this declaration is itself a transparent declaration context or
-      // inline namespace, add its members (recursively).
-      if (DeclContext *InnerCtx = dyn_cast<DeclContext>(*D))
-        if (InnerCtx->isTransparentContext() || InnerCtx->isInlineNamespace())
-          buildLookup(InnerCtx->getPrimaryContext());
-    }
-  }
-}
-
 DeclContext::lookup_result
 DeclContext::lookup(DeclarationName Name) {
   DeclContext *PrimaryContext = getPrimaryContext();
   if (PrimaryContext != this)
     return PrimaryContext->lookup(Name);
 
-  if (hasExternalVisibleStorage()) {
-    // Check to see if we've already cached the lookup results.
-    if (LookupPtr) {
-      StoredDeclsMap::iterator I = LookupPtr->find(Name);
-      if (I != LookupPtr->end())
-        return I->second.getLookupResult();
-    }
+  if (LookupPtr) {
+    StoredDeclsMap::iterator I = LookupPtr->find(Name);
+    if (I != LookupPtr->end())
+      return I->second.getLookupResult();
+  }
 
+  // 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 (hasExternalVisibleStorage()) {
     ExternalASTSource *Source = getParentASTContext().getExternalSource();
     return Source->FindExternalVisibleDeclsByName(this, Name);
   }
 
-  /// If there is no lookup data structure, build one now by walking
-  /// all of the linked DeclContexts (in declaration order!) and
-  /// inserting their values.
-  if (!LookupPtr) {
-    buildLookup(this);
-
-    if (!LookupPtr)
-      return lookup_result(lookup_iterator(0), lookup_iterator(0));
-  }
-
-  StoredDeclsMap::iterator Pos = LookupPtr->find(Name);
-  if (Pos == LookupPtr->end())
-    return lookup_result(lookup_iterator(0), lookup_iterator(0));
-  return Pos->second.getLookupResult();
+  return lookup_result(lookup_iterator(0), lookup_iterator(0));
 }
 
 DeclContext::lookup_const_result
@@ -1213,60 +1177,53 @@ bool DeclContext::InEnclosingNamespaceSetOf(const DeclContext *O) const {
   return false;
 }
 
-void DeclContext::makeDeclVisibleInContext(NamedDecl *D, bool Recoverable)
+void DeclContext::makeDeclVisibleInContext(NamedDecl *D)
 {
-    makeDeclVisibleInContextWithFlags(D, false, Recoverable);
+    makeDeclVisibleInContextWithFlags(D, false);
 }
 
-void DeclContext::makeDeclVisibleInContextInternal(NamedDecl *D, bool Recoverable)
+void DeclContext::makeDeclVisibleInContextInternal(NamedDecl *D)
 {
-    makeDeclVisibleInContextWithFlags(D, true, Recoverable);
+    makeDeclVisibleInContextWithFlags(D, true);
 }
 
-void DeclContext::makeDeclVisibleInContextWithFlags(NamedDecl *D, bool Internal, bool Recoverable) {
+void DeclContext::makeDeclVisibleInContextWithFlags(NamedDecl *D,
+                                                    bool Internal) {
+  // Skip unnamed declarations.
+  if (!D->getDeclName())
+    return;
+
+  // Skip entities that can't be found by name lookup into a particular
+  // context.
+  if ((D->getIdentifierNamespace() == 0 && !isa<UsingDirectiveDecl>(D)) ||
+      D->isTemplateParameter())
+    return;
+
+  // Skip template specializations.
   // FIXME: This feels like a hack. Should DeclarationName support
   // template-ids, or is there a better way to keep specializations
   // from being visible?
-  if (isa<ClassTemplateSpecializationDecl>(D) || D->isTemplateParameter())
+  if (isa<ClassTemplateSpecializationDecl>(D))
     return;
   if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
     if (FD->isFunctionTemplateSpecialization())
       return;
 
-  DeclContext *PrimaryContext = getPrimaryContext();
-  if (PrimaryContext != this) {
-    PrimaryContext->makeDeclVisibleInContextWithFlags(D, Internal, Recoverable);
-    return;
-  }
-
-  // If we already have a lookup data structure, perform the insertion
-  // into it. If we haven't deserialized externally stored decls, deserialize
-  // them so we can add the decl. Otherwise, be lazy and don't build that
-  // structure until someone asks for it.
-  if (LookupPtr || !Recoverable || hasExternalVisibleStorage())
-    makeDeclVisibleInContextImpl(D, Internal);
-
-  // If we are a transparent context or inline namespace, insert into our
-  // parent context, too. This operation is recursive.
-  if (isTransparentContext() || isInlineNamespace())
-    getParent()->makeDeclVisibleInContextWithFlags(D, Internal, Recoverable);
-
-  Decl *DCAsDecl = cast<Decl>(this);
-  // Notify that a decl was made visible unless it's a Tag being defined. 
-  if (!(isa<TagDecl>(DCAsDecl) && cast<TagDecl>(DCAsDecl)->isBeingDefined()))
-    if (ASTMutationListener *L = DCAsDecl->getASTMutationListener())
-      L->AddedVisibleDecl(this, D);
+  // Add the declaration into our lookup table (and those of containing
+  // contexts).
+  getPrimaryContext()->makeDeclVisibleInContextImpl(D, Internal);
 }
 
 void DeclContext::makeDeclVisibleInContextImpl(NamedDecl *D, bool Internal) {
-  // Skip unnamed declarations.
-  if (!D->getDeclName())
-    return;
-
-  // Skip entities that can't be found by name lookup into a particular
-  // context.
-  if ((D->getIdentifierNamespace() == 0 && !isa<UsingDirectiveDecl>(D)) ||
-      D->isTemplateParameter())
+  // Skip declarations within functions.
+  // FIXME: We shouldn't need to build lookup tables for function declarations
+  // ever, and we can't do so correctly because we can't model the nesting of
+  // scopes which occurs within functions. We use "qualified" lookup into
+  // function declarations when handling friend declarations inside nested
+  // classes, and consequently accept the following invalid code:
+  //
+  //   void f() { void g(); { int g; struct S { friend void g(); }; } }
+  if (isFunctionOrMethod() && !isa<FunctionDecl>(D))
     return;
 
   ASTContext *C = 0;
@@ -1289,23 +1246,32 @@ void DeclContext::makeDeclVisibleInContextImpl(NamedDecl *D, bool Internal) {
   StoredDeclsList &DeclNameEntries = (*LookupPtr)[D->getDeclName()];
   if (DeclNameEntries.isNull()) {
     DeclNameEntries.setOnlyValue(D);
-    return;
+  } else if (DeclNameEntries.HandleRedeclaration(D)) {
+    // This declaration has replaced an existing one for which
+    // declarationReplaces returns true.
+  } else {
+    // Put this declaration into the appropriate slot.
+    DeclNameEntries.AddSubsequentDecl(D);
   }
 
-  // If it is possible that this is a redeclaration, check to see if there is
-  // already a decl for which declarationReplaces returns true.  If there is
-  // one, just replace it and return.
-  if (DeclNameEntries.HandleRedeclaration(D))
-    return;
+  // If we are a transparent context or inline namespace, insert into our
+  // parent context, too. This operation is recursive.
+  if (isTransparentContext() || isInlineNamespace())
+    getParent()->getPrimaryContext()->makeDeclVisibleInContextImpl(D, Internal);
 
-  // Put this declaration into the appropriate slot.
-  DeclNameEntries.AddSubsequentDecl(D);
+  Decl *DCAsDecl = cast<Decl>(this);
+  // Notify that a decl was made visible unless we are a Tag being defined.
+  if (!(isa<TagDecl>(DCAsDecl) && cast<TagDecl>(DCAsDecl)->isBeingDefined()))
+    if (ASTMutationListener *L = DCAsDecl->getASTMutationListener())
+      L->AddedVisibleDecl(this, D);
 }
 
 /// Returns iterator range [First, Last) of UsingDirectiveDecls stored within
 /// this context.
 DeclContext::udir_iterator_range
 DeclContext::getUsingDirectives() const {
+  // FIXME: Use something more efficient than normal lookup for using
+  // directives. In C++, using directives are looked up more than anything else.
   lookup_const_result Result = lookup(UsingDirectiveDecl::getName());
   return udir_iterator_range(reinterpret_cast<udir_iterator>(Result.first),
                              reinterpret_cast<udir_iterator>(Result.second));
index a9c1ec1cd6863141ccd81b095a41c2ff5ed4c9fc..bee542234b092900c4fc1b1f98e95d100dcf0504 100644 (file)
@@ -8514,7 +8514,7 @@ CreateNewDecl:
       New->setAccess(PrevDecl->getAccess());
 
     DeclContext *DC = New->getDeclContext()->getRedeclContext();
-    DC->makeDeclVisibleInContext(New, /* Recoverable = */ false);
+    DC->makeDeclVisibleInContext(New);
     if (Name) // can be null along some error paths
       if (Scope *EnclosingScope = getScopeForDeclContext(S, DC))
         PushOnScopeChains(New, EnclosingScope, /* AddToContext = */ false);
@@ -8522,7 +8522,7 @@ CreateNewDecl:
     S = getNonFieldDeclScope(S);
     PushOnScopeChains(New, S, !IsForwardReference);
     if (IsForwardReference)
-      SearchDC->makeDeclVisibleInContext(New, /* Recoverable = */ false);
+      SearchDC->makeDeclVisibleInContext(New);
 
   } else {
     CurContext->addDecl(New);
index abbbe11561b52719a2f106b7ff1aed1567241e53..b6866d04e4aeb9abce736b1d8909c680ec2969a7 100644 (file)
@@ -5829,14 +5829,15 @@ Decl *Sema::ActOnUsingDirective(Scope *S,
 }
 
 void Sema::PushUsingDirective(Scope *S, UsingDirectiveDecl *UDir) {
-  // If scope has associated entity, then using directive is at namespace
-  // or translation unit scope. We add UsingDirectiveDecls, into
-  // it's lookup structure.
-  if (DeclContext *Ctx = static_cast<DeclContext*>(S->getEntity()))
+  // If the scope has an associated entity and the using directive is at
+  // namespace or translation unit scope, add the UsingDirectiveDecl into
+  // its lookup structure so qualified name lookup can find it.
+  DeclContext *Ctx = static_cast<DeclContext*>(S->getEntity());
+  if (Ctx && !Ctx->isFunctionOrMethod())
     Ctx->addDecl(UDir);
   else
-    // Otherwise it is block-sope. using-directives will affect lookup
-    // only to the end of scope.
+    // Otherwise, it is at block sope. The using-directives will affect lookup
+    // only to the end of the scope.
     S->PushUsingDirective(UDir);
 }
 
@@ -10195,7 +10196,7 @@ Decl *Sema::ActOnFriendFunctionDecl(Scope *S, Declarator &D,
   // lookup context is in lexical scope.
   if (!CurContext->isDependentContext()) {
     DC = DC->getRedeclContext();
-    DC->makeDeclVisibleInContext(ND, /* Recoverable=*/ false);
+    DC->makeDeclVisibleInContext(ND);
     if (Scope *EnclosingScope = getScopeForDeclContext(S, DC))
       PushOnScopeChains(ND, EnclosingScope, /*AddToContext=*/ false);
   }
index d224f1058a06ca893721602eb6d99cd9a2d87665..268ab368ceaf7b21e67ae2010cc39ab766ef7ae9 100644 (file)
@@ -1026,7 +1026,7 @@ void Sema::CheckImplementationIvars(ObjCImplementationDecl *ImpDecl,
     // Add ivar's to class's DeclContext.
     for (unsigned i = 0, e = numIvars; i != e; ++i) {
       ivars[i]->setLexicalDeclContext(ImpDecl);
-      IDecl->makeDeclVisibleInContext(ivars[i], false);
+      IDecl->makeDeclVisibleInContext(ivars[i]);
       ImpDecl->addDecl(ivars[i]);
     }
     
@@ -1050,7 +1050,7 @@ void Sema::CheckImplementationIvars(ObjCImplementationDecl *ImpDecl,
       }
       // Instance ivar to Implementation's DeclContext.
       ImplIvar->setLexicalDeclContext(ImpDecl);
-      IDecl->makeDeclVisibleInContext(ImplIvar, false);
+      IDecl->makeDeclVisibleInContext(ImplIvar);
       ImpDecl->addDecl(ImplIvar);
     }
     return;
index f1eb52241585911436241e21377e9e49790236e6..fbb0dc6b0f9009f9af65d099b54d0dceed943913 100644 (file)
@@ -106,7 +106,8 @@ namespace {
       assert(InnermostFileDC && InnermostFileDC->isFileContext());
 
       for (; S; S = S->getParent()) {
-        if (DeclContext *Ctx = static_cast<DeclContext*>(S->getEntity())) {
+        DeclContext *Ctx = static_cast<DeclContext*>(S->getEntity());
+        if (Ctx && !Ctx->isFunctionOrMethod()) {
           DeclContext *EffectiveDC = (Ctx->isFileContext() ? Ctx : InnermostFileDC);
           visit(Ctx, EffectiveDC);
         } else {
index 5c28db4c5e6194a46e8b98ec232cc425083f29b1..5ece8f11e767bc6fcbc39bcba12424b3726f201c 100644 (file)
@@ -740,7 +740,7 @@ Decl *Sema::ActOnPropertyImplDecl(Scope *S,
                                   ObjCIvarDecl::Private,
                                   (Expr *)0, true);
       ClassImpDecl->addDecl(Ivar);
-      IDecl->makeDeclVisibleInContext(Ivar, false);
+      IDecl->makeDeclVisibleInContext(Ivar);
       property->setPropertyIvarDecl(Ivar);
 
       if (!getLangOpts().ObjCNonFragileABI)
index af6b319e901b6989fd8042a5290c9de9a2d33ce0..e76a2538693a94d0eb60976e970fc7d8570d2b1d 100644 (file)
@@ -1088,7 +1088,7 @@ Sema::CheckClassTemplate(Scope *S, unsigned TagSpec, TagUseKind TUK,
     // Friend templates are visible in fairly strange ways.
     if (!CurContext->isDependentContext()) {
       DeclContext *DC = SemanticContext->getRedeclContext();
-      DC->makeDeclVisibleInContext(NewTemplate, /* Recoverable = */ false);
+      DC->makeDeclVisibleInContext(NewTemplate);
       if (Scope *EnclosingScope = getScopeForDeclContext(S, DC))
         PushOnScopeChains(NewTemplate, EnclosingScope,
                           /* AddToContext = */ false);
index 56ce50e592cf0491872f017efc83fa486ab0d3a8..f10670c47a694edc35925a4fdaba699e922b94d1 100644 (file)
@@ -817,7 +817,7 @@ Decl *TemplateDeclInstantiator::VisitClassTemplateDecl(ClassTemplateDecl *D) {
 
   // Finish handling of friends.
   if (isFriend) {
-    DC->makeDeclVisibleInContext(Inst, /*Recoverable*/ false);
+    DC->makeDeclVisibleInContext(Inst);
     Inst->setLexicalDeclContext(Owner);
     RecordInst->setLexicalDeclContext(Owner);
     return Inst;
@@ -1189,7 +1189,7 @@ Decl *TemplateDeclInstantiator::VisitFunctionDecl(FunctionDecl *D,
       PrevDecl = Function->getPreviousDecl();
 
     PrincipalDecl->setObjectOfFriendDecl(PrevDecl != 0);
-    DC->makeDeclVisibleInContext(PrincipalDecl, /*Recoverable=*/ false);
+    DC->makeDeclVisibleInContext(PrincipalDecl);
 
     bool queuedInstantiation = false;
 
index 99903b57bf8cef1e47fc7afb1ffa5736ec820d39..c7966ce643f1bb36cf81debf603ea11725ddc658 100644 (file)
@@ -70,10 +70,8 @@ namespace test4 {
 }
 
 // FIXME: we should be able to diagnose both of these, but we can't.
-// ...I'm actually not sure why we can diagnose either of them; it's
-// probably a bug.
 namespace test5 {
-  namespace ns { void foo(int); } // expected-note {{target of using declaration}}
+  namespace ns { void foo(int); }
   template <typename T> class Test0 {
     void test() {
       int foo(T);
@@ -83,11 +81,11 @@ namespace test5 {
 
   template <typename T> class Test1 {
     void test() {
-      using ns::foo; // expected-note {{using declaration}}
-      int foo(T); // expected-error {{declaration conflicts with target of using declaration already in scope}}
+      using ns::foo;
+      int foo(T);
     }
   };
 
   template class Test0<int>;
-  template class Test1<int>; // expected-note {{in instantiation of member function}}
+  template class Test1<int>;
 }
index a51c65992ebd1a49e9e50e6bf3b2abd14802a257..19e0c5a991f95e72aedae77ac2e6c2a636d10e7a 100644 (file)
@@ -1,6 +1,10 @@
 // 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: *
+
 namespace N6 {
   char &f(char);
 }
diff --git a/test/SemaCXX/PR10447.cpp b/test/SemaCXX/PR10447.cpp
new file mode 100644 (file)
index 0000000..08644ad
--- /dev/null
@@ -0,0 +1,22 @@
+// RUN: %clang_cc1 -verify %s
+
+// PR12223
+namespace test1 {
+  namespace N {
+    extern "C" void f(struct S*);
+    void g(S*);
+  }
+  namespace N {
+    void f(struct S *s) {
+      g(s);
+    }
+  }
+}
+
+// PR10447
+namespace test2 {
+  extern "C" {
+    void f(struct Bar*) { }
+    test2::Bar *ptr;
+  }
+}