Index: ../llvm/tools/clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- ../llvm/tools/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ ../llvm/tools/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -3392,6 +3392,8 @@ def note_specialized_decl : Note<"attempt to specialize declaration here">; def err_specialization_after_instantiation : Error< "explicit specialization of %0 after instantiation">; +def err_part_specialization_after_instantiation : Error< + "partial specialization of %0 after instantiation">; def note_instantiation_required_here : Note< "%select{implicit|explicit}0 instantiation first required here">; def err_template_spec_friend : Error< Index: ../llvm/tools/clang/lib/Sema/SemaTemplate.cpp =================================================================== --- ../llvm/tools/clang/lib/Sema/SemaTemplate.cpp +++ ../llvm/tools/clang/lib/Sema/SemaTemplate.cpp @@ -6217,6 +6217,38 @@ CanonType = Context.getTypeDeclType(Specialization); } + // C++ [temp.class.spec]p1: + // A partial specialization shall be declared before the first use of a class + // template specialization that would make use of partial specialization as + // the result of an implicit or explicit instantiation in every translation + // unit in which such a use occurs; no diagnostic is required. + if (isPartialSpecialization) { + auto *ThisPartialSpec = + static_cast(Specialization); + for (const auto &S : ClassTemplate->specializations()) { + TemplateDeductionInfo Info(KWLoc); + if (S->getSpecializationKind() != TSK_ExplicitSpecialization && + S->hasDefinition() && + !DeduceTemplateArguments(ThisPartialSpec, S->getTemplateArgs(), Info)) { + auto *InstantiatedFrom = + S->getInstantiatedFrom() + .dyn_cast(); + if (!InstantiatedFrom || + getMoreSpecializedPartialSpecialization( + ThisPartialSpec, InstantiatedFrom, KWLoc) == ThisPartialSpec) { + SourceRange Range(TemplateNameLoc, RAngleLoc); + Diag(TemplateNameLoc, diag::err_part_specialization_after_instantiation) + << Context.getTypeDeclType(ThisPartialSpec) << Range; + + Diag(S->getPointOfInstantiation(), + diag::note_instantiation_required_here) + << (S->getTemplateSpecializationKind() != + TSK_ImplicitInstantiation); + } + } + } + } + // 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: ../llvm/tools/clang/test/SemaTemplate/class-template-spec.cpp =================================================================== --- ../llvm/tools/clang/test/SemaTemplate/class-template-spec.cpp +++ ../llvm/tools/clang/test/SemaTemplate/class-template-spec.cpp @@ -177,3 +177,16 @@ > struct S; template struct S {}; // expected-error {{non-type template argument specializes a template parameter with dependent type 'T'}} } + +namespace PartialSpecAfterInstantiation { + template struct A {}; + + A a; // expected-note {{implicit instantiation}} + template struct A {}; // expected-error {{partial specialization}} + + template struct A; // expected-note {{explicit instantiation}} + template struct A {}; // expected-error {{partial specialization}} + + A* b; // that's fine + template struct A {}; +}