[clang] [Clang][Sema] Ignore previous partial specializations of member class templates explicitly specialized for a implicitly instantiated class template specialization (PR #113464)
Krystian Stasiowski via cfe-commits
cfe-commits at lists.llvm.org
Wed Oct 30 07:08:25 PDT 2024
https://github.com/sdkrystian updated https://github.com/llvm/llvm-project/pull/113464
>From 7542bda555601ed537dd9bacde65537021cf402d Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
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 fcf05798d9c709..83e8447c0cdc0f 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 dea97bfce532c9..6e1492f8cd447e 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -3978,11 +3978,23 @@ 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->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 00000000000000..9a71da4bae37ca
--- /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<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;
+};
+
+static_assert(A<short>::B<int>::y == 0);
+static_assert(A<short>::B<int*>::y == 1);
+static_assert(A<short>::x<int> == 0);
+static_assert(A<short>::x<int*> == 1);
+
+template<>
+template<typename U>
+struct A<long>::B {
+ static constexpr int y = 2;
+};
+
+template<>
+template<typename U>
+struct A<long>::B<U&> {
+ static constexpr int y = 3;
+};
+
+template<>
+template<typename U>
+constexpr int A<long>::x = 2;
+
+template<>
+template<typename U>
+constexpr int A<long>::x<U&> = 3;
+
+static_assert(A<long>::B<int>::y == 2);
+static_assert(A<long>::B<int*>::y == 2);
+static_assert(A<long>::B<int&>::y == 3);
+static_assert(A<long>::x<int> == 2);
+static_assert(A<long>::x<int*> == 2);
+static_assert(A<long>::x<int&> == 3);
>From f2858862e71543a80a1f9790897ff6ba6e68d547 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
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 83e8447c0cdc0f..4503e60cff8c2f 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -4381,8 +4381,7 @@ 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
@@ -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 6e1492f8cd447e..b63063813f1b56 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<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
@@ -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 <sdkrystian at gmail.com>
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 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
^^^^^^^^^^^^^^^^^^^^^^^^^
>From a3356a0a0fe36902896c69c82ca30e4deda238d1 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
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 9a71da4bae37ca..0536cd23fd9adf 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<short>::x<int*> == 1);
template<>
template<typename U>
-struct A<long>::B {
+struct A<int>::B {
static constexpr int y = 2;
};
template<>
template<typename U>
-struct A<long>::B<U&> {
+struct A<int>::B<U&> {
static constexpr int y = 3;
};
template<>
template<typename U>
-constexpr int A<long>::x = 2;
+struct A<long>::B<U&> {
+ static constexpr int y = 4;
+};
+
+template<>
+template<typename U>
+constexpr int A<int>::x = 2;
template<>
template<typename U>
-constexpr int A<long>::x<U&> = 3;
-
-static_assert(A<long>::B<int>::y == 2);
-static_assert(A<long>::B<int*>::y == 2);
-static_assert(A<long>::B<int&>::y == 3);
-static_assert(A<long>::x<int> == 2);
-static_assert(A<long>::x<int*> == 2);
-static_assert(A<long>::x<int&> == 3);
+constexpr int A<int>::x<U&> = 3;
+
+template<>
+template<typename U>
+constexpr int A<long>::x<U&> = 4;
+
+static_assert(A<int>::B<int>::y == 2);
+static_assert(A<int>::B<int*>::y == 2);
+static_assert(A<int>::B<int&>::y == 3);
+static_assert(A<int>::x<int> == 2);
+static_assert(A<int>::x<int*> == 2);
+static_assert(A<int>::x<int&> == 3);
+static_assert(A<long>::B<int>::y == 0);
+static_assert(A<long>::B<int*>::y == 1);
+static_assert(A<long>::B<int&>::y == 4);
+static_assert(A<long>::x<int> == 0);
+static_assert(A<long>::x<int*> == 1);
+static_assert(A<long>::x<int&> == 4);
>From 792f0c1afdd8c1309d4ad83f81dadaf8bf5d77e3 Mon Sep 17 00:00:00 2001
From: Krystian Stasiowski <sdkrystian at gmail.com>
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 0536cd23fd9adf..7969b7efe597f9 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<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 = 2;
+ static constexpr int y = 3;
};
template<>
template<typename U>
struct A<int>::B<U&> {
- static constexpr int y = 3;
+ static constexpr int y = 4;
};
template<>
template<typename U>
struct A<long>::B<U&> {
- static constexpr int y = 4;
+ static constexpr int y = 5;
};
template<>
template<typename U>
-constexpr int A<int>::x = 2;
+constexpr int A<int>::x = 3;
template<>
template<typename U>
-constexpr int A<int>::x<U&> = 3;
+constexpr int A<int>::x<U&> = 4;
template<>
template<typename U>
-constexpr int A<long>::x<U&> = 4;
+constexpr int A<long>::x<U&> = 5;
-static_assert(A<int>::B<int>::y == 2);
-static_assert(A<int>::B<int*>::y == 2);
-static_assert(A<int>::B<int&>::y == 3);
-static_assert(A<int>::x<int> == 2);
-static_assert(A<int>::x<int*> == 2);
-static_assert(A<int>::x<int&> == 3);
+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 == 4);
+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&> == 4);
+static_assert(A<long>::x<int[]> == 2);
+static_assert(A<long>::x<int&> == 5);
More information about the cfe-commits
mailing list