https://github.com/mizvekov updated 
https://github.com/llvm/llvm-project/pull/207256

>From 9d3af67b6fd516f2c0abb60983b90ef5fb4f3f17 Mon Sep 17 00:00:00 2001
From: Matheus Izvekov <[email protected]>
Date: Thu, 2 Jul 2026 15:35:23 -0300
Subject: [PATCH] [clang] accept member specializations declared in class scope

Explicit specializations are not restricted to namespace scope since CWG727 was
accepted as a DR.

Also fixes a crash upon error recovery in this case which was a recent
unreleased regression.

Fixes #206866
---
 clang/docs/ReleaseNotes.md                    |  3 ++
 clang/lib/Sema/SemaDecl.cpp                   |  6 +++
 clang/lib/Sema/SemaTemplate.cpp               |  9 ++--
 clang/test/CXX/drs/cwg7xx.cpp                 |  6 +++
 .../SemaTemplate/member-specialization.cpp    | 41 +++++++++++++++++++
 5 files changed, 61 insertions(+), 4 deletions(-)

diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index ba9572e2e7c59..9d96ec5439dcb 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -249,6 +249,9 @@ latest release, please see the [Clang Web 
Site](https://clang.llvm.org) or the
   operator()](https://cplusplus.github.io/CWG/issues/1780.html)
 - Clang now allows omitting `typename` before a template name in a
   conversion operator, implementing [CWG2413](https://wg21.link/cwg2413).
+- Member specializations can now be declared in class scope, according to the
+  resolution of [CWG727](https://wg21.link/cwg727). This is still not 
sufficient
+  to resolve that core issue.
 - Clang now uses non-reference types for structured bindings whose initializer
   returns a prvalue. This resolves [CWG3135](https://wg21.link/cwg3135).
 
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index f6d1d7a7877c7..1147bae36dad5 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -6407,6 +6407,12 @@ bool Sema::diagnoseQualifiedDeclaration(CXXScopeSpec 
&SS, DeclContext *DC,
   }
 
   if (Cur->isRecord()) {
+    // C++26 [temp.expl.spec]p3 (Adopted as a DR in CWG727):
+    //   An explicit specialization may be declared in any scope in which the
+    //   corresponding primary template may be defined.
+    if (IsMemberSpecialization)
+      return false;
+
     // Cannot qualify members within a class.
     Diag(Loc, diag::err_member_qualification)
       << Name << SS.getRange();
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 4f5eb36406f65..f1d72eaf4b572 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -1984,10 +1984,11 @@ DeclResult Sema::CheckClassTemplate(
         Invalid = true;
     }
 
-    if (TUK != TagUseKind::Friend && TUK != TagUseKind::Reference)
-      diagnoseQualifiedDeclaration(SS, SemanticContext, Name, NameLoc,
-                                   /*TemplateId-*/ nullptr,
-                                   /*IsMemberSpecialization*/ false);
+    if (TUK != TagUseKind::Friend && TUK != TagUseKind::Reference &&
+        diagnoseQualifiedDeclaration(SS, SemanticContext, Name, NameLoc,
+                                     /*TemplateId=*/nullptr,
+                                     IsMemberSpecialization))
+      return true;
 
     LookupQualifiedName(Previous, SemanticContext);
   } else {
diff --git a/clang/test/CXX/drs/cwg7xx.cpp b/clang/test/CXX/drs/cwg7xx.cpp
index 03a1cf1e73469..515071518d26e 100644
--- a/clang/test/CXX/drs/cwg7xx.cpp
+++ b/clang/test/CXX/drs/cwg7xx.cpp
@@ -153,6 +153,12 @@ namespace cwg727 { // cwg727: partial
       // expected-error@-2 {{variable template partial specialization of 'N' 
not in class 'A' or an enclosing namespace}}
       //   expected-note@#cwg727-N {{explicitly specialized declaration is 
here}}
     };
+
+    template <class> struct E {
+      template <class> struct F {};
+    };
+    template <> template <class> struct E<void>::F {};
+    template <> template <class> struct E<char>::F {};
   };
 
   template<> struct A::C<char>;
diff --git a/clang/test/SemaTemplate/member-specialization.cpp 
b/clang/test/SemaTemplate/member-specialization.cpp
index 894b8c4e4bd04..4b8ee193853bf 100644
--- a/clang/test/SemaTemplate/member-specialization.cpp
+++ b/clang/test/SemaTemplate/member-specialization.cpp
@@ -9,6 +9,47 @@ int f(X<int, int> x) {
   return x.as<int>();
 }
 
+namespace ClassScope1 {
+  struct A {
+    template <class> struct B {
+      template <int V> struct C {
+        static constexpr auto value = V / 2;
+      };
+    };
+    template <> template <int V> struct B<void>::C {
+      static constexpr auto value = V;
+    };
+    template <> template <int V> struct B<char>::C {
+      static constexpr auto value = 2 * V;
+    };
+  };
+
+  static_assert(A::B<void>::C<2>::value == 2);
+  static_assert(A::B<void>::C<3>::value == 3);
+  static_assert(A::B<char>::C<2>::value == 4);
+  static_assert(A::B<char>::C<3>::value == 6);
+  static_assert(A::B<int>::C<10>::value == 5);
+  static_assert(A::B<int>::C<20>::value == 10);
+} // ClassScope1
+
+namespace DifferentTemplateHeads1 {
+  struct A {
+    template <class> struct B {
+      template <class> struct C {}; // expected-note {{previous template 
declaration is here}}
+    };
+    template <> template <int> struct B<void>::C {};
+    // expected-error@-1 {{template parameter has a different kind in template 
redeclaration}}
+  };
+} // namespace DifferentTemplateHeads1
+
+namespace GH206866 {
+  class A {
+    template <class> class B {};
+    template <> template <class> class B<void>::C {};
+    // expected-error@-1 {{out-of-line definition of 'C' does not match any 
declaration}}
+  };
+} // namespace GH206866
+
 namespace GH205971 {
   template<class> struct A {};
 

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to