Author: Krystian Stasiowski Date: 2024-10-30T11:24:10-04:00 New Revision: 7d1e283bd3b4440aea9ac375ca51e2ee6b0e86f5
URL: https://github.com/llvm/llvm-project/commit/7d1e283bd3b4440aea9ac375ca51e2ee6b0e86f5 DIFF: https://github.com/llvm/llvm-project/commit/7d1e283bd3b4440aea9ac375ca51e2ee6b0e86f5.diff LOG: [Clang][Sema] Ignore previous partial specializations of member templates explicitly specialized for an implicitly instantiated class template specialization (#113464) Consider the following: ``` template<typename T> struct A { template<typename U> struct B { static constexpr int x = 0; // #1 }; template<typename U> struct B<U*> { static constexpr int x = 1; // #2 }; }; template<> template<typename U> struct A<long>::B { static constexpr int x = 2; // #3 }; static_assert(A<short>::B<int>::y == 0); // uses #1 static_assert(A<short>::B<int*>::y == 1); // uses #2 static_assert(A<long>::B<int>::y == 2); // uses #3 static_assert(A<long>::B<int*>::y == 2); // uses #3 ``` According to [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. The example above fails to compile because we currently don't implement [temp.spec.partial.member] p2. This patch implements the wording, fixing #51051. Added: clang/test/CXX/temp/temp.decls/temp.spec.partial/temp.spec.partial.member/p2.cpp Modified: clang/docs/ReleaseNotes.rst clang/lib/Sema/SemaTemplate.cpp clang/lib/Sema/SemaTemplateInstantiate.cpp Removed: ################################################################################ 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<VarTemplatePartialSpecializationDecl *, 4> 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<ClassTemplatePartialSpecializationDecl *, 4> 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<ClassTemplatePartialSpecializationDecl *, 4> 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<typename T> +struct A { + template<typename U> + struct B { + static constexpr int y = 0; + }; + + template<typename U> + struct B<U*> { + static constexpr int y = 1; + }; + + template<typename U> + static constexpr int x = 0; + + template<typename U> + static constexpr int x<U*> = 1; +}; + +template<typename T> +template<typename U> +struct A<T>::B<U[]> { + static constexpr int y = 2; +}; + +template<typename T> +template<typename U> +constexpr int A<T>::x<U[]> = 2; + +static_assert(A<short>::B<int>::y == 0); +static_assert(A<short>::B<int*>::y == 1); +static_assert(A<short>::B<int[]>::y == 2); +static_assert(A<short>::x<int> == 0); +static_assert(A<short>::x<int*> == 1); +static_assert(A<short>::x<int[]> == 2); + +template<> +template<typename U> +struct A<int>::B { + static constexpr int y = 3; +}; + +template<> +template<typename U> +struct A<int>::B<U&> { + static constexpr int y = 4; +}; + +template<> +template<typename U> +struct A<long>::B<U&> { + static constexpr int y = 5; +}; + +template<> +template<typename U> +constexpr int A<int>::x = 3; + +template<> +template<typename U> +constexpr int A<int>::x<U&> = 4; + +template<> +template<typename U> +constexpr int A<long>::x<U&> = 5; + +static_assert(A<int>::B<int>::y == 3); +static_assert(A<int>::B<int*>::y == 3); +static_assert(A<int>::B<int[]>::y == 3); +static_assert(A<int>::B<int&>::y == 4); +static_assert(A<int>::x<int> == 3); +static_assert(A<int>::x<int*> == 3); +static_assert(A<int>::x<int[]> == 3); +static_assert(A<int>::x<int&> == 4); +static_assert(A<long>::B<int>::y == 0); +static_assert(A<long>::B<int*>::y == 1); +static_assert(A<long>::B<int[]>::y == 2); +static_assert(A<long>::B<int&>::y == 5); +static_assert(A<long>::x<int> == 0); +static_assert(A<long>::x<int*> == 1); +static_assert(A<long>::x<int[]> == 2); +static_assert(A<long>::x<int&> == 5); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits