From: John McCall Date: Wed, 20 Mar 2013 01:53:00 +0000 (+0000) Subject: Don't look outside the innermost enclosing namespace when X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=3c581bf152e5ff054d5de91ee5cb347e3c8e6988;p=platform%2Fupstream%2Fllvm.git Don't look outside the innermost enclosing namespace when performing unqualified lookup for a friend class declaration. rdar://13393749 llvm-svn: 177473 --- diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index b3edc5e..1cffce0 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -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()) diff --git a/clang/test/CXX/dcl.dcl/basic.namespace/namespace.def/namespace.memdef/p3.cpp b/clang/test/CXX/dcl.dcl/basic.namespace/namespace.def/namespace.memdef/p3.cpp index 069ca0a..11372dd 100644 --- a/clang/test/CXX/dcl.dcl/basic.namespace/namespace.def/namespace.memdef/p3.cpp +++ b/clang/test/CXX/dcl.dcl/basic.namespace/namespace.def/namespace.memdef/p3.cpp @@ -91,3 +91,104 @@ namespace test5 { template void f(int); template void f(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(); + } + }; +}