[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