diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 6085352dfafe6b..1a179e63f902f3 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -574,6 +574,8 @@ Bug Fixes to C++ Support (#GH95854). - Fixed an assertion failure when evaluating an invalid expression in an array initializer. (#GH112140) - Fixed an assertion failure in range calculations for conditional throw expressions. (#GH111854) +- Clang now correctly ignores previous partial specializations of member templates explicitly specialized for + an implicitly instantiated class template specialization. (#GH51051) Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index fcf05798d9c709..4503e60cff8c2f 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -4381,8 +4381,20 @@ Sema::CheckVarTemplateId(VarTemplateDecl *Template, SourceLocation TemplateLoc, SmallVector PartialSpecs; Template->getPartialSpecializations(PartialSpecs); - for (unsigned I = 0, N = PartialSpecs.size(); I != N; ++I) { - VarTemplatePartialSpecializationDecl *Partial = PartialSpecs[I]; + for (VarTemplatePartialSpecializationDecl *Partial : PartialSpecs) { + // C++ [temp.spec.partial.member]p2: + // If the primary member template is explicitly specialized for a given + // (implicit) specialization of the enclosing class template, the partial + // specializations of the member template are ignored for this + // specialization of the enclosing class template. If a partial + // specialization of the member template is explicitly specialized for a + // given (implicit) specialization of the enclosing class template, the + // primary member template and its other partial specializations are still + // considered for this specialization of the enclosing class template. + if (Template->getMostRecentDecl()->isMemberSpecialization() && + !Partial->getMostRecentDecl()->isMemberSpecialization()) + continue; + TemplateDeductionInfo Info(FailedCandidates.getLocation()); if (TemplateDeductionResult Result = diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index dea97bfce532c9..b63063813f1b56 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -3978,11 +3978,24 @@ bool Sema::usesPartialOrExplicitSpecialization( return true; SmallVector PartialSpecs; - ClassTemplateSpec->getSpecializedTemplate() - ->getPartialSpecializations(PartialSpecs); - for (unsigned I = 0, N = PartialSpecs.size(); I != N; ++I) { + ClassTemplateDecl *CTD = ClassTemplateSpec->getSpecializedTemplate(); + CTD->getPartialSpecializations(PartialSpecs); + for (ClassTemplatePartialSpecializationDecl *CTPSD : PartialSpecs) { + // C++ [temp.spec.partial.member]p2: + // If the primary member template is explicitly specialized for a given + // (implicit) specialization of the enclosing class template, the partial + // specializations of the member template are ignored for this + // specialization of the enclosing class template. If a partial + // specialization of the member template is explicitly specialized for a + // given (implicit) specialization of the enclosing class template, the + // primary member template and its other partial specializations are still + // considered for this specialization of the enclosing class template. + if (CTD->getMostRecentDecl()->isMemberSpecialization() && + !CTPSD->getMostRecentDecl()->isMemberSpecialization()) + continue; + TemplateDeductionInfo Info(Loc); - if (DeduceTemplateArguments(PartialSpecs[I], + if (DeduceTemplateArguments(CTPSD, ClassTemplateSpec->getTemplateArgs().asArray(), Info) == TemplateDeductionResult::Success) return true; @@ -4025,8 +4038,21 @@ getPatternForClassTemplateSpecialization( SmallVector PartialSpecs; Template->getPartialSpecializations(PartialSpecs); TemplateSpecCandidateSet FailedCandidates(PointOfInstantiation); - for (unsigned I = 0, N = PartialSpecs.size(); I != N; ++I) { - ClassTemplatePartialSpecializationDecl *Partial = PartialSpecs[I]; + for (ClassTemplatePartialSpecializationDecl *Partial : PartialSpecs) { + // C++ [temp.spec.partial.member]p2: + // If the primary member template is explicitly specialized for a given + // (implicit) specialization of the enclosing class template, the + // partial specializations of the member template are ignored for this + // specialization of the enclosing class template. If a partial + // specialization of the member template is explicitly specialized for a + // given (implicit) specialization of the enclosing class template, the + // primary member template and its other partial specializations are + // still considered for this specialization of the enclosing class + // template. + if (Template->getMostRecentDecl()->isMemberSpecialization() && + !Partial->getMostRecentDecl()->isMemberSpecialization()) + continue; + TemplateDeductionInfo Info(FailedCandidates.getLocation()); if (TemplateDeductionResult Result = S.DeduceTemplateArguments( Partial, ClassTemplateSpec->getTemplateArgs().asArray(), Info); diff --git a/clang/test/CXX/temp/temp.decls/temp.spec.partial/temp.spec.partial.member/p2.cpp b/clang/test/CXX/temp/temp.decls/temp.spec.partial/temp.spec.partial.member/p2.cpp new file mode 100644 index 00000000000000..7969b7efe597f9 --- /dev/null +++ b/clang/test/CXX/temp/temp.decls/temp.spec.partial/temp.spec.partial.member/p2.cpp @@ -0,0 +1,85 @@ +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s +// expected-no-diagnostics + +template +struct A { + template + struct B { + static constexpr int y = 0; + }; + + template + struct B { + static constexpr int y = 1; + }; + + template + static constexpr int x = 0; + + template + static constexpr int x = 1; +}; + +template +template +struct A::B { + static constexpr int y = 2; +}; + +template +template +constexpr int A::x = 2; + +static_assert(A::B::y == 0); +static_assert(A::B::y == 1); +static_assert(A::B::y == 2); +static_assert(A::x == 0); +static_assert(A::x == 1); +static_assert(A::x == 2); + +template<> +template +struct A::B { + static constexpr int y = 3; +}; + +template<> +template +struct A::B { + static constexpr int y = 4; +}; + +template<> +template +struct A::B { + static constexpr int y = 5; +}; + +template<> +template +constexpr int A::x = 3; + +template<> +template +constexpr int A::x = 4; + +template<> +template +constexpr int A::x = 5; + +static_assert(A::B::y == 3); +static_assert(A::B::y == 3); +static_assert(A::B::y == 3); +static_assert(A::B::y == 4); +static_assert(A::x == 3); +static_assert(A::x == 3); +static_assert(A::x == 3); +static_assert(A::x == 4); +static_assert(A::B::y == 0); +static_assert(A::B::y == 1); +static_assert(A::B::y == 2); +static_assert(A::B::y == 5); +static_assert(A::x == 0); +static_assert(A::x == 1); +static_assert(A::x == 2); +static_assert(A::x == 5);