[clang] 7d1e283 - [Clang][Sema] Ignore previous partial specializations of member templates explicitly specialized for an implicitly instantiated class template specialization (#113464)

via cfe-commits cfe-commits at lists.llvm.org
Wed Oct 30 08:24:13 PDT 2024


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);


        


More information about the cfe-commits mailing list