[clang] [clang] Improve checking of operator functions (PR #131777)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Mar 20 04:42:58 PDT 2025
https://github.com/offsetof updated https://github.com/llvm/llvm-project/pull/131777
>From ff54ffd5ac3f761c877eebce52ef81d096d98f16 Mon Sep 17 00:00:00 2001
From: offsetof <offsetof at mailo.com>
Date: Tue, 18 Mar 2025 10:08:56 +0000
Subject: [PATCH 1/3] [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 at -1 {{candidate template ignored: substitution failure [with T = <int, int>]: overloaded 'operator++' must be a unary or binary operator (has 3 parameters)}}
+ // expected-note at -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 at -5 {{overloaded 'operator++' must be a unary or binary operator}}
- // expected-note at -3 {{in instantiation of function template specialization 'PR14995::F::operator++<int, int>' requested here}}
- // expected-error at -4 {{no matching member function for call to 'operator++'}}
- // expected-note at -8 {{candidate template ignored: substitution failure}}
- // expected-error at -9 {{parameter of overloaded post-increment operator must have type 'int' (not 'char')}}
- // expected-note at -6 {{in instantiation of function template specialization 'PR14995::F::operator++<char>' requested here}}
- // expected-error at -7 {{no matching member function for call to 'operator++'}}
- // expected-note at -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 at -1 {{overloaded 'operator*' must have at least one parameter of class or enumeration type}}
+// expected-error at -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 at -1 {{while checking constraint satisfaction for template 'operator+<int>' required here}}
+// expected-note at -2 {{in instantiation of function template specialization 'BadConstraints::operator+<int>' requested here}}
+
+} // namespace BadConstraints
>From 701f851443255f4a352fc9904c408c233a04bb74 Mon Sep 17 00:00:00 2001
From: offsetof <offsetof at mailo.com>
Date: Wed, 19 Mar 2025 18:07:38 +0000
Subject: [PATCH 2/3] fixup! [clang] Improve checking of operator functions
---
clang/lib/Sema/SemaTemplateDeduction.cpp | 2 ++
clang/test/SemaTemplate/operator-template.cpp | 34 +++++++++----------
2 files changed, 19 insertions(+), 17 deletions(-)
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 6eaa2a6a0b2e4..d7d24327d32c2 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -4123,6 +4123,8 @@ TemplateDeductionResult Sema::FinishTemplateArgumentDeduction(
// If the template is an operator function template, check that the
// resulting specialization is a valid operator function.
+ // Invalid specializations are eliminated from the candidate set,
+ // per C++2c [temp.over] p1.2.
switch (Specialization->getOverloadedOperator()) {
case OO_None:
case OO_New:
diff --git a/clang/test/SemaTemplate/operator-template.cpp b/clang/test/SemaTemplate/operator-template.cpp
index a23b131af2d14..ccbf89e7e5a26 100644
--- a/clang/test/SemaTemplate/operator-template.cpp
+++ b/clang/test/SemaTemplate/operator-template.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 -Wno-anonymous-pack-parens %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s
namespace ParamTypes {
@@ -91,22 +91,22 @@ void operator/(Bad, int, auto); // expected-error {{overloaded 'operator/' mu
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*); // 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...);
>From 8aee45d83f93db37421f306335bb5bd5aaf34ad1 Mon Sep 17 00:00:00 2001
From: offsetof <offsetof at mailo.com>
Date: Thu, 20 Mar 2025 11:40:54 +0000
Subject: [PATCH 3/3] fixup! [clang] Improve checking of operator functions
Remove excessive release notes
---
clang/docs/ReleaseNotes.rst | 13 +++++--------
1 file changed, 5 insertions(+), 8 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index d7e6dcfe5d10e..9f8fc966991f9 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -267,7 +267,6 @@ 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
----------------------------------
@@ -331,13 +330,11 @@ 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.
+- Fixed some issues with the semantic analysis of operator functions.
+ Most notably, invalid specializations generated from operator function
+ templates during overload resolution are now eliminated from the candidate
+ set without causing hard errors. (#GH49197, #GH126855, #GH128902)
+
Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
More information about the cfe-commits
mailing list