Don't look outside the innermost enclosing namespace when

performing unqualified lookup for a friend class declaration.

rdar://13393749

llvm-svn: 177473
This commit is contained in:
John McCall 2013-03-20 01:53:00 +00:00
parent be99cc3a3e
commit 3c581bf152
2 changed files with 124 additions and 0 deletions

View 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())

View 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();
}
};
}