[cfe-commits] r86669 - in /cfe/trunk: lib/Sema/SemaLookup.cpp test/CXX/dcl.dcl/basic.namespace/namespace.def/namespace.unnamed/p1.cpp

John McCall rjmccall at apple.com
Mon Nov 9 23:01:14 PST 2009


Author: rjmccall
Date: Tue Nov 10 01:01:13 2009
New Revision: 86669

URL: http://llvm.org/viewvc/llvm-project?rev=86669&view=rev
Log:
Fix unqualified lookup through using directives.

This is a pretty minimal test case;  I'll make a better one later.


Added:
    cfe/trunk/test/CXX/dcl.dcl/basic.namespace/namespace.def/namespace.unnamed/p1.cpp
Modified:
    cfe/trunk/lib/Sema/SemaLookup.cpp

Modified: cfe/trunk/lib/Sema/SemaLookup.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaLookup.cpp?rev=86669&r1=86668&r2=86669&view=diff

==============================================================================
--- cfe/trunk/lib/Sema/SemaLookup.cpp (original)
+++ cfe/trunk/lib/Sema/SemaLookup.cpp Tue Nov 10 01:01:13 2009
@@ -34,77 +34,163 @@
 
 using namespace clang;
 
-typedef llvm::SmallVector<UsingDirectiveDecl*, 4> UsingDirectivesTy;
-typedef llvm::DenseSet<NamespaceDecl*> NamespaceSet;
+namespace {
+  class UnqualUsingEntry {
+    const DeclContext *Nominated;
+    const DeclContext *CommonAncestor;
 
-/// UsingDirAncestorCompare - Implements strict weak ordering of
-/// UsingDirectives. It orders them by address of its common ancestor.
-struct UsingDirAncestorCompare {
+  public:
+    UnqualUsingEntry(const DeclContext *Nominated,
+                     const DeclContext *CommonAncestor)
+      : Nominated(Nominated), CommonAncestor(CommonAncestor) {
+    }
 
-  /// @brief Compares UsingDirectiveDecl common ancestor with DeclContext.
-  bool operator () (UsingDirectiveDecl *U, const DeclContext *Ctx) const {
-    return U->getCommonAncestor() < Ctx;
-  }
+    const DeclContext *getCommonAncestor() const {
+      return CommonAncestor;
+    }
 
-  /// @brief Compares UsingDirectiveDecl common ancestor with DeclContext.
-  bool operator () (const DeclContext *Ctx, UsingDirectiveDecl *U) const {
-    return Ctx < U->getCommonAncestor();
-  }
+    const DeclContext *getNominatedNamespace() const {
+      return Nominated;
+    }
 
-  /// @brief Compares UsingDirectiveDecl common ancestors.
-  bool operator () (UsingDirectiveDecl *U1, UsingDirectiveDecl *U2) const {
-    return U1->getCommonAncestor() < U2->getCommonAncestor();
-  }
-};
+    // Sort by the pointer value of the common ancestor.
+    struct Comparator {
+      bool operator()(const UnqualUsingEntry &L, const UnqualUsingEntry &R) {
+        return L.getCommonAncestor() < R.getCommonAncestor();
+      }
 
-/// AddNamespaceUsingDirectives - Adds all UsingDirectiveDecl's to heap UDirs
-/// (ordered by common ancestors), found in namespace NS,
-/// including all found (recursively) in their nominated namespaces.
-void AddNamespaceUsingDirectives(ASTContext &Context,
-                                 DeclContext *NS,
-                                 UsingDirectivesTy &UDirs,
-                                 NamespaceSet &Visited) {
-  DeclContext::udir_iterator I, End;
+      bool operator()(const UnqualUsingEntry &E, const DeclContext *DC) {
+        return E.getCommonAncestor() < DC;
+      }
 
-  for (llvm::tie(I, End) = NS->getUsingDirectives(); I !=End; ++I) {
-    UDirs.push_back(*I);
-    std::push_heap(UDirs.begin(), UDirs.end(), UsingDirAncestorCompare());
-    NamespaceDecl *Nominated = (*I)->getNominatedNamespace();
-    if (Visited.insert(Nominated).second)
-      AddNamespaceUsingDirectives(Context, Nominated, UDirs, /*ref*/ Visited);
-  }
-}
+      bool operator()(const DeclContext *DC, const UnqualUsingEntry &E) {
+        return DC < E.getCommonAncestor();
+      }
+    };
+  };
 
-/// AddScopeUsingDirectives - Adds all UsingDirectiveDecl's found in Scope S,
-/// including all found in the namespaces they nominate.
-static void AddScopeUsingDirectives(ASTContext &Context, Scope *S,
-                                    UsingDirectivesTy &UDirs) {
-  NamespaceSet VisitedNS;
+  /// A collection of using directives, as used by C++ unqualified
+  /// lookup.
+  class UnqualUsingDirectiveSet {
+    typedef llvm::SmallVector<UnqualUsingEntry, 8> ListTy;
+
+    ListTy list;
+    llvm::SmallPtrSet<DeclContext*, 8> visited;
+
+  public:
+    UnqualUsingDirectiveSet() {}
+
+    void visitScopeChain(Scope *S, Scope *InnermostFileScope) {
+      // C++ [namespace.udir]p1: 
+      //   During unqualified name lookup, the names appear as if they
+      //   were declared in the nearest enclosing namespace which contains
+      //   both the using-directive and the nominated namespace.
+      DeclContext *InnermostFileDC
+        = static_cast<DeclContext*>(InnermostFileScope->getEntity());
+      assert(InnermostFileDC && InnermostFileDC->isFileContext());
 
-  if (DeclContext *Ctx = static_cast<DeclContext*>(S->getEntity())) {
+      for (; S; S = S->getParent()) {
+        if (!(S->getFlags() & Scope::DeclScope))
+          continue;
 
-    if (NamespaceDecl *NS = dyn_cast<NamespaceDecl>(Ctx))
-      VisitedNS.insert(NS);
+        if (DeclContext *Ctx = static_cast<DeclContext*>(S->getEntity())) {
+          DeclContext *EffectiveDC = (Ctx->isFileContext() ? Ctx : InnermostFileDC);
+          visit(Ctx, EffectiveDC);
+        } else {
+          Scope::udir_iterator I = S->using_directives_begin(),
+                             End = S->using_directives_end();
+          
+          for (; I != End; ++I)
+            visit(I->getAs<UsingDirectiveDecl>(), InnermostFileDC);
+        }
+      }
+    }
 
-    AddNamespaceUsingDirectives(Context, Ctx, UDirs, /*ref*/ VisitedNS);
+    // Visits a context and collect all of its using directives
+    // recursively.  Treats all using directives as if they were
+    // declared in the context.
+    //
+    // A given context is only every visited once, so it is important
+    // that contexts be visited from the inside out in order to get
+    // the effective DCs right.
+    void visit(DeclContext *DC, DeclContext *EffectiveDC) {
+      if (!visited.insert(DC))
+        return;
+
+      addUsingDirectives(DC, EffectiveDC);
+    }
+
+    // Visits a using directive and collects all of its using
+    // directives recursively.  Treats all using directives as if they
+    // were declared in the effective DC.
+    void visit(UsingDirectiveDecl *UD, DeclContext *EffectiveDC) {
+      DeclContext *NS = UD->getNominatedNamespace();
+      if (!visited.insert(NS))
+        return;
+
+      addUsingDirective(UD, EffectiveDC);
+      addUsingDirectives(NS, EffectiveDC);
+    }
+
+    // Adds all the using directives in a context (and those nominated
+    // by its using directives, transitively) as if they appeared in
+    // the given effective context.
+    void addUsingDirectives(DeclContext *DC, DeclContext *EffectiveDC) {
+      llvm::SmallVector<DeclContext*,4> queue;
+      while (true) {
+        DeclContext::udir_iterator I, End;
+        for (llvm::tie(I, End) = DC->getUsingDirectives(); I != End; ++I) {
+          UsingDirectiveDecl *UD = *I;
+          DeclContext *NS = UD->getNominatedNamespace();
+          if (visited.insert(NS)) {
+            addUsingDirective(UD, EffectiveDC);
+            queue.push_back(NS);
+          }
+        }
 
-  } else {
-    Scope::udir_iterator I = S->using_directives_begin(),
-                         End = S->using_directives_end();
+        if (queue.empty())
+          return;
 
-    for (; I != End; ++I) {
-      UsingDirectiveDecl *UD = I->getAs<UsingDirectiveDecl>();
-      UDirs.push_back(UD);
-      std::push_heap(UDirs.begin(), UDirs.end(), UsingDirAncestorCompare());
-
-      NamespaceDecl *Nominated = UD->getNominatedNamespace();
-      if (!VisitedNS.count(Nominated)) {
-        VisitedNS.insert(Nominated);
-        AddNamespaceUsingDirectives(Context, Nominated, UDirs,
-                                    /*ref*/ VisitedNS);
+        DC = queue.back();
+        queue.pop_back();
       }
     }
-  }
+
+    // Add a using directive as if it had been declared in the given
+    // context.  This helps implement C++ [namespace.udir]p3:
+    //   The using-directive is transitive: if a scope contains a
+    //   using-directive that nominates a second namespace that itself
+    //   contains using-directives, the effect is as if the
+    //   using-directives from the second namespace also appeared in
+    //   the first.
+    void addUsingDirective(UsingDirectiveDecl *UD, DeclContext *EffectiveDC) {
+      // Find the common ancestor between the effective context and
+      // the nominated namespace.
+      DeclContext *Common = UD->getNominatedNamespace();
+      while (!Common->Encloses(EffectiveDC))
+        Common = Common->getParent();
+      
+      list.push_back(UnqualUsingEntry(UD->getNominatedNamespace(), Common));
+    }
+
+    void done() {
+      std::sort(list.begin(), list.end(), UnqualUsingEntry::Comparator());
+    }
+
+    typedef ListTy::iterator iterator;
+    typedef ListTy::const_iterator const_iterator;
+    
+    iterator begin() { return list.begin(); }
+    iterator end() { return list.end(); }
+    const_iterator begin() const { return list.begin(); }
+    const_iterator end() const { return list.end(); }
+
+    std::pair<const_iterator,const_iterator>
+    getNamespacesFor(DeclContext *DC) const {
+      return std::equal_range(begin(), end(), DC,
+                              UnqualUsingEntry::Comparator());
+    }
+  };
 }
 
 // Retrieve the set of identifier namespaces that correspond to a
@@ -306,13 +392,14 @@
 
 // Adds all qualifying matches for a name within a decl context to the
 // given lookup result.  Returns true if any matches were found.
-static bool LookupDirect(Sema::LookupResult &R, DeclContext *DC,
+static bool LookupDirect(Sema::LookupResult &R,
+                         const DeclContext *DC,
                          DeclarationName Name,
                          Sema::LookupNameKind NameKind,
                          unsigned IDNS) {
   bool Found = false;
 
-  DeclContext::lookup_iterator I, E;
+  DeclContext::lookup_const_iterator I, E;
   for (llvm::tie(I, E) = DC->lookup(Name); I != E; ++I)
     if (Sema::isAcceptableLookupResult(*I, NameKind, IDNS))
       R.addDecl(*I), Found = true;
@@ -320,28 +407,25 @@
   return Found;
 }
 
+// Performs C++ unqualified lookup into the given file context.
 static bool
 CppNamespaceLookup(Sema::LookupResult &R, ASTContext &Context, DeclContext *NS,
                    DeclarationName Name, Sema::LookupNameKind NameKind,
-                   unsigned IDNS, UsingDirectivesTy *UDirs = 0) {
+                   unsigned IDNS, UnqualUsingDirectiveSet &UDirs) {
 
   assert(NS && NS->isFileContext() && "CppNamespaceLookup() requires namespace!");
 
-  // Perform qualified name lookup into the LookupCtx.
+  // Perform direct name lookup into the LookupCtx.
   bool Found = LookupDirect(R, NS, Name, NameKind, IDNS);
 
-  if (UDirs) {
-    // For each UsingDirectiveDecl, which common ancestor is equal
-    // to NS, we preform qualified name lookup into namespace nominated by it.
-    UsingDirectivesTy::const_iterator UI, UEnd;
-    llvm::tie(UI, UEnd) =
-      std::equal_range(UDirs->begin(), UDirs->end(), NS,
-                       UsingDirAncestorCompare());
+  // Perform direct name lookup into the namespaces nominated by the
+  // using directives whose common ancestor is this namespace.
+  UnqualUsingDirectiveSet::const_iterator UI, UEnd;
+  llvm::tie(UI, UEnd) = UDirs.getNamespacesFor(NS);
 
-    for (; UI != UEnd; ++UI)
-      if (LookupDirect(R, (*UI)->getNominatedNamespace(), Name, NameKind, IDNS))
-        Found = true;
-  }
+  for (; UI != UEnd; ++UI)
+    if (LookupDirect(R, UI->getNominatedNamespace(), Name, NameKind, IDNS))
+      Found = true;
 
   R.resolveKind();
 
@@ -433,18 +517,19 @@
     }
   }
 
+  // Stop if we ran out of scopes.
+  // FIXME:  This really, really shouldn't be happening.
+  if (!S) return false;
+
   // Collect UsingDirectiveDecls in all scopes, and recursively all
   // nominated namespaces by those using-directives.
-  // UsingDirectives are pushed to heap, in common ancestor pointer value order.
+  //
   // FIXME: Cache this sorted list in Scope structure, and DeclContext, so we
   // don't build it for each lookup!
-  UsingDirectivesTy UDirs;
-  for (Scope *SC = Initial; SC; SC = SC->getParent())
-    if (SC->getFlags() & Scope::DeclScope)
-      AddScopeUsingDirectives(Context, SC, UDirs);
 
-  // Sort heapified UsingDirectiveDecls.
-  std::sort_heap(UDirs.begin(), UDirs.end(), UsingDirAncestorCompare());
+  UnqualUsingDirectiveSet UDirs;
+  UDirs.visitScopeChain(Initial, S);
+  UDirs.done();
 
   // Lookup namespace scope, and global scope.
   // Unqualified name lookup in C++ requires looking into scopes
@@ -473,7 +558,7 @@
     }
 
     // Look into context considering using-directives.
-    if (CppNamespaceLookup(R, Context, Ctx, Name, NameKind, IDNS, &UDirs))
+    if (CppNamespaceLookup(R, Context, Ctx, Name, NameKind, IDNS, UDirs))
       Found = true;
 
     if (Found) {

Added: cfe/trunk/test/CXX/dcl.dcl/basic.namespace/namespace.def/namespace.unnamed/p1.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.dcl/basic.namespace/namespace.def/namespace.unnamed/p1.cpp?rev=86669&view=auto

==============================================================================
--- cfe/trunk/test/CXX/dcl.dcl/basic.namespace/namespace.def/namespace.unnamed/p1.cpp (added)
+++ cfe/trunk/test/CXX/dcl.dcl/basic.namespace/namespace.def/namespace.unnamed/p1.cpp Tue Nov 10 01:01:13 2009
@@ -0,0 +1,24 @@
+// RUN: clang-cc -emit-llvm-only -verify %s
+
+// This lame little test was ripped straight from the standard.
+
+namespace {
+  int i; // expected-note {{candidate}}
+}
+void test0() { i++; }
+
+namespace A {
+  namespace {
+    int i; // expected-note {{candidate}}
+    int j;
+  }
+  void test1() { i++; }
+}
+
+using namespace A;
+
+void test2() {
+  i++; // expected-error {{reference to 'i' is ambiguous}}
+  A::i++;
+  j++;
+}





More information about the cfe-commits mailing list