[clang] [clang] Handle templated operators with reversed arguments (PR #69595)
Utkarsh Saxena via cfe-commits
cfe-commits at lists.llvm.org
Thu Oct 19 05:13:02 PDT 2023
https://github.com/usx95 created https://github.com/llvm/llvm-project/pull/69595
https://github.com/llvm/llvm-project/pull/68999 correctly computed conversion sequence for reversed args to a template operators. This was a breaking change as code, previously accepted in C++17, starts to break in C++20.
Example:
```cpp
struct P {};
template<class S> bool operator==(const P&, const S &);
struct A : public P {};
struct B : public P {};
bool check(A a, B b) { return a == b; } // This is now ambiguous in C++20.
```
In order to minimise widespread breakages, as a clang extension, we had previously accepted such ambiguities with a warning (`-Wambiguous-reversed-operator`) for non-template operators. Due to the same reasons, we extend this relaxation for template operators.
Fixes https://github.com/llvm/llvm-project/issues/53954
>From 4460b02508d21d98cf05103bece99fc5bd474ab0 Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Thu, 19 Oct 2023 12:33:45 +0200
Subject: [PATCH 1/2] Reapply "Correctly compute conversion seq for args to fn
with reversed param order (#68999)"
This reverts commit a3a0f59a1e1cb0ac02f06b19f730ea05a6541c96.
---
clang/docs/ReleaseNotes.rst | 2 ++
clang/lib/Sema/SemaOverload.cpp | 2 +-
.../over.match.oper/p3-2a.cpp | 35 +++++++++++++++++++
3 files changed, 38 insertions(+), 1 deletion(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index eee48431d716878..f6ad453cb17f9d9 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -117,6 +117,8 @@ C++ Language Changes
C++20 Feature Support
^^^^^^^^^^^^^^^^^^^^^
+- Fix a bug in conversion sequence of arguments to a function with reversed parameter order.
+ Fixes `GH <https://github.com/llvm/llvm-project/issues/53954>`_.
C++23 Feature Support
^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index ce78994e6553814..c271cebb9eb638f 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -7688,7 +7688,7 @@ bool Sema::CheckNonDependentConversions(
QualType ParamType = ParamTypes[I + Offset];
if (!ParamType->isDependentType()) {
unsigned ConvIdx = PO == OverloadCandidateParamOrder::Reversed
- ? 0
+ ? Args.size() - 1 - (ThisConversions + I)
: (ThisConversions + I);
Conversions[ConvIdx]
= TryCopyInitialization(*this, Args[I], ParamType,
diff --git a/clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp b/clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp
index 5c6804eb7726b5f..02fe37dc1be5058 100644
--- a/clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp
+++ b/clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp
@@ -324,6 +324,41 @@ bool x = X() == X(); // expected-warning {{ambiguous}}
}
} // namespace P2468R2
+namespace GH53954{
+namespace test1 {
+struct P {
+ template <class T>
+ friend bool operator==(const P&, const T&); // expected-note {{candidate}} \
+ // expected-note {{reversed parameter order}}
+};
+struct A : public P {};
+struct B : public P {};
+bool check(A a, B b) { return a == b; } // expected-error {{ '==' is ambiguous}}
+}
+
+namespace test2 {
+struct P {
+ template <class T>
+ friend bool operator==(const T&, const P&); // expected-note {{candidate}} \
+ // expected-note {{reversed parameter order}}
+};
+struct A : public P {};
+struct B : public P {};
+bool check(A a, B b) { return a == b; } // expected-error {{ '==' is ambiguous}}
+}
+
+namespace test3 {
+struct P {
+ template<class S>
+ bool operator==(const S &) const; // expected-note {{candidate}} \
+ // expected-note {{reversed parameter order}}
+};
+struct A : public P {};
+struct B : public P {};
+bool check(A a, B b) { return a == b; } // expected-error {{ '==' is ambiguous}}
+}
+}
+
#else // NO_ERRORS
namespace problem_cases {
>From 12f5b5e43e51fbd966fc99d82c5d04019cfa3adf Mon Sep 17 00:00:00 2001
From: Utkarsh Saxena <usx at google.com>
Date: Thu, 19 Oct 2023 14:02:24 +0200
Subject: [PATCH 2/2] Do not hard error for ambiguity with reversed arguments
for templated operators
---
clang/docs/ReleaseNotes.rst | 18 ++++++++-
clang/lib/Sema/SemaOverload.cpp | 10 +++--
.../over.match.oper/p3-2a.cpp | 40 ++++++++++++++-----
3 files changed, 54 insertions(+), 14 deletions(-)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index f6ad453cb17f9d9..a38cd74b6165c84 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -37,6 +37,22 @@ These changes are ones which we think may surprise users when upgrading to
Clang |release| because of the opportunity they pose for disruption to existing
code bases.
+- Fix a bug in reversed argument for templated operators.
+ This breaks code in C++20 which was previously accepted in C++17. Eg:
+ ```
+ struct P {};
+ template<class S> bool operator==(const P&, const S &);
+
+ struct A : public P {};
+ struct B : public P {};
+ // This equality is now ambiguous in C++20.
+ bool check(A a, B b) { return a == b; }
+ ```
+ To reduce widespread breakages, as an extension, clang would accept this with a
+ `-Wambiguous-reversed-operator` warning.
+ Fixes `GH <https://github.com/llvm/llvm-project/issues/53954>`_.
+
+
C/C++ Language Potentially Breaking Changes
-------------------------------------------
@@ -117,8 +133,6 @@ C++ Language Changes
C++20 Feature Support
^^^^^^^^^^^^^^^^^^^^^
-- Fix a bug in conversion sequence of arguments to a function with reversed parameter order.
- Fixes `GH <https://github.com/llvm/llvm-project/issues/53954>`_.
C++23 Feature Support
^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index c271cebb9eb638f..53de7cb783a6ced 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -10085,10 +10085,14 @@ getImplicitObjectParamType(ASTContext &Context, const FunctionDecl *F) {
return M->getFunctionObjectParameterReferenceType();
}
-static bool haveSameParameterTypes(ASTContext &Context, const FunctionDecl *F1,
+static bool allowAmbiguityWithSelf(ASTContext &Context, const FunctionDecl *F1,
const FunctionDecl *F2) {
if (declaresSameEntity(F1, F2))
return true;
+ if (F1->isTemplateInstantiation() && F2->isTemplateInstantiation() &&
+ declaresSameEntity(F1->getPrimaryTemplate(), F2->getPrimaryTemplate())) {
+ return true;
+ }
auto NextParam = [&](const FunctionDecl *F, unsigned &I, bool First) {
if (First) {
@@ -10274,7 +10278,7 @@ bool clang::isBetterOverloadCandidate(
case ImplicitConversionSequence::Worse:
if (Cand1.Function && Cand2.Function &&
Cand1.isReversed() != Cand2.isReversed() &&
- haveSameParameterTypes(S.Context, Cand1.Function, Cand2.Function)) {
+ allowAmbiguityWithSelf(S.Context, Cand1.Function, Cand2.Function)) {
// Work around large-scale breakage caused by considering reversed
// forms of operator== in C++20:
//
@@ -14421,7 +14425,7 @@ ExprResult Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
llvm::SmallVector<FunctionDecl*, 4> AmbiguousWith;
for (OverloadCandidate &Cand : CandidateSet) {
if (Cand.Viable && Cand.Function && Cand.isReversed() &&
- haveSameParameterTypes(Context, Cand.Function, FnDecl)) {
+ allowAmbiguityWithSelf(Context, Cand.Function, FnDecl)) {
for (unsigned ArgIdx = 0; ArgIdx < 2; ++ArgIdx) {
if (CompareImplicitConversionSequences(
*this, OpLoc, Cand.Conversions[ArgIdx],
diff --git a/clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp b/clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp
index 02fe37dc1be5058..e075580828f42e5 100644
--- a/clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp
+++ b/clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp
@@ -325,38 +325,60 @@ bool x = X() == X(); // expected-warning {{ambiguous}}
} // namespace P2468R2
namespace GH53954{
-namespace test1 {
+namespace friend_template_1 {
struct P {
template <class T>
friend bool operator==(const P&, const T&); // expected-note {{candidate}} \
- // expected-note {{reversed parameter order}}
+ // expected-note {{ambiguous candidate function with reversed arguments}}
};
struct A : public P {};
struct B : public P {};
-bool check(A a, B b) { return a == b; } // expected-error {{ '==' is ambiguous}}
+bool check(A a, B b) { return a == b; } // expected-warning {{use of overloaded operator '==' (with operand types 'A' and 'B') to be ambiguous}}
}
-namespace test2 {
+namespace friend_template_2 {
struct P {
template <class T>
friend bool operator==(const T&, const P&); // expected-note {{candidate}} \
- // expected-note {{reversed parameter order}}
+ // expected-note {{ambiguous candidate function with reversed arguments}}
};
struct A : public P {};
struct B : public P {};
-bool check(A a, B b) { return a == b; } // expected-error {{ '==' is ambiguous}}
+bool check(A a, B b) { return a == b; } // expected-warning {{use of overloaded operator '==' (with operand types 'A' and 'B') to be ambiguous}}
}
-namespace test3 {
+namespace member_template {
struct P {
template<class S>
bool operator==(const S &) const; // expected-note {{candidate}} \
- // expected-note {{reversed parameter order}}
+ // expected-note {{ambiguous candidate function with reversed arguments}}
};
struct A : public P {};
struct B : public P {};
-bool check(A a, B b) { return a == b; } // expected-error {{ '==' is ambiguous}}
+bool check(A a, B b) { return a == b; } // expected-warning {{use of overloaded operator '==' (with operand types 'A' and 'B') to be ambiguous}}
}
+
+namespace non_member_template_1 {
+struct P {};
+template<class S>
+bool operator==(const P&, const S &); // expected-note {{candidate}} \
+ // expected-note {{ambiguous candidate function with reversed arguments}}
+
+struct A : public P {};
+struct B : public P {};
+bool check(A a, B b) { return a == b; } // expected-warning {{use of overloaded operator '==' (with operand types 'A' and 'B') to be ambiguous}}
+}
+}
+
+namespace non_member_template_2 {
+struct P {};
+template<class S>
+bool operator==(const S&, const P&); // expected-note {{candidate}} \
+ // expected-note {{ambiguous candidate function with reversed arguments}}
+
+struct A : public P {};
+struct B : public P {};
+bool check(A a, B b) { return a == b; } // expected-warning {{use of overloaded operator '==' (with operand types 'A' and 'B') to be ambiguous}}
}
#else // NO_ERRORS
More information about the cfe-commits
mailing list