From 711be1e89a56cdf679143ad18afaa58ed59f0584 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Mon, 15 Mar 2010 14:33:29 +0000 Subject: [PATCH] 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. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@98544 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Sema/SemaLookup.cpp | 103 ++++++++++++++++++++--- test/CXX/temp/temp.res/temp.local/p7.cpp | 10 +++ test/CXX/temp/temp.res/temp.local/p8.cpp | 34 ++++++++ test/CXX/temp/temp.res/temp.local/p9.cpp | 15 ++++ 4 files changed, 152 insertions(+), 10 deletions(-) create mode 100644 test/CXX/temp/temp.res/temp.local/p7.cpp create mode 100644 test/CXX/temp/temp.res/temp.local/p8.cpp create mode 100644 test/CXX/temp/temp.res/temp.local/p9.cpp diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index 744b806328..615f2f1d84 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -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(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 findOuterContext(Scope *S) { + DeclContext *DC = static_cast(S->getEntity()); + DeclContext *Lexical = 0; + for (Scope *OuterS = S->getParent(); OuterS; + OuterS = OuterS->getParent()) { + if (OuterS->getEntity()) { + Lexical + = static_cast(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 B { + // void f(T); + // }; + // } + // + // template void N::B::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. + 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(S->getEntity())) { - DeclContext *OuterCtx = findOuterContext(S); + DeclContext *Ctx = static_cast(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()) { diff --git a/test/CXX/temp/temp.res/temp.local/p7.cpp b/test/CXX/temp/temp.res/temp.local/p7.cpp new file mode 100644 index 0000000000..bd05e756a1 --- /dev/null +++ b/test/CXX/temp/temp.res/temp.local/p7.cpp @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +template struct A { + int B; + int f(); +}; + +template int A::f() { + return B; +} diff --git a/test/CXX/temp/temp.res/temp.local/p8.cpp b/test/CXX/temp/temp.res/temp.local/p8.cpp new file mode 100644 index 0000000000..a90c78cd4b --- /dev/null +++ b/test/CXX/temp/temp.res/temp.local/p8.cpp @@ -0,0 +1,34 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +namespace N { + enum { C }; + template class B { + void f(T); + }; +} + +template void N::B::f(C) { + C b; +} + +namespace N { + enum { D }; + namespace M { + enum { C , D }; + template class X { + template void f(C, U); + + template void g(C, D) { + C c; + D d; + } + }; + } +} + +template +template +void N::M::X::f(C, D) { + C c; + D d; +} diff --git a/test/CXX/temp/temp.res/temp.local/p9.cpp b/test/CXX/temp/temp.res/temp.local/p9.cpp new file mode 100644 index 0000000000..9ca8d8877d --- /dev/null +++ b/test/CXX/temp/temp.res/temp.local/p9.cpp @@ -0,0 +1,15 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s +struct A { + struct B { void f(); }; + int a; + int Y; +}; + +template 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'}} + } +}; -- 2.40.0