Index: clang/include/clang/AST/DeclTemplate.h =================================================================== --- clang/include/clang/AST/DeclTemplate.h +++ clang/include/clang/AST/DeclTemplate.h @@ -1688,6 +1688,9 @@ /// Really a value of type TemplateSpecializationKind. unsigned SpecializationKind : 3; + /// If this class template specialization was declared at class scope (DR727). + unsigned IsSpecializedMember : 1; + protected: ClassTemplateSpecializationDecl(ASTContext &Context, Kind DK, TagKind TK, DeclContext *DC, SourceLocation StartLoc, @@ -1765,6 +1768,16 @@ PointOfInstantiation = Loc; } + void setIsSpecializedMember(bool V = true) { IsSpecializedMember = V; } + + /// If this class template specialization was declared at class scope (DR727). + /// For example, the specialization G below: + /// struct S { + /// template struct G {}; + /// template <> struct G {}; + /// }; + bool isSpecializedMember() const { return IsSpecializedMember; } + /// If this class template specialization is an instantiation of /// a template (rather than an explicit specialization), return the /// class template or class template partial specialization from which it @@ -2523,6 +2536,10 @@ /// no initializer. unsigned IsCompleteDefinition : 1; + /// If this variable template specialization was declared at class scope + /// (DR727). + unsigned IsSpecializedMember : 1; + protected: VarTemplateSpecializationDecl(Kind DK, ASTContext &Context, DeclContext *DC, SourceLocation StartLoc, SourceLocation IdLoc, @@ -2704,6 +2721,17 @@ return ExplicitInfo ? ExplicitInfo->TemplateKeywordLoc : SourceLocation(); } + void setIsSpecializedMember(bool V = true) { IsSpecializedMember = V; } + + /// If this variable template specialization was declared at class scope + /// (DR727). For example, the specialization v below: + /// struct S { + /// template static int v; + /// template <> static int v; + /// }; + bool isSpecializedMember() const { return IsSpecializedMember; } + + void Profile(llvm::FoldingSetNodeID &ID) const { Profile(ID, TemplateArgs->asArray(), getASTContext()); } Index: clang/lib/AST/DeclTemplate.cpp =================================================================== --- clang/lib/AST/DeclTemplate.cpp +++ clang/lib/AST/DeclTemplate.cpp @@ -691,14 +691,14 @@ SpecializedTemplate->getIdentifier(), PrevDecl), SpecializedTemplate(SpecializedTemplate), TemplateArgs(TemplateArgumentList::CreateCopy(Context, Args)), - SpecializationKind(TSK_Undeclared) { + SpecializationKind(TSK_Undeclared), IsSpecializedMember(0) { } ClassTemplateSpecializationDecl::ClassTemplateSpecializationDecl(ASTContext &C, Kind DK) : CXXRecordDecl(DK, TTK_Struct, C, nullptr, SourceLocation(), SourceLocation(), nullptr, nullptr), - SpecializationKind(TSK_Undeclared) {} + SpecializationKind(TSK_Undeclared), IsSpecializedMember(0) {} ClassTemplateSpecializationDecl * ClassTemplateSpecializationDecl::Create(ASTContext &Context, TagKind TK, @@ -1019,13 +1019,15 @@ SpecializedTemplate->getIdentifier(), T, TInfo, S), SpecializedTemplate(SpecializedTemplate), TemplateArgs(TemplateArgumentList::CreateCopy(Context, Args)), - SpecializationKind(TSK_Undeclared), IsCompleteDefinition(false) {} + SpecializationKind(TSK_Undeclared), IsCompleteDefinition(false), + IsSpecializedMember(0) {} VarTemplateSpecializationDecl::VarTemplateSpecializationDecl(Kind DK, ASTContext &C) : VarDecl(DK, C, nullptr, SourceLocation(), SourceLocation(), nullptr, QualType(), nullptr, SC_None), - SpecializationKind(TSK_Undeclared), IsCompleteDefinition(false) {} + SpecializationKind(TSK_Undeclared), IsCompleteDefinition(false), + IsSpecializedMember(0) {} VarTemplateSpecializationDecl *VarTemplateSpecializationDecl::Create( ASTContext &Context, DeclContext *DC, SourceLocation StartLoc, Index: clang/lib/Sema/SemaTemplate.cpp =================================================================== --- clang/lib/Sema/SemaTemplate.cpp +++ clang/lib/Sema/SemaTemplate.cpp @@ -3820,6 +3820,10 @@ VarTemplate->AddSpecialization(Specialization, InsertPos); } + // Record that this specialization was declared at class scope. + if (!CurContext->getRedeclContext()->isFileContext()) + Specialization->setIsSpecializedMember(); + // C++ [temp.expl.spec]p6: // If a template, a member template or the member of a class template is // explicitly specialized then that specialization shall be declared @@ -7662,8 +7666,13 @@ } else { CanonType = Context.getTypeDeclType(Specialization); } + } + // Record that this specialization was declared at class scope. + if (!CurContext->getRedeclContext()->isFileContext()) + Specialization->setIsSpecializedMember(); + // C++ [temp.expl.spec]p6: // If a template, a member template or the member of a class template is // explicitly specialized then that specialization shall be declared Index: clang/lib/Sema/SemaTemplateInstantiate.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiate.cpp +++ clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -70,27 +70,27 @@ if (VarTemplateSpecializationDecl *Spec = dyn_cast(D)) { // We're done when we hit an explicit specialization. - if (Spec->getSpecializationKind() == TSK_ExplicitSpecialization && - !isa(Spec)) + if (Spec->getSpecializationKind() != TSK_ExplicitSpecialization || + isa(Spec)) { + Result.addOuterTemplateArguments(&Spec->getTemplateInstantiationArgs()); + + // If this variable template specialization was instantiated from a + // specialized member that is a variable template, we're done. + assert(Spec->getSpecializedTemplate() && "No variable template?"); + llvm::PointerUnion Specialized + = Spec->getSpecializedTemplateOrPartial(); + if (VarTemplatePartialSpecializationDecl *Partial = + Specialized.dyn_cast()) { + if (Partial->isMemberSpecialization()) + return Result; + } else { + VarTemplateDecl *Tmpl = Specialized.get(); + if (Tmpl->isMemberSpecialization()) + return Result; + } + } else if (!Spec->isSpecializedMember()) return Result; - - Result.addOuterTemplateArguments(&Spec->getTemplateInstantiationArgs()); - - // If this variable template specialization was instantiated from a - // specialized member that is a variable template, we're done. - assert(Spec->getSpecializedTemplate() && "No variable template?"); - llvm::PointerUnion Specialized - = Spec->getSpecializedTemplateOrPartial(); - if (VarTemplatePartialSpecializationDecl *Partial = - Specialized.dyn_cast()) { - if (Partial->isMemberSpecialization()) - return Result; - } else { - VarTemplateDecl *Tmpl = Specialized.get(); - if (Tmpl->isMemberSpecialization()) - return Result; - } } // If we have a template template parameter with translation unit context, @@ -113,10 +113,19 @@ // Add template arguments from a class template instantiation. if (ClassTemplateSpecializationDecl *Spec = dyn_cast(Ctx)) { - // We're done when we hit an explicit specialization. + // We're done when we hit an explicit specialization ... if (Spec->getSpecializationKind() == TSK_ExplicitSpecialization && - !isa(Spec)) + !isa(Spec)) { + // ... unless this explicit specialization is a member of a template + // (DR727), in which case we need to skip past it to gather the + // enclosing template argument lists. + if (Spec->isSpecializedMember()) { + Ctx = Spec->getDeclContext(); + RelativeToPrimary = false; + continue; + } break; + } Result.addOuterTemplateArguments(&Spec->getTemplateInstantiationArgs()); Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2982,6 +2982,7 @@ InstD->setTypeAsWritten(WrittenTy); InstD->setExternLoc(D->getExternLoc()); InstD->setTemplateKeywordLoc(D->getTemplateKeywordLoc()); + InstD->setIsSpecializedMember(D->isSpecializedMember()); Owner->addDecl(InstD); Index: clang/lib/Serialization/ASTReaderDecl.cpp =================================================================== --- clang/lib/Serialization/ASTReaderDecl.cpp +++ clang/lib/Serialization/ASTReaderDecl.cpp @@ -2177,6 +2177,7 @@ D->TemplateArgs = TemplateArgumentList::CreateCopy(C, TemplArgs); D->PointOfInstantiation = ReadSourceLocation(); D->SpecializationKind = (TemplateSpecializationKind)Record.readInt(); + D->IsSpecializedMember = Record.readInt(); bool writtenAsCanonicalDecl = Record.readInt(); if (writtenAsCanonicalDecl) { @@ -2298,6 +2299,7 @@ D->PointOfInstantiation = ReadSourceLocation(); D->SpecializationKind = (TemplateSpecializationKind)Record.readInt(); D->IsCompleteDefinition = Record.readInt(); + D->IsSpecializedMember = Record.readInt(); bool writtenAsCanonicalDecl = Record.readInt(); if (writtenAsCanonicalDecl) { Index: clang/lib/Serialization/ASTWriterDecl.cpp =================================================================== --- clang/lib/Serialization/ASTWriterDecl.cpp +++ clang/lib/Serialization/ASTWriterDecl.cpp @@ -1452,6 +1452,7 @@ Record.AddTemplateArgumentList(&D->getTemplateArgs()); Record.AddSourceLocation(D->getPointOfInstantiation()); Record.push_back(D->getSpecializationKind()); + Record.push_back(D->isSpecializedMember()); Record.push_back(D->isCanonicalDecl()); if (D->isCanonicalDecl()) { @@ -1519,6 +1520,7 @@ Record.AddSourceLocation(D->getPointOfInstantiation()); Record.push_back(D->getSpecializationKind()); Record.push_back(D->IsCompleteDefinition); + Record.push_back(D->isSpecializedMember()); Record.push_back(D->isCanonicalDecl()); if (D->isCanonicalDecl()) { Index: clang/test/SemaCXX/member-spec-dr727.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/member-spec-dr727.cpp @@ -0,0 +1,52 @@ +// RUN: %clang_cc1 -std=c++17 -verify %s -emit-llvm -o - + +// expected-no-diagnostics + +namespace PR39031 { +template +struct S { + template + struct Wrapper; + + template <> + struct Wrapper { + template + void f(T) { + T x; + } + }; +}; + +void Run() { + S::Wrapper().f(1); +} +} // namespace PR39031 + +template struct is_same { static constexpr bool value = false; }; +template struct is_same { static constexpr bool value = true; }; + +namespace t1 { +template struct A { + template struct B; + + template <> struct B { + template struct C { + static_assert(is_same::value); + static_assert(is_same::value); + }; + }; +}; + +A::B::C x; +} + +namespace t2 { +template +struct A { + template static constexpr int p = 0; + template <> static constexpr int p = 1; +}; + +// FIXME: why aren't we selecting the specialization here? +static_assert(A::p == 0); +}