-
Notifications
You must be signed in to change notification settings - Fork 12.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Clang][Sema] Diagnose explicit specializations with object parameters that do not match their primary template #89300
base: main
Are you sure you want to change the base?
Conversation
…s that do not match their primary template
|
@llvm/pr-subscribers-clang Author: Krystian Stasiowski (sdkrystian) ChangesAccording to [[dcl.fct] p6](https://eel.is/c++draft/dcl.fct#6) (with the resolution for CWG2846 applied): This patch diagnoses explicit specializations declared with an object parameter that does not match the primary template. For example: 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 parameterSince 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:
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>'}}
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code changes seem fine so far.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As you mentioned, we need to test friends, and a subset of tests in scope of CWG2846 should be copied or moved to DR tests. Then you should be able to fill in the status of the DR and update cxx_dr_status.html via make_cxx_dr_status.
According to [dcl.fct] p6 (with the resolution for CWG2846 applied):
This patch diagnoses explicit specializations declared with an object parameter that does not match the primary template. For example:
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 :)