https://github.com/mizvekov updated https://github.com/llvm/llvm-project/pull/207256
>From 757fa6a065d2d7dd430e0a5b210c7d2907f6d606 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 | 9 ++++ .../SemaTemplate/member-specialization.cpp | 41 +++++++++++++++++++ 5 files changed, 64 insertions(+), 4 deletions(-) diff --git a/clang/docs/ReleaseNotes.md b/clang/docs/ReleaseNotes.md index bc9bda95d1990..81b7d24162676 100644 --- a/clang/docs/ReleaseNotes.md +++ b/clang/docs/ReleaseNotes.md @@ -254,6 +254,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 6eb6c00f842c8..2fc4fac6d5351 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 96c3544847849..ad0e653e254dd 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -1980,10 +1980,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..ca712333f9410 100644 --- a/clang/test/CXX/drs/cwg7xx.cpp +++ b/clang/test/CXX/drs/cwg7xx.cpp @@ -153,6 +153,15 @@ 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 {}; // #cwg727-EcharF + template <> template <class> struct E<char>::F {}; + // expected-error@-1 {{redefinition of 'F'}} + // expected-note@#cwg727-EcharF {{previous definition is here}} }; 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
