Index: clang/include/clang/AST/DeclTemplate.h =================================================================== --- clang/include/clang/AST/DeclTemplate.h +++ clang/include/clang/AST/DeclTemplate.h @@ -1765,6 +1765,19 @@ PointOfInstantiation = Loc; } + /// If this class template specialization was declared at class scope, as + /// opposed to out-of-line (DR727). For example, G below: + /// + /// \code + /// template struct S { + /// template struct G {}; + /// template <> struct G {}; + /// }; + /// \endcode + bool isClassScopeSpecialization() const { + return !getFirstDecl()->isOutOfLine(); + } + /// 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 @@ -2704,6 +2717,19 @@ return ExplicitInfo ? ExplicitInfo->TemplateKeywordLoc : SourceLocation(); } + /// If this variable template specialization was declared at class scope, as + /// opposed to out-of-line (DR727). For example, v below: + /// + /// \code + /// template struct S { + /// template static int v; + /// template <> static int v; + /// }; + /// \endcode + bool isClassScopeSpecialization() const { + return !getFirstDecl()->isOutOfLine(); + } + void Profile(llvm::FoldingSetNodeID &ID) const { Profile(ID, TemplateArgs->asArray(), getASTContext()); } Index: clang/lib/Sema/SemaTemplateInstantiate.cpp =================================================================== --- clang/lib/Sema/SemaTemplateInstantiate.cpp +++ clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -71,7 +71,8 @@ dyn_cast(D)) { // We're done when we hit an explicit specialization. if (Spec->getSpecializationKind() == TSK_ExplicitSpecialization && - !isa(Spec)) + !isa(Spec) && + !Spec->isClassScopeSpecialization()) return Result; Result.addOuterTemplateArguments(&Spec->getTemplateInstantiationArgs()); @@ -113,10 +114,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->isClassScopeSpecialization()) { + Ctx = Spec->getDeclContext(); + RelativeToPrimary = false; + continue; + } break; + } Result.addOuterTemplateArguments(&Spec->getTemplateInstantiationArgs()); 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); +}