From 7542bda555601ed537dd9bacde65537021cf402d Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski Date: Wed, 23 Oct 2024 10:28:23 -0400 Subject: [PATCH 1/5] [Clang][Sema] Ignore previous partial specializations of member class templates explicitly specialized for a implicitly instantiated class template specialization --- clang/lib/Sema/SemaTemplate.cpp | 13 +++++ clang/lib/Sema/SemaTemplateInstantiate.cpp | 34 ++++++++++-- .../temp.spec.partial.member/p2.cpp | 53 +++++++++++++++++++ 3 files changed, 96 insertions(+), 4 deletions(-) create mode 100644 clang/test/CXX/temp/temp.decls/temp.spec.partial/temp.spec.partial.member/p2.cpp diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index fcf05798d9c70..83e8447c0cdc0 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -4383,6 +4383,19 @@ Sema::CheckVarTemplateId(VarTemplateDecl *Template, SourceLocation TemplateLoc, for (unsigned I = 0, N = PartialSpecs.size(); I != N; ++I) { VarTemplatePartialSpecializationDecl *Partial = PartialSpecs[I]; + // 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->isMemberSpecialization() && + !Partial->isMemberSpecialization()) + continue; + TemplateDeductionInfo Info(FailedCandidates.getLocation()); if (TemplateDeductionResult Result = diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index dea97bfce532c..6e1492f8cd447 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -3978,11 +3978,23 @@ 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->isMemberSpecialization() && !CTPSD->isMemberSpecialization()) + continue; + TemplateDeductionInfo Info(Loc); - if (DeduceTemplateArguments(PartialSpecs[I], + if (DeduceTemplateArguments(CTPSD, ClassTemplateSpec->getTemplateArgs().asArray(), Info) == TemplateDeductionResult::Success) return true; @@ -4027,6 +4039,20 @@ getPatternForClassTemplateSpecialization( TemplateSpecCandidateSet FailedCandidates(PointOfInstantiation); for (unsigned I = 0, N = PartialSpecs.size(); I != N; ++I) { ClassTemplatePartialSpecializationDecl *Partial = PartialSpecs[I]; + // 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->isMemberSpecialization() && + !Partial->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 0000000000000..9a71da4bae37c --- /dev/null +++ b/clang/test/CXX/temp/temp.decls/temp.spec.partial/temp.spec.partial.member/p2.cpp @@ -0,0 +1,53 @@ +// 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; +}; + +static_assert(A::B::y == 0); +static_assert(A::B::y == 1); +static_assert(A::x == 0); +static_assert(A::x == 1); + +template<> +template +struct A::B { + static constexpr int y = 2; +}; + +template<> +template +struct A::B { + static constexpr int y = 3; +}; + +template<> +template +constexpr int A::x = 2; + +template<> +template +constexpr int A::x = 3; + +static_assert(A::B::y == 2); +static_assert(A::B::y == 2); +static_assert(A::B::y == 3); +static_assert(A::x == 2); +static_assert(A::x == 2); +static_assert(A::x == 3); From f2858862e71543a80a1f9790897ff6ba6e68d547 Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski Date: Wed, 30 Oct 2024 09:43:47 -0400 Subject: [PATCH 2/5] [FOLD] use most recent declaration --- clang/lib/Sema/SemaTemplate.cpp | 7 +++---- clang/lib/Sema/SemaTemplateInstantiate.cpp | 10 +++++----- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 83e8447c0cdc0..4503e60cff8c2 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -4381,8 +4381,7 @@ 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 @@ -4392,8 +4391,8 @@ Sema::CheckVarTemplateId(VarTemplateDecl *Template, SourceLocation TemplateLoc, // 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->isMemberSpecialization() && - !Partial->isMemberSpecialization()) + if (Template->getMostRecentDecl()->isMemberSpecialization() && + !Partial->getMostRecentDecl()->isMemberSpecialization()) continue; TemplateDeductionInfo Info(FailedCandidates.getLocation()); diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index 6e1492f8cd447..b63063813f1b5 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -3990,7 +3990,8 @@ bool Sema::usesPartialOrExplicitSpecialization( // 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->isMemberSpecialization() && !CTPSD->isMemberSpecialization()) + if (CTD->getMostRecentDecl()->isMemberSpecialization() && + !CTPSD->getMostRecentDecl()->isMemberSpecialization()) continue; TemplateDeductionInfo Info(Loc); @@ -4037,8 +4038,7 @@ 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 @@ -4049,8 +4049,8 @@ getPatternForClassTemplateSpecialization( // primary member template and its other partial specializations are // still considered for this specialization of the enclosing class // template. - if (Template->isMemberSpecialization() && - !Partial->isMemberSpecialization()) + if (Template->getMostRecentDecl()->isMemberSpecialization() && + !Partial->getMostRecentDecl()->isMemberSpecialization()) continue; TemplateDeductionInfo Info(FailedCandidates.getLocation()); From b8b0869d37b327df69a6be425407a279380ab8c3 Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski Date: Wed, 30 Oct 2024 09:51:40 -0400 Subject: [PATCH 3/5] [FOLD] add release note --- clang/docs/ReleaseNotes.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 6085352dfafe6..1a179e63f902f 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 ^^^^^^^^^^^^^^^^^^^^^^^^^ From a3356a0a0fe36902896c69c82ca30e4deda238d1 Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski Date: Wed, 30 Oct 2024 10:01:34 -0400 Subject: [PATCH 4/5] [FOLD] add more tests --- .../temp.spec.partial.member/p2.cpp | 38 +++++++++++++------ 1 file changed, 27 insertions(+), 11 deletions(-) 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 index 9a71da4bae37c..0536cd23fd9ad 100644 --- 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 @@ -27,27 +27,43 @@ static_assert(A::x == 1); template<> template -struct A::B { +struct A::B { static constexpr int y = 2; }; template<> template -struct A::B { +struct A::B { static constexpr int y = 3; }; template<> template -constexpr int A::x = 2; +struct A::B { + static constexpr int y = 4; +}; + +template<> +template +constexpr int A::x = 2; template<> template -constexpr int A::x = 3; - -static_assert(A::B::y == 2); -static_assert(A::B::y == 2); -static_assert(A::B::y == 3); -static_assert(A::x == 2); -static_assert(A::x == 2); -static_assert(A::x == 3); +constexpr int A::x = 3; + +template<> +template +constexpr int A::x = 4; + +static_assert(A::B::y == 2); +static_assert(A::B::y == 2); +static_assert(A::B::y == 3); +static_assert(A::x == 2); +static_assert(A::x == 2); +static_assert(A::x == 3); +static_assert(A::B::y == 0); +static_assert(A::B::y == 1); +static_assert(A::B::y == 4); +static_assert(A::x == 0); +static_assert(A::x == 1); +static_assert(A::x == 4); From 792f0c1afdd8c1309d4ad83f81dadaf8bf5d77e3 Mon Sep 17 00:00:00 2001 From: Krystian Stasiowski Date: Wed, 30 Oct 2024 10:08:11 -0400 Subject: [PATCH 5/5] [FOLD] add tests with out-of-line partial specializations --- .../temp.spec.partial.member/p2.cpp | 44 +++++++++++++------ 1 file changed, 30 insertions(+), 14 deletions(-) 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 index 0536cd23fd9ad..7969b7efe597f 100644 --- 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 @@ -20,50 +20,66 @@ struct A { 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 = 2; + static constexpr int y = 3; }; template<> template struct A::B { - static constexpr int y = 3; + static constexpr int y = 4; }; template<> template struct A::B { - static constexpr int y = 4; + static constexpr int y = 5; }; template<> template -constexpr int A::x = 2; +constexpr int A::x = 3; template<> template -constexpr int A::x = 3; +constexpr int A::x = 4; template<> template -constexpr int A::x = 4; +constexpr int A::x = 5; -static_assert(A::B::y == 2); -static_assert(A::B::y == 2); -static_assert(A::B::y == 3); -static_assert(A::x == 2); -static_assert(A::x == 2); -static_assert(A::x == 3); +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 == 4); +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 == 4); +static_assert(A::x == 2); +static_assert(A::x == 5);