https://github.com/offsetof created https://github.com/llvm/llvm-project/pull/131777
* Move validation of operator function parameters into a new routine, `Sema::CheckOverloadedOperatorParams`. * Treat operator function template specializations failing these checks as a substitution failure. * Allow binary operator function templates to have one parameter if that parameter is a pack. * Diagnose some operator function templates that could never produce a valid specialization based on their parameter types. * Diagnose operator functions with an explicit object parameter and no parameters of class or enumeration type. Fixes #49197 Fixes #126855 Fixes #128902 >From 865b435df1439d11aaf321c2a670672565576faf Mon Sep 17 00:00:00 2001 From: offsetof <offse...@mailo.com> Date: Tue, 18 Mar 2025 10:08:56 +0000 Subject: [PATCH] [clang] Improve checking of operator functions * Move validation of operator function parameters into a new routine, `Sema::CheckOverloadedOperatorParams`. * Treat operator function template specializations failing these checks as a substitution failure. * Allow binary operator function templates to have one parameter if that parameter is a pack. * Diagnose some operator function templates that could never produce a valid specialization based on their parameter types. * Diagnose operator functions with an explicit object parameter and no parameters of class or enumeration type. --- clang/docs/ReleaseNotes.rst | 8 + clang/include/clang/Sema/Sema.h | 8 + clang/lib/Sema/SemaDeclCXX.cpp | 217 +++++++++--------- clang/lib/Sema/SemaTemplateDeduction.cpp | 23 ++ clang/test/Parser/cxx20-coroutines.cpp | 2 +- .../SemaCXX/cxx2a-three-way-comparison.cpp | 2 +- .../test/SemaCXX/overloaded-operator-decl.cpp | 26 ++- clang/test/SemaCXX/overloaded-operator.cpp | 29 ++- clang/test/SemaTemplate/operator-template.cpp | 186 +++++++++++++-- 9 files changed, 359 insertions(+), 142 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index d9f1c95533c9c..d7e6dcfe5d10e 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -267,6 +267,7 @@ Improvements to Clang's diagnostics - Improve the diagnostics for chained comparisons to report actual expressions and operators (#GH129069). - Improve the diagnostics for shadows template parameter to report correct location (#GH129060). +- Some operator function templates that could never produce a valid specialization are now diagnosed at definition time. Improvements to Clang's time-trace ---------------------------------- @@ -330,6 +331,13 @@ Bug Fixes to C++ Support - Fixed an assertion failure affecting code that uses C++23 "deducing this". (#GH130272) - Clang now properly instantiates destructors for initialized members within non-delegating constructors. (#GH93251) - Correctly diagnoses if unresolved using declarations shadows template paramters (#GH129411) +- Invalid operator function signatures generated from templates during + overload resolution are now eliminated from the candidate set without making + the program ill-formed. (#GH49197) +- Binary operator function templates with a single parameter are no longer + rejected when that parameter is a pack. +- Fixed operator functions without a class or enumeration parameter not being + rejected when an explicit object parameter is present. Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index fc3936d649320..923ff3edec9a5 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -5863,6 +5863,14 @@ class Sema final : public SemaBase { // C++ Overloaded Operators [C++ 13.5] // + /// CheckOverloadedOperatorParams - Check whether the parameter-type-list + /// of an overloaded operator is valid (has the correct number of + /// parameters for the operator, at least one parameter is of a class or + /// enumeration type, and, for the postfix increment/decrement operators, + /// the last parameter is of type int). If so, returns false; otherwise, + /// emits appropriate diagnostics and returns true. + bool CheckOverloadedOperatorParams(FunctionDecl *FnDecl); + /// CheckOverloadedOperatorDeclaration - Check whether the declaration /// of this overloaded operator is well-formed. If so, returns false; /// otherwise, emits appropriate diagnostics and returns true. diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 20533961a2217..69e66499b016c 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -27,6 +27,7 @@ #include "clang/AST/TypeLoc.h" #include "clang/AST/TypeOrdering.h" #include "clang/Basic/AttributeCommonInfo.h" +#include "clang/Basic/OperatorKinds.h" #include "clang/Basic/PartialDiagnostic.h" #include "clang/Basic/Specifiers.h" #include "clang/Basic/TargetInfo.h" @@ -47,6 +48,7 @@ #include "clang/Sema/SemaOpenMP.h" #include "clang/Sema/Template.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Bitset.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/STLForwardCompat.h" #include "llvm/ADT/StringExtras.h" @@ -16404,6 +16406,94 @@ CheckOperatorDeleteDeclaration(Sema &SemaRef, FunctionDecl *FnDecl) { return false; } +bool Sema::CheckOverloadedOperatorParams(FunctionDecl *FnDecl) { + assert(FnDecl && FnDecl->isOverloadedOperator() && + "Expected an overloaded operator declaration"); + + auto Op = FnDecl->getOverloadedOperator(); + bool IsImplicitObjectMemFn = isa<CXXMethodDecl>(FnDecl) && + !FnDecl->hasCXXExplicitFunctionObjectParameter(); + auto Params = FnDecl->parameters(); + auto NumParams = Params.size() + IsImplicitObjectMemFn; + + // C++2c [over.oper.general] p10: + // Operator functions cannot have more or fewer parameters than the + // number required for the corresponding operator, as described in the + // rest of [over.oper]. + static constexpr llvm::Bitset<NUM_OVERLOADED_OPERATORS> UnaryOps{ +#define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemberOnly) \ + Unary ? OO_##Name : OO_None, +#include "clang/Basic/OperatorKinds.def" + }; + static constexpr llvm::Bitset<NUM_OVERLOADED_OPERATORS> BinaryOps{ +#define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemberOnly) \ + Binary ? OO_##Name : OO_None, +#include "clang/Basic/OperatorKinds.def" + }; + + if (Op == OO_Subscript) { + if (NumParams != 2) { + // 0 = no parameters, 2 = more than one parameter + int ErrorKind = NumParams == 1 ? 0 : 2; + Diag(FnDecl->getLocation(), LangOpts.CPlusPlus23 + ? diag::ext_subscript_overload + : diag::error_subscript_overload) + << FnDecl->getDeclName() << ErrorKind; + if (!LangOpts.CPlusPlus23) + return true; + } + } else if (Op != OO_Call) { + bool NumParamsIsValid = false; + if (NumParams == 1) + NumParamsIsValid = UnaryOps[Op] || (!IsImplicitObjectMemFn && + Params[0]->isParameterPack()); + else if (NumParams == 2) + NumParamsIsValid = BinaryOps[Op]; + + if (!NumParamsIsValid) { + // 0 = unary, 1 = binary, 2 = unary or binary + int ErrorKind = (BinaryOps[Op] << 1 | UnaryOps[Op]) - 1; + return Diag(FnDecl->getLocation(), diag::err_operator_overload_must_be) + << FnDecl->getDeclName() << NumParams << ErrorKind; + } + } + + // The second parameter of operator++ and operator--, if present, + // must be of type 'int' (C++2c [over.inc] p1). + if ((Op == OO_PlusPlus || Op == OO_MinusMinus) && NumParams == 2) { + ParmVarDecl *SecondParam = Params[!IsImplicitObjectMemFn]; + QualType Type = SecondParam->getType(); + if (!(Type->isSpecificBuiltinType(BuiltinType::Int) || + Type->isDependentType())) + return Diag(SecondParam->getLocation(), + diag::err_operator_overload_post_incdec_must_be_int) + << Type << (Op == OO_MinusMinus); + + // Ignore the 'int' parameter for subsequent checks. + Params = Params.drop_back(); + } + + // C++2c [over.oper.general] p7: + // An operator function shall have at least one function parameter + // or implicit object parameter whose type is a class, a reference + // to a class, an enumeration, or a reference to an enumeration. + bool HasClassOrEnumParam = + IsImplicitObjectMemFn || llvm::any_of(Params, [](ParmVarDecl *Param) { + QualType Type = + Param->getType().getNonPackExpansionType().getNonReferenceType(); + return Type->isDependentType() + ? !(Type->isPointerType() || Type->isMemberPointerType() || + Type->isFunctionType() || Type->isArrayType()) + : Type->isRecordType() || Type->isEnumeralType(); + }); + if (!HasClassOrEnumParam) + return Diag(FnDecl->getLocation(), + diag::err_operator_overload_needs_class_or_enum) + << FnDecl->getDeclName(); + + return false; +} + bool Sema::CheckOverloadedOperatorDeclaration(FunctionDecl *FnDecl) { assert(FnDecl && FnDecl->isOverloadedOperator() && "Expected an overloaded operator declaration"); @@ -16422,40 +16512,19 @@ bool Sema::CheckOverloadedOperatorDeclaration(FunctionDecl *FnDecl) { if (Op == OO_New || Op == OO_Array_New) return CheckOperatorNewDeclaration(*this, FnDecl); - // C++ [over.oper]p7: - // An operator function shall either be a member function or - // be a non-member function and have at least one parameter - // whose type is a class, a reference to a class, an enumeration, - // or a reference to an enumeration. - // Note: Before C++23, a member function could not be static. The only member - // function allowed to be static is the call operator function. - if (CXXMethodDecl *MethodDecl = dyn_cast<CXXMethodDecl>(FnDecl)) { - if (MethodDecl->isStatic()) { - if (Op == OO_Call || Op == OO_Subscript) - Diag(FnDecl->getLocation(), - (LangOpts.CPlusPlus23 - ? diag::warn_cxx20_compat_operator_overload_static - : diag::ext_operator_overload_static)) - << FnDecl; - else - return Diag(FnDecl->getLocation(), diag::err_operator_overload_static) - << FnDecl; - } - } else { - bool ClassOrEnumParam = false; - for (auto *Param : FnDecl->parameters()) { - QualType ParamType = Param->getType().getNonReferenceType(); - if (ParamType->isDependentType() || ParamType->isRecordType() || - ParamType->isEnumeralType()) { - ClassOrEnumParam = true; - break; - } - } - - if (!ClassOrEnumParam) - return Diag(FnDecl->getLocation(), - diag::err_operator_overload_needs_class_or_enum) - << FnDecl->getDeclName(); + // Only function call and subscript operators are allowed to be + // static member functions, and only since C++23. + if (auto *MethodDecl = dyn_cast<CXXMethodDecl>(FnDecl); + MethodDecl && MethodDecl->isStatic()) { + if (Op == OO_Call || Op == OO_Subscript) + Diag(FnDecl->getLocation(), + (LangOpts.CPlusPlus23 + ? diag::warn_cxx20_compat_operator_overload_static + : diag::ext_operator_overload_static)) + << FnDecl; + else + return Diag(FnDecl->getLocation(), diag::err_operator_overload_static) + << FnDecl; } // C++ [over.oper]p8: @@ -16488,52 +16557,6 @@ bool Sema::CheckOverloadedOperatorDeclaration(FunctionDecl *FnDecl) { } } - static const bool OperatorUses[NUM_OVERLOADED_OPERATORS][3] = { - { false, false, false } -#define OVERLOADED_OPERATOR(Name,Spelling,Token,Unary,Binary,MemberOnly) \ - , { Unary, Binary, MemberOnly } -#include "clang/Basic/OperatorKinds.def" - }; - - bool CanBeUnaryOperator = OperatorUses[Op][0]; - bool CanBeBinaryOperator = OperatorUses[Op][1]; - bool MustBeMemberOperator = OperatorUses[Op][2]; - - // C++ [over.oper]p8: - // [...] Operator functions cannot have more or fewer parameters - // than the number required for the corresponding operator, as - // described in the rest of this subclause. - unsigned NumParams = FnDecl->getNumParams() + - (isa<CXXMethodDecl>(FnDecl) && - !FnDecl->hasCXXExplicitFunctionObjectParameter() - ? 1 - : 0); - if (Op != OO_Call && Op != OO_Subscript && - ((NumParams == 1 && !CanBeUnaryOperator) || - (NumParams == 2 && !CanBeBinaryOperator) || (NumParams < 1) || - (NumParams > 2))) { - // We have the wrong number of parameters. - unsigned ErrorKind; - if (CanBeUnaryOperator && CanBeBinaryOperator) { - ErrorKind = 2; // 2 -> unary or binary. - } else if (CanBeUnaryOperator) { - ErrorKind = 0; // 0 -> unary - } else { - assert(CanBeBinaryOperator && - "All non-call overloaded operators are unary or binary!"); - ErrorKind = 1; // 1 -> binary - } - return Diag(FnDecl->getLocation(), diag::err_operator_overload_must_be) - << FnDecl->getDeclName() << NumParams << ErrorKind; - } - - if (Op == OO_Subscript && NumParams != 2) { - Diag(FnDecl->getLocation(), LangOpts.CPlusPlus23 - ? diag::ext_subscript_overload - : diag::error_subscript_overload) - << FnDecl->getDeclName() << (NumParams == 1 ? 0 : 2); - } - // Overloaded operators other than operator() and operator[] cannot be // variadic. if (Op != OO_Call && @@ -16543,32 +16566,20 @@ bool Sema::CheckOverloadedOperatorDeclaration(FunctionDecl *FnDecl) { } // Some operators must be member functions. - if (MustBeMemberOperator && !isa<CXXMethodDecl>(FnDecl)) { + static constexpr llvm::Bitset<NUM_OVERLOADED_OPERATORS> MemberOnlyOps{ +#define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemberOnly) \ + MemberOnly ? OO_##Name : OO_None, +#include "clang/Basic/OperatorKinds.def" + }; + + if (MemberOnlyOps[Op] && !isa<CXXMethodDecl>(FnDecl)) return Diag(FnDecl->getLocation(), diag::err_operator_overload_must_be_member) - << FnDecl->getDeclName(); - } - - // C++ [over.inc]p1: - // The user-defined function called operator++ implements the - // prefix and postfix ++ operator. If this function is a member - // function with no parameters, or a non-member function with one - // parameter of class or enumeration type, it defines the prefix - // increment operator ++ for objects of that type. If the function - // is a member function with one parameter (which shall be of type - // int) or a non-member function with two parameters (the second - // of which shall be of type int), it defines the postfix - // increment operator ++ for objects of that type. - if ((Op == OO_PlusPlus || Op == OO_MinusMinus) && NumParams == 2) { - ParmVarDecl *LastParam = FnDecl->getParamDecl(FnDecl->getNumParams() - 1); - QualType ParamType = LastParam->getType(); + << FnDecl->getDeclName(); - if (!ParamType->isSpecificBuiltinType(BuiltinType::Int) && - !ParamType->isDependentType()) - return Diag(LastParam->getLocation(), - diag::err_operator_overload_post_incdec_must_be_int) - << LastParam->getType() << (Op == OO_MinusMinus); - } + if (!FnDecl->isFunctionTemplateSpecialization() && + CheckOverloadedOperatorParams(FnDecl)) + return true; return false; } diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index e6ec4a7178e81..6eaa2a6a0b2e4 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -34,6 +34,7 @@ #include "clang/Basic/ExceptionSpecificationType.h" #include "clang/Basic/LLVM.h" #include "clang/Basic/LangOptions.h" +#include "clang/Basic/OperatorKinds.h" #include "clang/Basic/PartialDiagnostic.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/Specifiers.h" @@ -4120,6 +4121,28 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction( } } + // If the template is an operator function template, check that the + // resulting specialization is a valid operator function. + switch (Specialization->getOverloadedOperator()) { + case OO_None: + case OO_New: + case OO_Array_New: + case OO_Delete: + case OO_Array_Delete: + break; + + default: + // SFINAE does not apply at this point in the instantiation process. + // Push a new CodeSynthesisContext to briefly re-enable it here. + InstantiatingTemplate Inst( + *this, Info.getLocation(), FunctionTemplate, DeducedArgs, + CodeSynthesisContext::DeducedTemplateArgumentSubstitution, Info); + if (Inst.isInvalid()) + return TemplateDeductionResult::InstantiationDepth; + if (CheckOverloadedOperatorParams(Specialization)) + return TemplateDeductionResult::SubstitutionFailure; + } + // We skipped the instantiation of the explicit-specifier during the // substitution of `FD` before. So, we try to instantiate it back if // `Specialization` is either a constructor or a conversion function. diff --git a/clang/test/Parser/cxx20-coroutines.cpp b/clang/test/Parser/cxx20-coroutines.cpp index 7207fb98587ab..d0ea7cec4336f 100644 --- a/clang/test/Parser/cxx20-coroutines.cpp +++ b/clang/test/Parser/cxx20-coroutines.cpp @@ -30,6 +30,6 @@ void f(X x, Z z) { operator co_await(z); } -void operator co_await(); // expected-error {{must have at least one parameter}} +void operator co_await(); // expected-error {{must be a unary operator}} void operator co_await(X, Y, Z); // expected-error {{must be a unary operator}} void operator co_await(int); // expected-error {{parameter of class or enumeration type}} diff --git a/clang/test/SemaCXX/cxx2a-three-way-comparison.cpp b/clang/test/SemaCXX/cxx2a-three-way-comparison.cpp index b94225274fffb..5fc805ae395c5 100644 --- a/clang/test/SemaCXX/cxx2a-three-way-comparison.cpp +++ b/clang/test/SemaCXX/cxx2a-three-way-comparison.cpp @@ -13,7 +13,7 @@ struct A {}; constexpr int operator<=>(A a, A b) { return 42; } static_assert(operator<=>(A(), A()) == 42); -int operator<=>(); // expected-error {{overloaded 'operator<=>' must have at least one parameter of class or enumeration type}} +int operator<=>(); // expected-error {{overloaded 'operator<=>' must be a binary operator}} int operator<=>(A); // expected-error {{overloaded 'operator<=>' must be a binary operator}} int operator<=>(int, int); // expected-error {{overloaded 'operator<=>' must have at least one parameter of class or enumeration type}} int operator<=>(A, A, A); // expected-error {{overloaded 'operator<=>' must be a binary operator}} diff --git a/clang/test/SemaCXX/overloaded-operator-decl.cpp b/clang/test/SemaCXX/overloaded-operator-decl.cpp index 8a76c9251e419..ce54f166d4d67 100644 --- a/clang/test/SemaCXX/overloaded-operator-decl.cpp +++ b/clang/test/SemaCXX/overloaded-operator-decl.cpp @@ -21,6 +21,8 @@ void f(X x) { x = operator+(x, x); } +X operator+(); // expected-error {{overloaded 'operator+' must be a unary or binary operator}} + X operator+(int, float); // expected-error{{overloaded 'operator+' must have at least one parameter of class or enumeration type}} X operator*(X, X = 5); // expected-error{{parameter of overloaded 'operator*' cannot have a default argument}} @@ -69,33 +71,33 @@ class E { void operator+(E, ...) {} // expected-error{{overloaded 'operator+' cannot be variadic}} void operator-(E, ...) {} // expected-error{{overloaded 'operator-' cannot be variadic}} void operator*(E, ...) {} // expected-error{{overloaded 'operator*' cannot be variadic}} -void operator/(E, ...) {} // expected-error{{overloaded 'operator/' must be a binary operator}} +void operator/(E, ...) {} // expected-error{{overloaded 'operator/' cannot be variadic}} void operator/(E, E, ...) {} // expected-error{{overloaded 'operator/' cannot be variadic}} -void operator%(E, ...) {} // expected-error{{overloaded 'operator%' must be a binary operator}} +void operator%(E, ...) {} // expected-error{{overloaded 'operator%' cannot be variadic}} void operator%(E, E, ...) {} // expected-error{{overloaded 'operator%' cannot be variadic}} E& operator++(E&, ...); // expected-error{{overloaded 'operator++' cannot be variadic}} E& operator--(E&, ...); // expected-error{{overloaded 'operator--' cannot be variadic}} -bool operator<(const E& lhs, ...); // expected-error{{overloaded 'operator<' must be a binary operator}} +bool operator<(const E& lhs, ...); // expected-error{{overloaded 'operator<' cannot be variadic}} bool operator<(const E& lhs, const E& rhs, ...); // expected-error{{cannot be variadic}} -bool operator>(const E& lhs, ...); // expected-error{{overloaded 'operator>' must be a binary operator}} +bool operator>(const E& lhs, ...); // expected-error{{overloaded 'operator>' cannot be variadic}} bool operator>(const E& lhs, const E& rhs, ...); // expected-error{{cannot be variadic}} -bool operator>=(const E& lhs, ...); // expected-error{{overloaded 'operator>=' must be a binary operator}} +bool operator>=(const E& lhs, ...); // expected-error{{overloaded 'operator>=' cannot be variadic}} bool operator>=(const E& lhs, const E& rhs, ...); // expected-error{{cannot be variadic}} -bool operator<=(const E& lhs, ...); // expected-error{{overloaded 'operator<=' must be a binary operator}} +bool operator<=(const E& lhs, ...); // expected-error{{overloaded 'operator<=' cannot be variadic}} bool operator<=(const E& lhs, const E& rhs, ...); // expected-error{{cannot be variadic}} -bool operator==(const E& lhs, ...); // expected-error{{overloaded 'operator==' must be a binary operator}} +bool operator==(const E& lhs, ...); // expected-error{{overloaded 'operator==' cannot be variadic}} bool operator==(const E& lhs, const E& rhs, ...); // expected-error{{cannot be variadic}} -bool operator!=(const E& lhs, ...); // expected-error{{overloaded 'operator!=' must be a binary operator}} +bool operator!=(const E& lhs, ...); // expected-error{{overloaded 'operator!=' cannot be variadic}} bool operator!=(const E& lhs, const E& rhs, ...); // expected-error{{cannot be variadic}} -bool operator&&(const E& lhs, ...); // expected-error{{overloaded 'operator&&' must be a binary operator}} +bool operator&&(const E& lhs, ...); // expected-error{{overloaded 'operator&&' cannot be variadic}} bool operator&&(const E& lhs, const E& rhs, ...); // expected-error{{cannot be variadic}} -bool operator||(const E& lhs, ...); // expected-error{{overloaded 'operator||' must be a binary operator}} +bool operator||(const E& lhs, ...); // expected-error{{overloaded 'operator||' cannot be variadic}} bool operator||(const E& lhs, const E& rhs, ...); // expected-error{{cannot be variadic}} -bool operator>>(const E& lhs, ...); // expected-error{{overloaded 'operator>>' must be a binary operator}} +bool operator>>(const E& lhs, ...); // expected-error{{overloaded 'operator>>' cannot be variadic}} bool operator>>(const E& lhs, const E& rhs, ...); // expected-error{{cannot be variadic}} bool operator&(const E& lhs, ...); // expected-error{{cannot be variadic}} #if __cplusplus >= 202002L -auto operator<=>(const E& lhs, ...); // expected-error{{overloaded 'operator<=>' must be a binary operator}} +auto operator<=>(const E& lhs, ...); // expected-error{{overloaded 'operator<=>' cannot be variadic}} #endif void d() { E() + E(); diff --git a/clang/test/SemaCXX/overloaded-operator.cpp b/clang/test/SemaCXX/overloaded-operator.cpp index 0701a96d5d0ce..4c337e2b2a0f8 100644 --- a/clang/test/SemaCXX/overloaded-operator.cpp +++ b/clang/test/SemaCXX/overloaded-operator.cpp @@ -496,22 +496,16 @@ namespace PR14995 { }; E<char> e; // expected-note {{in instantiation of template class 'PR14995::E<char>' requested here}} - + struct F { template<typename... T> int operator++ (T...) {} + // expected-note@-1 {{candidate template ignored: substitution failure [with T = <int, int>]: overloaded 'operator++' must be a unary or binary operator (has 3 parameters)}} + // expected-note@-2 {{candidate template ignored: substitution failure [with T = <char>]: parameter of overloaded post-increment operator must have type 'int' (not 'char')}} }; - int k1 = F().operator++(0, 0); - int k2 = F().operator++('0'); - // expected-error@-5 {{overloaded 'operator++' must be a unary or binary operator}} - // expected-note@-3 {{in instantiation of function template specialization 'PR14995::F::operator++<int, int>' requested here}} - // expected-error@-4 {{no matching member function for call to 'operator++'}} - // expected-note@-8 {{candidate template ignored: substitution failure}} - // expected-error@-9 {{parameter of overloaded post-increment operator must have type 'int' (not 'char')}} - // expected-note@-6 {{in instantiation of function template specialization 'PR14995::F::operator++<char>' requested here}} - // expected-error@-7 {{no matching member function for call to 'operator++'}} - // expected-note@-12 {{candidate template ignored: substitution failure}} + int k1 = F().operator++(0, 0); // expected-error {{no matching member function for call to 'operator++'}} + int k2 = F().operator++('0'); // expected-error {{no matching member function for call to 'operator++'}} } // namespace PR14995 namespace ConversionVersusTemplateOrdering { @@ -688,7 +682,7 @@ namespace GH88329 { template <auto T> struct A {}; template <auto T> A<*T> operator *() { return {}; } -// expected-error@-1 {{overloaded 'operator*' must have at least one parameter of class or enumeration type}} +// expected-error@-1 {{overloaded 'operator*' must be a unary or binary operator}} } namespace GH92275 { @@ -703,3 +697,14 @@ auto operator *(constant<x>) } #endif + +#if __cpp_explicit_this_parameter >= 202110 +namespace ExplicitThisOperator { + +struct Bad { + void operator+(this int); // cxx23-error {{overloaded 'operator+' must have at least one parameter of class or enumeration type}} + void operator+(this int, int); // cxx23-error {{overloaded 'operator+' must have at least one parameter of class or enumeration type}} +}; + +} +#endif diff --git a/clang/test/SemaTemplate/operator-template.cpp b/clang/test/SemaTemplate/operator-template.cpp index cf604691b943c..a23b131af2d14 100644 --- a/clang/test/SemaTemplate/operator-template.cpp +++ b/clang/test/SemaTemplate/operator-template.cpp @@ -1,4 +1,6 @@ -// RUN: %clang_cc1 -fsyntax-only -verify -std=c++2a %s +// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 -Wno-anonymous-pack-parens %s + +namespace ParamTypes { // Make sure we accept this template<class X>struct A{typedef X Y;}; @@ -8,19 +10,177 @@ int a(A<int> x) { return operator==(x,1); } int a0(A<int> x) { return x == 1; } -// FIXME: the location information for the note isn't very good template<class X>struct B{typedef X Y;}; -template<class X>bool operator==(B<X>*,typename B<X>::Y); // \ -// expected-error{{overloaded 'operator==' must have at least one parameter of class or enumeration type}} \ -// expected-note{{candidate template ignored: substitution failure [with X = int]}} -int a(B<int> x) { return operator==(&x,1); } // expected-error{{no matching function for call to 'operator=='}} \ -// expected-note{{in instantiation of function template specialization}} +template<class X>bool operator==(B<X>*,typename B<X>::Y); // expected-note{{candidate template ignored: substitution failure [with X = int]}} +int a(B<int> x) { return operator==(&x,1); } // expected-error{{no matching function for call to 'operator=='}} + +} // namespace ParamTypes -// Ensure we take parameter list reversal into account in partial oredring. namespace CompareOrdering { - template<typename T> struct A {}; - template<typename T> int operator<=>(A<T>, int) = delete; - template<typename T> int operator<=>(int, A<T*>); - // OK, selects the more-specialized reversed function. - bool b = A<int*>() < 0; + +// Ensure we take parameter list reversal into account in partial ordering. +template<typename T> struct A {}; +template<typename T> int operator<=>(A<T>, int) = delete; +template<typename T> int operator<=>(int, A<T*>); +// OK, selects the more-specialized reversed function. +bool b = A<int*>() < 0; + +} // namespace CompareOrdering + +namespace SFINAE { + +struct A { + constexpr operator int() { return 1; } +}; + +int operator+(auto, int) { static_assert(false); } + +static_assert(2 + A() == 3); + + +struct B {}; + +constexpr bool operator==(auto, int i) { + return i; } + +static_assert(B() != 0); +static_assert(B() == 1); +static_assert(0 != B()); +static_assert(1 == B()); + + +struct C { + int operator++(auto) { static_assert(false); } + constexpr int operator++(int) { return 1; } +}; + +static_assert(C().operator++(1.0) == 1); + +int operator++(C, auto) { static_assert(false); } +constexpr int operator++(C, int) { return 2; } + +static_assert(operator++(C(), 1.0) == 2); + + +constexpr int operator%(auto...) { + return 10; +} + +static_assert(A() % B() == 10); + +} // namespace SFINAE + +namespace InvalidDecls { + +class Bad { + void operator~(auto); // expected-error {{overloaded 'operator~' must be a unary operator (has 2 parameters)}} + void operator~(auto...); // expected-error {{overloaded 'operator~' must be a unary operator (has 2 parameters)}} + void operator+(int, auto); // expected-error {{overloaded 'operator+' must be a unary or binary operator (has 3 parameters)}} + void operator+(int, auto...); // expected-error {{overloaded 'operator+' must be a unary or binary operator (has 3 parameters)}} + void operator/(int, auto); // expected-error {{overloaded 'operator/' must be a binary operator (has 3 parameters)}} + void operator/(int, auto...); // expected-error {{overloaded 'operator/' must be a binary operator (has 3 parameters)}} +}; + +void operator~(Bad, auto); // expected-error {{overloaded 'operator~' must be a unary operator (has 2 parameters)}} +void operator~(Bad, auto...); // expected-error {{overloaded 'operator~' must be a unary operator (has 2 parameters)}} +void operator+(Bad, int, auto); // expected-error {{overloaded 'operator+' must be a unary or binary operator (has 3 parameters)}} +void operator+(Bad, int, auto...); // expected-error {{overloaded 'operator+' must be a unary or binary operator (has 3 parameters)}} +void operator/(auto); // expected-error {{overloaded 'operator/' must be a binary operator (has 1 parameter)}} +void operator/(Bad, int, auto); // expected-error {{overloaded 'operator/' must be a binary operator (has 3 parameters)}} +void operator/(Bad, int, auto...); // expected-error {{overloaded 'operator/' must be a binary operator (has 3 parameters)}} + +class C; +int operator+(auto*); // expected-error {{overloaded 'operator+' must have at least one parameter of class or enumeration type}} +int operator-(auto[]); // expected-error {{overloaded 'operator-' must have at least one parameter of class or enumeration type}} +int operator+(auto()); // expected-error {{overloaded 'operator+' must have at least one parameter of class or enumeration type}} +int operator+(auto C::*); // expected-error {{overloaded 'operator+' must have at least one parameter of class or enumeration type}} +int operator+(auto*&); // expected-error {{overloaded 'operator+' must have at least one parameter of class or enumeration type}} +int operator+(auto (&)[]); // expected-error {{overloaded 'operator+' must have at least one parameter of class or enumeration type}} +int operator+(auto (&)()); // expected-error {{overloaded 'operator+' must have at least one parameter of class or enumeration type}} +int operator+(auto C::*&); // expected-error {{overloaded 'operator+' must have at least one parameter of class or enumeration type}} +int operator+(auto*...); // expected-error {{overloaded 'operator+' must have at least one parameter of class or enumeration type}} +int operator-(auto... _[]); // expected-error {{overloaded 'operator-' must have at least one parameter of class or enumeration type}} +int operator+(auto...()); // expected-error {{overloaded 'operator+' must have at least one parameter of class or enumeration type}} +int operator+(auto C::*...); // expected-error {{overloaded 'operator+' must have at least one parameter of class or enumeration type}} +int operator+(auto*&...); // expected-error {{overloaded 'operator+' must have at least one parameter of class or enumeration type}} +int operator+(auto (&...)[]); // expected-error {{overloaded 'operator+' must have at least one parameter of class or enumeration type}} +int operator+(auto (&...)()); // expected-error {{overloaded 'operator+' must have at least one parameter of class or enumeration type}} +int operator+(auto C::*&...); // expected-error {{overloaded 'operator+' must have at least one parameter of class or enumeration type}} + +int operator++(auto); +int operator++(auto...); +int operator++(auto, auto); +int operator++(auto*...); // expected-error {{overloaded 'operator++' must have at least one parameter of class or enumeration type}} +int operator++(auto*, auto); // expected-error {{overloaded 'operator++' must have at least one parameter of class or enumeration type}} +int operator--(auto[], auto); // expected-error {{overloaded 'operator--' must have at least one parameter of class or enumeration type}} +int operator++(auto(), auto); // expected-error {{overloaded 'operator++' must have at least one parameter of class or enumeration type}} +int operator++(auto C::*, auto); // expected-error {{overloaded 'operator++' must have at least one parameter of class or enumeration type}} +int operator++(auto*&, auto); // expected-error {{overloaded 'operator++' must have at least one parameter of class or enumeration type}} +int operator++(auto (&)[], auto); // expected-error {{overloaded 'operator++' must have at least one parameter of class or enumeration type}} +int operator++(auto (&)(), auto); // expected-error {{overloaded 'operator++' must have at least one parameter of class or enumeration type}} +int operator++(auto C::*&, auto); // expected-error {{overloaded 'operator++' must have at least one parameter of class or enumeration type}} + +int operator~(auto, ...); // expected-error {{overloaded 'operator~' cannot be variadic}} +int operator~(auto..., ...); // expected-error {{overloaded 'operator~' cannot be variadic}} +int operator/(auto..., ...); // expected-error {{overloaded 'operator/' cannot be variadic}} +int operator/(auto, auto, ...); // expected-error {{overloaded 'operator/' cannot be variadic}} + +int operator!(auto...); // expected-note-re 2 {{candidate template ignored: substitution failure [with {{.+}}]: overloaded 'operator!' must be a unary operator}} +int bad1 = operator!(); // expected-error {{no matching function for call to 'operator!'}} +int bad2 = operator!(Bad(), Bad()); // expected-error {{no matching function for call to 'operator!'}} + +int operator*(auto...); // expected-note-re 2 {{candidate template ignored: substitution failure [with {{.+}}]: overloaded 'operator*' must be a unary or binary operator}} +int bad3 = operator*(); // expected-error {{no matching function for call to 'operator*'}} +int bad4 = operator*(Bad(), Bad(), Bad()); // expected-error {{no matching function for call to 'operator*'}} + +int operator%(auto...); // expected-note-re 3 {{candidate template ignored: substitution failure [with {{.+}}]: overloaded 'operator%' must be a binary operator}} +int bad5 = operator%(); // expected-error {{no matching function for call to 'operator%'}} +int bad6 = operator%(Bad()); // expected-error {{no matching function for call to 'operator%'}} +int bad7 = operator%(Bad(), Bad(), Bad()); // expected-error {{no matching function for call to 'operator%'}} + +int operator&(auto...); // expected-note-re 4 {{candidate template ignored: substitution failure [with {{.+}}]: overloaded 'operator&' must}} + +template<> int operator&(); // expected-error {{no function template matches function template specialization 'operator&'}} +template<> int operator&(int); // expected-error {{no function template matches function template specialization 'operator&'}} +template<> int operator&<int>(int); // expected-error {{no function template matches function template specialization 'operator&'}} +template<> int operator&(Bad, Bad, Bad); // expected-error {{no function template matches function template specialization 'operator&'}} + +int operator-(auto...); // expected-note-re 4 {{candidate template ignored: substitution failure [with {{.+}}]: overloaded 'operator-' must}} + +template int operator-(); // expected-error {{explicit instantiation of 'operator-' does not refer to a function template}} +template int operator-(int); // expected-error {{explicit instantiation of 'operator-' does not refer to a function template}} +template int operator-<int>(int); // expected-error {{explicit instantiation of 'operator-' does not refer to a function template}} +template int operator-(Bad, Bad, Bad); // expected-error {{explicit instantiation of 'operator-' does not refer to a function template}} + +template<class... Ts> +class F { + friend void operator^(Ts...); // expected-error 3 {{overloaded 'operator^' must be a binary operator}} +}; + +F<> s1; // expected-note {{in instantiation of template class}} +F<Bad> s2; // expected-note {{in instantiation of template class}} +F<Bad, Bad> s3; +F<Bad, Bad, Bad> s4; // expected-note {{in instantiation of template class}} + +} // namespace InvalidDecls + +namespace BadConstraints { + +template<class...> +int Poison; + +struct S { + S(int); + operator int(); +}; + +template<class T> +int operator+(T, int) requires Poison<T>; // expected-error {{atomic constraint must be of type 'bool'}} +int operator+(int, auto); + +int i = 1 + S(2); +// expected-note@-1 {{while checking constraint satisfaction for template 'operator+<int>' required here}} +// expected-note@-2 {{in instantiation of function template specialization 'BadConstraints::operator+<int>' requested here}} + +} // namespace BadConstraints _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits