Implement C++ [temp.local]p8, which specifies that a template

parameter hides a namespace-scope declararion with the same name in an
out-of-line definition of a template. The lookup requires a strange
interleaving of lexical and semantic scopes (go C++), which I have not
yet handled in the typo correction/code completion path.

Fixes PR6594.

llvm-svn: 98544
This commit is contained in:
Douglas Gregor 2010-03-15 14:33:29 +00:00
parent 145584e037
commit 6623006249
4 changed files with 152 additions and 10 deletions

View File

@ -590,13 +590,74 @@ static bool isNamespaceOrTranslationUnitScope(Scope *S) {
return false;
}
// Find the next outer declaration context corresponding to this scope.
static DeclContext *findOuterContext(Scope *S) {
for (S = S->getParent(); S; S = S->getParent())
if (S->getEntity())
return static_cast<DeclContext *>(S->getEntity())->getPrimaryContext();
// Find the next outer declaration context from this scope. This
// routine actually returns the semantic outer context, which may
// differ from the lexical context (encoded directly in the Scope
// stack) when we are parsing a member of a class template. In this
// case, the second element of the pair will be true, to indicate that
// name lookup should continue searching in this semantic context when
// it leaves the current template parameter scope.
static std::pair<DeclContext *, bool> findOuterContext(Scope *S) {
DeclContext *DC = static_cast<DeclContext *>(S->getEntity());
DeclContext *Lexical = 0;
for (Scope *OuterS = S->getParent(); OuterS;
OuterS = OuterS->getParent()) {
if (OuterS->getEntity()) {
Lexical
= static_cast<DeclContext *>(OuterS->getEntity())->getPrimaryContext();
break;
}
}
// C++ [temp.local]p8:
// In the definition of a member of a class template that appears
// outside of the namespace containing the class template
// definition, the name of a template-parameter hides the name of
// a member of this namespace.
//
// Example:
//
// namespace N {
// class C { };
//
// template<class T> class B {
// void f(T);
// };
// }
//
// template<class C> void N::B<C>::f(C) {
// C b; // C is the template parameter, not N::C
// }
//
// In this example, the lexical context we return is the
// TranslationUnit, while the semantic context is the namespace N.
if (!Lexical || !DC || !S->getParent() ||
!S->getParent()->isTemplateParamScope())
return std::make_pair(Lexical, false);
// Find the outermost template parameter scope.
// For the example, this is the scope for the template parameters of
// template<class C>.
Scope *OutermostTemplateScope = S->getParent();
while (OutermostTemplateScope->getParent() &&
OutermostTemplateScope->getParent()->isTemplateParamScope())
OutermostTemplateScope = OutermostTemplateScope->getParent();
return 0;
// Find the namespace context in which the original scope occurs. In
// the example, this is namespace N.
DeclContext *Semantic = DC;
while (!Semantic->isFileContext())
Semantic = Semantic->getParent();
// Find the declaration context just outside of the template
// parameter scope. This is the context in which the template is
// being lexically declaration (a namespace context). In the
// example, this is the global scope.
if (Lexical->isFileContext() && !Lexical->Equals(Semantic) &&
Lexical->Encloses(Semantic))
return std::make_pair(Semantic, true);
return std::make_pair(Lexical, false);
}
bool Sema::CppLookupName(LookupResult &R, Scope *S) {
@ -627,6 +688,7 @@ bool Sema::CppLookupName(LookupResult &R, Scope *S) {
// }
// }
//
DeclContext *OutsideOfTemplateParamDC = 0;
for (; S && !isNamespaceOrTranslationUnitScope(S); S = S->getParent()) {
// Check whether the IdResolver has anything in this scope.
bool Found = false;
@ -641,8 +703,25 @@ bool Sema::CppLookupName(LookupResult &R, Scope *S) {
return true;
}
if (DeclContext *Ctx = static_cast<DeclContext*>(S->getEntity())) {
DeclContext *OuterCtx = findOuterContext(S);
DeclContext *Ctx = static_cast<DeclContext*>(S->getEntity());
if (!Ctx && S->isTemplateParamScope() && OutsideOfTemplateParamDC &&
S->getParent() && !S->getParent()->isTemplateParamScope()) {
// We've just searched the last template parameter scope and
// found nothing, so look into the the contexts between the
// lexical and semantic declaration contexts returned by
// findOuterContext(). This implements the name lookup behavior
// of C++ [temp.local]p8.
Ctx = OutsideOfTemplateParamDC;
OutsideOfTemplateParamDC = 0;
}
if (Ctx) {
DeclContext *OuterCtx;
bool SearchAfterTemplateScope;
llvm::tie(OuterCtx, SearchAfterTemplateScope) = findOuterContext(S);
if (SearchAfterTemplateScope)
OutsideOfTemplateParamDC = OuterCtx;
for (; Ctx && Ctx->getPrimaryContext() != OuterCtx;
Ctx = Ctx->getLookupParent()) {
// We do not directly look into transparent contexts, since
@ -725,7 +804,10 @@ bool Sema::CppLookupName(LookupResult &R, Scope *S) {
}
}
if (Ctx) {
// If we have a context, and it's not a context stashed in the
// template parameter scope for an out-of-line definition, also
// look into that context.
if (Ctx && !(Found && S && S->isTemplateParamScope())) {
assert(Ctx->isFileContext() &&
"We should have been looking only at file context here already.");
@ -2216,13 +2298,14 @@ static void LookupVisibleDecls(Scope *S, LookupResult &Result,
}
}
// FIXME: C++ [temp.local]p8
DeclContext *Entity = 0;
if (S->getEntity()) {
// Look into this scope's declaration context, along with any of its
// parent lookup contexts (e.g., enclosing classes), up to the point
// where we hit the context stored in the next outer scope.
Entity = (DeclContext *)S->getEntity();
DeclContext *OuterCtx = findOuterContext(S);
DeclContext *OuterCtx = findOuterContext(S).first; // FIXME
for (DeclContext *Ctx = Entity; Ctx && Ctx->getPrimaryContext() != OuterCtx;
Ctx = Ctx->getLookupParent()) {

View File

@ -0,0 +1,10 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
template<class T> struct A {
int B;
int f();
};
template<class B> int A<B>::f() {
return B;
}

View File

@ -0,0 +1,34 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
namespace N {
enum { C };
template<class T> class B {
void f(T);
};
}
template<class C> void N::B<C>::f(C) {
C b;
}
namespace N {
enum { D };
namespace M {
enum { C , D };
template<typename C> class X {
template<typename U> void f(C, U);
template<typename D> void g(C, D) {
C c;
D d;
}
};
}
}
template<typename C>
template<typename D>
void N::M::X<C>::f(C, D) {
C c;
D d;
}

View File

@ -0,0 +1,15 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
struct A {
struct B { void f(); };
int a;
int Y;
};
template<class B, class a> struct X : A {
B b; // A's B
a c; // expected-error{{unknown type name 'a'}}
void g() {
b.g(); // expected-error{{no member named 'g' in 'A::B'}}
}
};