forked from OSchip/llvm-project
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:
parent
145584e037
commit
6623006249
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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'}}
|
||||
}
|
||||
};
|
Loading…
Reference in New Issue