Fix unqualified lookup through using directives.

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

llvm-svn: 86669
This commit is contained in:
John McCall 2009-11-10 07:01:13 +00:00
parent 5b3def9b86
commit f6c8a4ef1f
2 changed files with 189 additions and 80 deletions

View File

@ -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;
if (DeclContext *Ctx = static_cast<DeclContext*>(S->getEntity())) {
ListTy list;
llvm::SmallPtrSet<DeclContext*, 8> visited;
if (NamespaceDecl *NS = dyn_cast<NamespaceDecl>(Ctx))
VisitedNS.insert(NS);
public:
UnqualUsingDirectiveSet() {}
AddNamespaceUsingDirectives(Context, Ctx, UDirs, /*ref*/ VisitedNS);
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());
} else {
Scope::udir_iterator I = S->using_directives_begin(),
End = S->using_directives_end();
for (; S; S = S->getParent()) {
if (!(S->getFlags() & Scope::DeclScope))
continue;
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);
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);
}
}
}
}
// 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);
}
}
if (queue.empty())
return;
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 @@ void Sema::LookupResult::print(llvm::raw_ostream &Out) {
// 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 @@ static bool LookupDirect(Sema::LookupResult &R, DeclContext *DC,
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 @@ Sema::CppLookupName(LookupResult &R, Scope *S, DeclarationName Name,
}
}
// 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 @@ Sema::CppLookupName(LookupResult &R, Scope *S, DeclarationName Name,
}
// 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) {

View File

@ -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++;
}