[clang] [clang] accept member specializations declared in class scope (PR #207256)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Jul 2 12:53:51 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Matheus Izvekov (mizvekov)
<details>
<summary>Changes</summary>
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
---
Full diff: https://github.com/llvm/llvm-project/pull/207256.diff
4 Files Affected:
- (modified) clang/docs/ReleaseNotes.md (+1)
- (modified) clang/lib/Sema/SemaDecl.cpp (+6)
- (modified) clang/lib/Sema/SemaTemplate.cpp (+5-4)
- (modified) clang/test/SemaTemplate/member-specialization.cpp (+41)
``````````diff
diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md
index ba9572e2e7c59..b590d88ff2921 100644
--- a/clang/docs/ReleaseNotes.md
+++ b/clang/docs/ReleaseNotes.md
@@ -792,6 +792,7 @@ latest release, please see the [Clang Web Site](https://clang.llvm.org) or the
- Fix a problem where a substitution failure when evaluating a type requirement
could directly make the program ill-formed.
- Typo correction now corrects the name qualifier for invalid template names.
+- Member specializations can now be declared in class scope.
- Fix a problem where pack index expressions where incorrectly being regarded as equivalent.
- Correctly diagnose narrowing in pack index expressions. (#GH205650)
- Fixed a bug where captured variables in non-mutable lambdas were incorrectly treated as mutable
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/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 at -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 at -1 {{out-of-line definition of 'C' does not match any declaration}}
+ };
+} // namespace GH206866
+
namespace GH205971 {
template<class> struct A {};
``````````
</details>
https://github.com/llvm/llvm-project/pull/207256
More information about the cfe-commits
mailing list