[clang] [Clang][Sema] Diagnose explicit specializations with object parameters that do not match their primary template (PR #89300)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Apr 18 13:25:08 PDT 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Krystian Stasiowski (sdkrystian)
<details>
<summary>Changes</summary>
According to [[dcl.fct] p6](https://eel.is/c++draft/dcl.fct#<!-- -->6) (with the resolution for [CWG2846](https://cplusplus.github.io/CWG/issues/2846.html) applied):
> An explicit-object-parameter-declaration shall appear only as the first _parameter-declaration_ of a _parameter-declaration-list_ of either:
> - a declaration of a member function or member function template, or
> - an explicit instantiation or explicit specialization of a templated member function, or
> - a _lambda-declarator_.
This patch diagnoses explicit specializations declared with an object parameter that does not match the primary template. For example:
```cpp
struct A
{
template<typename T>
void f(this T);
template<typename T>
void g(T);
template<typename T>
static void h(T);
};
template<>
void A::f(int); // error: an explicit specialization of an explicit object member function must have an explicit object parameter
template<>
void A::g(this int); // error: an explicit specialization of an implicit object member function cannot have an explicit object parameter
template<>
void A::h(this int); // error: an explicit specialization of a static member function cannot have an explicit object parameter
```
Since the presence/absence of an explicit object parameter is not taken into consideration during template argument deduction, the selected primary template (after partial ordering) _can_ have a different object parameter than the explicit specialization. We therefore do not diagnose the mismatch until the primary function template has been selected.
Note: This still needs a release note + more tests (for friend specializations). I plan to add diagnostics for explicit instantiations in this patch as well, I just ran out of time today :)
---
Full diff: https://github.com/llvm/llvm-project/pull/89300.diff
3 Files Affected:
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+4)
- (modified) clang/lib/Sema/SemaTemplate.cpp (+38-4)
- (modified) clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6-cxx23.cpp (+142)
``````````diff
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index a9f4143c6b375e..4c172c63245c7b 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -7487,6 +7487,10 @@ def err_explicit_object_parameter_mutable: Error<
def err_invalid_explicit_object_type_in_lambda: Error<
"invalid explicit object parameter type %0 in lambda with capture; "
"the type must be the same as, or derived from, the lambda">;
+def err_explicit_object_spec_mismatch: Error<
+ "%select{an explicit|a friend function}0 specialization %select{of|naming}0 "
+ "%select{a static|an implicit object|an explicit object}1 member function "
+ "%select{cannot|cannot|must}1 have an explicit object parameter">;
def err_ref_qualifier_overload : Error<
"cannot overload a member function %select{without a ref-qualifier|with "
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index f4b6e1ceb6f023..69b4317f02c339 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -10238,6 +10238,44 @@ bool Sema::CheckFunctionTemplateSpecialization(
!ResolveExceptionSpec(FD->getLocation(), SpecializationFPT))
return true;
+ // If this is a friend declaration, then we're not really declaring
+ // an explicit specialization.
+ bool isFriend = (FD->getFriendObjectKind() != Decl::FOK_None);
+
+ // C++23 [dcl.fct]p6 (with CWG2846 applied):
+ // [...] An explicit-object-parameter-declaration shall appear
+ // only as the first parameter-declaration of a parameter-declaration-list
+ // of either one of:
+ // - a member-declarator that declares declaration of a member function or
+ // member function template, or
+ // - an explicit instantiation or explicit specialization of a templated
+ // member function, or
+ // - a lambda-declarator.
+ //
+ // If the primary template is an explicit object member function, then
+ // the specialization must have an explicit object parameter. Likewise,
+ // if the primary template is an implicit object member function,
+ // static member function, or non-member function, then the specialization
+ // cannot have an explicit object parameter.
+ FunctionDecl *Primary =
+ Specialization->getPrimaryTemplate()->getTemplatedDecl();
+ if (FD->hasCXXExplicitFunctionObjectParameter() !=
+ Primary->hasCXXExplicitFunctionObjectParameter()) {
+ Diag(FD->getLocation(), diag::err_explicit_object_spec_mismatch)
+ << isFriend
+ << (Primary->isStatic()
+ ? 0
+ : Specialization->hasCXXExplicitFunctionObjectParameter() + 1)
+ << (FD->hasCXXExplicitFunctionObjectParameter()
+ ? FD->getParamDecl(0)->getSourceRange()
+ : SourceRange());
+ Diag(Primary->getLocation(), diag::note_specialized_decl)
+ << (Primary->hasCXXExplicitFunctionObjectParameter()
+ ? Primary->getParamDecl(0)->getSourceRange()
+ : SourceRange());
+ return true;
+ }
+
FunctionTemplateSpecializationInfo *SpecInfo
= Specialization->getTemplateSpecializationInfo();
assert(SpecInfo && "Function template specialization info missing?");
@@ -10260,10 +10298,6 @@ bool Sema::CheckFunctionTemplateSpecialization(
// FIXME: Check if the prior specialization has a point of instantiation.
// If so, we have run afoul of .
- // If this is a friend declaration, then we're not really declaring
- // an explicit specialization.
- bool isFriend = (FD->getFriendObjectKind() != Decl::FOK_None);
-
// Check the scope of this explicit specialization.
if (!isFriend &&
CheckTemplateSpecializationScope(*this,
diff --git a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6-cxx23.cpp b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6-cxx23.cpp
index 9c1f30f81a0115..fcf52c022f2af6 100644
--- a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6-cxx23.cpp
+++ b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6-cxx23.cpp
@@ -5,3 +5,145 @@ auto x1 = requires (int, this int) { true; }; // expected-error {{a requires exp
template<this auto> // expected-error {{expected template parameter}}
void f(); // expected-error {{no function template matches function template specialization 'f'}}
+
+struct A {
+ template<typename T>
+ void f0(this T); // expected-note 2{{attempt to specialize declaration here}}
+
+ template<>
+ void f0(this short);
+
+ template<>
+ void f0(long); // expected-error {{an explicit specialization of an explicit object member function must have an explicit object parameter}}
+
+ template<typename T>
+ void g0(T); // expected-note 2{{attempt to specialize declaration here}}
+
+ template<>
+ void g0(short);
+
+ template<>
+ void g0(this long); // expected-error {{an explicit specialization of an implicit object member function cannot have an explicit object parameter}}
+
+ template<typename T>
+ static void h0(T); // expected-note 2{{attempt to specialize declaration here}}
+
+ template<>
+ void h0(short);
+
+ template<>
+ void h0(this long); // expected-error {{an explicit specialization of a static member function cannot have an explicit object parameter}}
+};
+
+template<>
+void A::f0(this signed);
+
+template<>
+void A::f0(unsigned); // expected-error {{an explicit specialization of an explicit object member function must have an explicit object parameter}}
+
+template<>
+void A::g0(signed);
+
+template<>
+void A::g0(this unsigned); // expected-error {{an explicit specialization of an implicit object member function cannot have an explicit object parameter}}
+
+template<>
+void A::h0(signed);
+
+template<>
+void A::h0(this unsigned); // expected-error {{an explicit specialization of a static member function cannot have an explicit object parameter}}
+
+template<typename T>
+struct B {
+ void f1(this int); // expected-note {{member declaration nearly matches}}
+
+ void g1(int); // expected-note {{member declaration nearly matches}}
+
+ static void h1(int); // expected-note {{member declaration nearly matches}}
+};
+
+template<>
+void B<short>::f1(this int);
+
+template<>
+void B<long>::f1(int); // expected-error {{out-of-line declaration of 'f1' does not match any declaration in 'B<long>'}}
+
+template<>
+void B<short>::g1(int);
+
+template<>
+void B<long>::g1(this int); // expected-error {{out-of-line declaration of 'g1' does not match any declaration in 'B<long>'}}
+
+template<>
+void B<short>::h1(int);
+
+template<>
+void B<long>::h1(this int); // expected-error {{out-of-line declaration of 'h1' does not match any declaration in 'B<long>'}}
+
+template<typename T>
+struct C {
+ template<typename U>
+ void f2(this U); // expected-note {{attempt to specialize declaration here}}
+
+ template<>
+ void f2(this short);
+
+ template<>
+ void f2(long); // expected-error {{an explicit specialization of an explicit object member function must have an explicit object parameter}}
+
+ template<typename U>
+ void g2(U); // expected-note {{attempt to specialize declaration here}}
+
+ template<>
+ void g2(short);
+
+ template<>
+ void g2(this long); // expected-error {{an explicit specialization of an implicit object member function cannot have an explicit object parameter}}
+
+ template<typename U>
+ static void h2(U); // expected-note {{attempt to specialize declaration here}}
+
+ template<>
+ void h2(short);
+
+ template<>
+ void h2(this long); // expected-error {{an explicit specialization of a static member function cannot have an explicit object parameter}}
+};
+
+template struct C<int>; // expected-note {{in instantiation of}}
+
+template<typename T>
+struct D {
+ template<typename U>
+ void f3(this U);
+
+ template<typename U>
+ void g3(U);
+
+ template<typename U>
+ static void h3(U);
+};
+
+template<>
+template<typename U>
+void D<short>::f3(this U);
+
+template<>
+template<typename U>
+void D<long>::f3(U); // expected-error {{out-of-line declaration of 'f3' does not match any declaration in 'D<long>'}}
+
+template<>
+template<typename U>
+void D<short>::g3(U);
+
+template<>
+template<typename U>
+void D<long>::g3(this U); // expected-error {{out-of-line declaration of 'g3' does not match any declaration in 'D<long>'}}
+
+template<>
+template<typename U>
+void D<short>::h3(U);
+
+template<>
+template<typename U>
+void D<long>::h3(this U); // expected-error {{out-of-line declaration of 'h3' does not match any declaration in 'D<long>'}}
``````````
</details>
https://github.com/llvm/llvm-project/pull/89300
More information about the cfe-commits
mailing list