From cc20945c787a56abe418947fc6a2c520bcec66c0 Mon Sep 17 00:00:00 2001 From: Douglas Gregor Date: Mon, 7 Mar 2011 16:54:27 +0000 Subject: [PATCH] Support explicit template specialization and instantiation for members of a C++0x inline namespace within enclosing namespaces, as noted in C++0x [namespace.def]p8. Fixes , a libc++ failure where Clang was rejected an explicit specialization of std::swap (since libc++ puts it into an inline, versioned namespace std::__1). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@127162 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Sema/IdentifierResolver.h | 7 +- include/clang/Sema/Sema.h | 7 +- lib/Sema/IdentifierResolver.cpp | 8 ++- lib/Sema/SemaDecl.cpp | 29 ++++++--- .../basic.namespace/namespace.def/p8.cpp | 21 ++++++ .../temp/temp.spec/temp.expl.spec/p2-0x.cpp | 63 ++++++++++++++++++ .../temp/temp.spec/temp.explicit/p3-0x.cpp | 65 +++++++++++++++++++ 7 files changed, 186 insertions(+), 14 deletions(-) create mode 100644 test/CXX/temp/temp.spec/temp.explicit/p3-0x.cpp diff --git a/include/clang/Sema/IdentifierResolver.h b/include/clang/Sema/IdentifierResolver.h index 7e9d338293..770f146e6a 100644 --- a/include/clang/Sema/IdentifierResolver.h +++ b/include/clang/Sema/IdentifierResolver.h @@ -146,8 +146,13 @@ public: /// isDeclInScope - If 'Ctx' is a function/method, isDeclInScope returns true /// if 'D' is in Scope 'S', otherwise 'S' is ignored and isDeclInScope returns /// true if 'D' belongs to the given declaration context. + /// + /// \param ExplicitInstantiationOrSpecialization When true, we are checking + /// whether the declaration is in scope for the purposes of explicit template + /// instantiation or specialization. The default is false. bool isDeclInScope(Decl *D, DeclContext *Ctx, ASTContext &Context, - Scope *S = 0) const; + Scope *S = 0, + bool ExplicitInstantiationOrSpecialization = false) const; /// AddDecl - Link the decl to its shadowed decl chain. void AddDecl(NamedDecl *D); diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index d5444257a6..70ec778289 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -1058,7 +1058,12 @@ public: /// isDeclInScope - If 'Ctx' is a function/method, isDeclInScope returns true /// if 'D' is in Scope 'S', otherwise 'S' is ignored and isDeclInScope returns /// true if 'D' belongs to the given declaration context. - bool isDeclInScope(NamedDecl *&D, DeclContext *Ctx, Scope *S = 0); + /// + /// \param ExplicitInstantiationOrSpecialization When true, we are checking + /// whether the declaration is in scope for the purposes of explicit template + /// instantiation or specialization. The default is false. + bool isDeclInScope(NamedDecl *&D, DeclContext *Ctx, Scope *S = 0, + bool ExplicitInstantiationOrSpecialization = false); /// Finds the scope corresponding to the given decl context, if it /// happens to be an enclosing scope. Otherwise return NULL. diff --git a/lib/Sema/IdentifierResolver.cpp b/lib/Sema/IdentifierResolver.cpp index 3f16ed7723..10856225ad 100644 --- a/lib/Sema/IdentifierResolver.cpp +++ b/lib/Sema/IdentifierResolver.cpp @@ -104,7 +104,8 @@ IdentifierResolver::~IdentifierResolver() { /// if 'D' is in Scope 'S', otherwise 'S' is ignored and isDeclInScope returns /// true if 'D' belongs to the given declaration context. bool IdentifierResolver::isDeclInScope(Decl *D, DeclContext *Ctx, - ASTContext &Context, Scope *S) const { + ASTContext &Context, Scope *S, + bool ExplicitInstantiationOrSpecialization) const { Ctx = Ctx->getRedeclContext(); if (Ctx->isFunctionOrMethod()) { @@ -135,7 +136,10 @@ bool IdentifierResolver::isDeclInScope(Decl *D, DeclContext *Ctx, return false; } - return D->getDeclContext()->getRedeclContext()->Equals(Ctx); + DeclContext *DCtx = D->getDeclContext()->getRedeclContext(); + return ExplicitInstantiationOrSpecialization + ? Ctx->InEnclosingNamespaceSetOf(DCtx) + : Ctx->Equals(DCtx); } /// AddDecl - Link the decl to its shadowed decl chain. diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index 20e9be023a..0e114eaa86 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -496,8 +496,10 @@ void Sema::PushOnScopeChains(NamedDecl *D, Scope *S, bool AddToContext) { IdResolver.AddDecl(D); } -bool Sema::isDeclInScope(NamedDecl *&D, DeclContext *Ctx, Scope *S) { - return IdResolver.isDeclInScope(D, Ctx, Context, S); +bool Sema::isDeclInScope(NamedDecl *&D, DeclContext *Ctx, Scope *S, + bool ExplicitInstantiationOrSpecialization) { + return IdResolver.isDeclInScope(D, Ctx, Context, S, + ExplicitInstantiationOrSpecialization); } Scope *Sema::getScopeForDeclContext(Scope *S, DeclContext *DC) { @@ -519,12 +521,13 @@ static bool isOutOfScopePreviousDeclaration(NamedDecl *, /// as determined by isDeclInScope. static void FilterLookupForScope(Sema &SemaRef, LookupResult &R, DeclContext *Ctx, Scope *S, - bool ConsiderLinkage) { + bool ConsiderLinkage, + bool ExplicitInstantiationOrSpecialization) { LookupResult::Filter F = R.makeFilter(); while (F.hasNext()) { NamedDecl *D = F.next(); - if (SemaRef.isDeclInScope(D, Ctx, S)) + if (SemaRef.isDeclInScope(D, Ctx, S, ExplicitInstantiationOrSpecialization)) continue; if (ConsiderLinkage && @@ -2886,7 +2889,8 @@ Sema::ActOnTypedefDeclarator(Scope* S, Declarator& D, DeclContext* DC, // Merge the decl with the existing one if appropriate. If the decl is // in an outer scope, it isn't the same thing. - FilterLookupForScope(*this, Previous, DC, S, /*ConsiderLinkage*/ false); + FilterLookupForScope(*this, Previous, DC, S, /*ConsiderLinkage*/ false, + /*ExplicitInstantiationOrSpecialization=*/false); if (!Previous.empty()) { Redeclaration = true; MergeTypeDefDecl(NewTD, Previous); @@ -3167,7 +3171,8 @@ Sema::ActOnVariableDeclarator(Scope *S, Declarator &D, DeclContext *DC, // Don't consider existing declarations that are in a different // scope and are out-of-semantic-context declarations (if the new // declaration has linkage). - FilterLookupForScope(*this, Previous, DC, S, NewVD->hasLinkage()); + FilterLookupForScope(*this, Previous, DC, S, NewVD->hasLinkage(), + isExplicitSpecialization); if (!getLangOptions().CPlusPlus) CheckVariableDeclaration(NewVD, Previous, Redeclaration); @@ -3624,7 +3629,8 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, // Set the lexical context. NewFD->setLexicalDeclContext(CurContext); // Filter out previous declarations that don't match the scope. - FilterLookupForScope(*this, Previous, DC, S, NewFD->hasLinkage()); + FilterLookupForScope(*this, Previous, DC, S, NewFD->hasLinkage(), + /*ExplicitInstantiationOrSpecialization=*/false); } else { isFriend = D.getDeclSpec().isFriendSpecified(); bool isVirtual = D.getDeclSpec().isVirtualSpecified(); @@ -3884,7 +3890,9 @@ Sema::ActOnFunctionDeclarator(Scope* S, Declarator& D, DeclContext* DC, } // Filter out previous declarations that don't match the scope. - FilterLookupForScope(*this, Previous, DC, S, NewFD->hasLinkage()); + FilterLookupForScope(*this, Previous, DC, S, NewFD->hasLinkage(), + isExplicitSpecialization || + isFunctionTemplateSpecialization); if (isFriend) { // For now, claim that the objects have no previous declaration. @@ -6268,7 +6276,7 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, // in the same scope (so that the definition/declaration completes or // rementions the tag), reuse the decl. if (TUK == TUK_Reference || TUK == TUK_Friend || - isDeclInScope(PrevDecl, SearchDC, S)) { + isDeclInScope(PrevDecl, SearchDC, S, isExplicitSpecialization)) { // Make sure that this wasn't declared as an enum and now used as a // struct or something similar. if (!isAcceptableTagRedeclaration(PrevTagDecl, Kind, KWLoc, *Name)) { @@ -6412,7 +6420,8 @@ Decl *Sema::ActOnTag(Scope *S, unsigned TagSpec, TagUseKind TUK, Invalid = true; // Otherwise, only diagnose if the declaration is in scope. - } else if (!isDeclInScope(PrevDecl, SearchDC, S)) { + } else if (!isDeclInScope(PrevDecl, SearchDC, S, + isExplicitSpecialization)) { // do nothing // Diagnose implicit declarations introduced by elaborated types. diff --git a/test/CXX/dcl.dcl/basic.namespace/namespace.def/p8.cpp b/test/CXX/dcl.dcl/basic.namespace/namespace.def/p8.cpp index b9ad6e1c06..7c4a21c35a 100644 --- a/test/CXX/dcl.dcl/basic.namespace/namespace.def/p8.cpp +++ b/test/CXX/dcl.dcl/basic.namespace/namespace.def/p8.cpp @@ -95,3 +95,24 @@ namespace redecl { inline namespace n1 { }; } } + +// Normal redeclarations (not for explicit instantiations or +// specializations) are distinct in an inline namespace vs. not in an +// inline namespace. +namespace redecl2 { + inline namespace n1 { + void f(int) { } + struct X1 { }; + template void f(T) { } + template struct X2 { }; + int i = 71; + enum E { e }; + } + + void f(int) { } + struct X1 { }; + template void f(T) { } + template struct X2 { }; + int i = 71; + enum E { e }; +} diff --git a/test/CXX/temp/temp.spec/temp.expl.spec/p2-0x.cpp b/test/CXX/temp/temp.spec/temp.expl.spec/p2-0x.cpp index ed600e4ad9..4a17ceca7c 100644 --- a/test/CXX/temp/temp.spec/temp.expl.spec/p2-0x.cpp +++ b/test/CXX/temp/temp.spec/temp.expl.spec/p2-0x.cpp @@ -237,3 +237,66 @@ void test_func_template(N0::X0 xvp, void *vp, const void *cvp, xvp.ft1(vp, i); xvp.ft1(vp, u); } + +namespace has_inline_namespaces { + inline namespace inner { + template void f(T&); + + template + struct X0 { + struct MemberClass; + + void mem_func(); + + template + struct MemberClassTemplate; + + template + void mem_func_template(U&); + + static int value; + }; + } + + struct X1; + struct X2; + + // An explicit specialization whose declarator-id is not qualified + // shall be declared in the nearest enclosing namespace of the + // template, or, if the namespace is inline (7.3.1), any namespace + // from its enclosing namespace set. + template<> void f(X1&); + template<> void f(X2&); + + template<> struct X0 { }; + + template<> struct X0::MemberClass { }; + + template<> void X0::mem_func(); + + template<> template struct X0::MemberClassTemplate { }; + + template<> template void X0::mem_func_template(T&) { } + + template<> int X0::value = 12; +} + +struct X3; +struct X4; + +template<> void has_inline_namespaces::f(X3&); +template<> void has_inline_namespaces::f(X4&); + +template<> struct has_inline_namespaces::X0 { }; + +template<> struct has_inline_namespaces::X0::MemberClass { }; + +template<> void has_inline_namespaces::X0::mem_func(); + +template<> template +struct has_inline_namespaces::X0::MemberClassTemplate { }; + +template<> template +void has_inline_namespaces::X0::mem_func_template(T&) { } + +template<> int has_inline_namespaces::X0::value = 13; diff --git a/test/CXX/temp/temp.spec/temp.explicit/p3-0x.cpp b/test/CXX/temp/temp.spec/temp.explicit/p3-0x.cpp new file mode 100644 index 0000000000..fdb922abcf --- /dev/null +++ b/test/CXX/temp/temp.spec/temp.explicit/p3-0x.cpp @@ -0,0 +1,65 @@ +// RUN: %clang_cc1 -std=c++0x -verify %s + +// If the name declared in the explicit instantiation is an +// unqualified name, the explicit instantiation shall appear in the +// namespace where its template is declared or, if that namespace is +// inline (7.3.1), any namespace from its enclosing namespace set. + +namespace has_inline_namespaces { + inline namespace inner { + template void f(T&) {} + + template + struct X0 { + struct MemberClass {}; + + void mem_func() {} + + template + struct MemberClassTemplate {}; + + template + void mem_func_template(U&) {} + + static int value; + }; + } + + template int X0::value = 17; + + struct X1 {}; + struct X2 {}; + + template void f(X1&); + template void f(X2&); + + template struct X0; + + template struct X0::MemberClass; + + template void X0::mem_func(); + + template struct X0::MemberClassTemplate; + + template void X0::mem_func_template(X1&); + + template int X0::value; +} + +struct X3; +struct X4; + +template void has_inline_namespaces::f(X3&); +template void has_inline_namespaces::f(X4&); + +template struct has_inline_namespaces::X0; + +template struct has_inline_namespaces::X0::MemberClass; + +template void has_inline_namespaces::X0::mem_func(); + +template +struct has_inline_namespaces::X0::MemberClassTemplate; + +template +void has_inline_namespaces::X0::mem_func_template(X3&); -- 2.40.0