]> granicus.if.org Git - clang/commitdiff
Don't look outside the innermost enclosing namespace when
authorJohn McCall <rjmccall@apple.com>
Wed, 20 Mar 2013 01:53:00 +0000 (01:53 +0000)
committerJohn McCall <rjmccall@apple.com>
Wed, 20 Mar 2013 01:53:00 +0000 (01:53 +0000)
performing unqualified lookup for a friend class declaration.

rdar://13393749

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

lib/Sema/SemaDecl.cpp
test/CXX/dcl.dcl/basic.namespace/namespace.def/namespace.memdef/p3.cpp

index b3edc5ef2afa185e5a471ef1829dee54d3b28a63..1cffce080879a42a84ca6bf9257d465fe14d1674 100644 (file)
@@ -9467,6 +9467,8 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
     // shouldn't be diagnosing.
     LookupName(Previous, S);
 
+    // When declaring or defining a tag, ignore ambiguities introduced
+    // by types using'ed into this scope.
     if (Previous.isAmbiguous() && 
         (TUK == TUK_Definition || TUK == TUK_Declaration)) {
       LookupResult::Filter F = Previous.makeFilter();
@@ -9477,6 +9479,27 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK,
       }
       F.done();
     }
+
+    // C++11 [namespace.memdef]p3:
+    //   If the name in a friend declaration is neither qualified nor
+    //   a template-id and the declaration is a function or an
+    //   elaborated-type-specifier, the lookup to determine whether
+    //   the entity has been previously declared shall not consider
+    //   any scopes outside the innermost enclosing namespace.
+    //
+    // Does it matter that this should be by scope instead of by
+    // semantic context?
+    if (!Previous.empty() && TUK == TUK_Friend) {
+      DeclContext *EnclosingNS = SearchDC->getEnclosingNamespaceContext();
+      LookupResult::Filter F = Previous.makeFilter();
+      while (F.hasNext()) {
+        NamedDecl *ND = F.next();
+        DeclContext *DC = ND->getDeclContext()->getRedeclContext();
+        if (DC->isFileContext() && !EnclosingNS->Encloses(ND->getDeclContext()))
+          F.erase();
+      }
+      F.done();
+    }
     
     // Note:  there used to be some attempt at recovery here.
     if (Previous.isAmbiguous())
index 069ca0a9258885c1b8f1dd224122a66f6f74c4c5..11372dd48a10a66a8abd4b0868798be359ce0a16 100644 (file)
@@ -91,3 +91,104 @@ namespace test5 {
   template void f<int>(int);
   template void f<long>(long); //expected-note {{instantiation}}
 }
+
+// rdar://13393749
+namespace test6 {
+  class A;
+  namespace ns {
+    class B {
+      static void foo(); // expected-note {{implicitly declared private here}}
+      friend union A;
+    };
+
+    union A {
+      void test() {
+        B::foo();
+      }
+    };
+  }
+
+  class A {
+    void test() {
+      ns::B::foo(); // expected-error {{'foo' is a private member of 'test6::ns::B'}}
+    }
+  };
+}
+
+// We seem to be following a correct interpretation with these, but
+// the standard could probably be a bit clearer.
+namespace test7a {
+  namespace ns {
+    class A;
+  }
+
+  using namespace ns;
+  class B {
+    static void foo();
+    friend class A;
+  };
+
+  class ns::A {
+    void test() {
+      B::foo();
+    }
+  };
+}
+namespace test7b {
+  namespace ns {
+    class A;
+  }
+
+  using ns::A;
+  class B {
+    static void foo();
+    friend class A;
+  };
+
+  class ns::A {
+    void test() {
+      B::foo();
+    }
+  };
+}
+namespace test7c {
+  namespace ns1 {
+    class A;
+  }
+
+  namespace ns2 {
+    // ns1::A appears as if declared in test7c according to [namespace.udir]p2.
+    // I think that means we aren't supposed to find it.
+    using namespace ns1;
+    class B {
+      static void foo(); // expected-note {{implicitly declared private here}}
+      friend class A;
+    };
+  }
+
+  class ns1::A {
+    void test() {
+      ns2::B::foo(); // expected-error {{'foo' is a private member of 'test7c::ns2::B'}}
+    }
+  };
+}
+namespace test7d {
+  namespace ns1 {
+    class A;
+  }
+
+  namespace ns2 {
+    // Honor the lexical context of a using-declaration, though.
+    using ns1::A;
+    class B {
+      static void foo();
+      friend class A;
+    };
+  }
+
+  class ns1::A {
+    void test() {
+      ns2::B::foo();
+    }
+  };
+}