[clang] [Clang][Sema] Ignore previous partial specializations of member classtemplates explicitly specialized for a implicitly instantiated class template specialization (PR #113464)

via cfe-commits cfe-commits at lists.llvm.org
Wed Oct 23 07:42:08 PDT 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Krystian Stasiowski (sdkrystian)

<details>
<summary>Changes</summary>

Consider the following:
```cpp
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](http://eel.is/c++draft/temp.spec.partial.member#<!-- -->2):
> 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](http://eel.is/c++draft/temp.spec.partial.member#<!-- -->2). This patch implements the wording, fixing #<!-- -->51051.

---
Full diff: https://github.com/llvm/llvm-project/pull/113464.diff


3 Files Affected:

- (modified) clang/lib/Sema/SemaTemplate.cpp (+13) 
- (modified) clang/lib/Sema/SemaTemplateInstantiate.cpp (+30-4) 
- (added) clang/test/CXX/temp/temp.decls/temp.spec.partial/temp.spec.partial.member/p2.cpp (+53) 


``````````diff
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 62f13610b5285c..705a29194f7d91 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 457a9968c32a4a..1dccb4dfca74c7 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -3979,11 +3979,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;
@@ -4028,6 +4040,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);

``````````

</details>


https://github.com/llvm/llvm-project/pull/113464


More information about the cfe-commits mailing list