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 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits