[clang] Recognize friend operator != to fix ambiguity with operator== (PR #70216)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Oct 25 07:49:45 PDT 2023
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Utkarsh Saxena (usx95)
<details>
<summary>Changes</summary>
The namespace lookup should give a matching friend operator!= as friend functions belong to the namespace scope (as opposed to the lexical class scope).
Thus to resolve ambiguity of operator== with itself, defining a corresponding friend operator!= should suffice.
Fixes: https://github.com/llvm/llvm-project/issues/70210
---
Full diff: https://github.com/llvm/llvm-project/pull/70216.diff
2 Files Affected:
- (modified) clang/lib/Sema/SemaOverload.cpp (+7-4)
- (modified) clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp (+163)
``````````diff
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index db386fef0661c05..92a58b3c3cf5ed7 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -14,6 +14,7 @@
#include "clang/AST/ASTLambda.h"
#include "clang/AST/CXXInheritance.h"
#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclFriend.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/DependenceFlags.h"
#include "clang/AST/Expr.h"
@@ -962,13 +963,16 @@ static bool shouldAddReversedEqEq(Sema &S, SourceLocation OpLoc,
return true;
}
// Otherwise the search scope is the namespace scope of which F is a member.
- for (NamedDecl *Op : EqFD->getEnclosingNamespaceContext()->lookup(NotEqOp)) {
+ DeclContext *EqDC = EqFD->getEnclosingNamespaceContext();
+ for (NamedDecl *Op : EqDC->lookup(NotEqOp)) {
auto *NotEqFD = Op->getAsFunction();
+ DeclContext *NotEqDC = Op->getFriendObjectKind()
+ ? NotEqFD->getEnclosingNamespaceContext()
+ : Op->getLexicalDeclContext();
if (auto *UD = dyn_cast<UsingShadowDecl>(Op))
NotEqFD = UD->getUnderlyingDecl()->getAsFunction();
if (FunctionsCorrespond(S.Context, EqFD, NotEqFD) && S.isVisible(NotEqFD) &&
- declaresSameEntity(cast<Decl>(EqFD->getEnclosingNamespaceContext()),
- cast<Decl>(Op->getLexicalDeclContext())))
+ declaresSameEntity(cast<Decl>(EqDC), cast<Decl>(NotEqDC)))
return false;
}
return true;
@@ -10086,7 +10090,6 @@ static bool haveSameParameterTypes(ASTContext &Context, const FunctionDecl *F1,
const FunctionDecl *F2) {
if (declaresSameEntity(F1, F2))
return true;
-
auto NextParam = [&](const FunctionDecl *F, unsigned &I, bool First) {
if (First) {
if (std::optional<QualType> T = getImplicitObjectParamType(Context, F))
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 d83a176ec07eec9..ec3cf18b377dfce 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
@@ -403,6 +403,169 @@ bool h(X x) {
return x == x; // expected-warning {{ambiguous}}
}
+namespace friend_opNE_GH{
+namespace test1 {
+struct S {
+ operator int();
+ friend bool operator==(const S &, int); // expected-note {{reversed}}
+};
+struct A : S {};
+struct B : S {};
+bool x = A{} == B{}; // expected-warning {{ambiguous}}
+}
+
+namespace test2 {
+struct S {
+ operator int();
+ friend bool operator==(const S &, int);
+ friend bool operator!=(const S &, int);
+};
+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_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}}
+
+template<class S>
+bool operator!=(const P&, const S &);
+bool fine(A a, B b) { return a == b; } // Ok. Found a matching operator!=.
+}
+}
+
+namespace friend_opNE_GH{
+namespace test1 {
+struct S {
+ operator int();
+ friend bool operator==(const S &, int); // expected-note {{reversed}}
+};
+struct A : S {};
+struct B : S {};
+bool x = A{} == B{}; // expected-warning {{ambiguous}}
+}
+
+namespace test2 {
+struct S {
+ operator int();
+ friend bool operator==(const S &, int);
+ friend bool operator!=(const S &, int);
+};
+struct A : S {};
+struct B : S {};
+bool x = A{} == B{};
+}
+}
+
+namespace ADL_GH68901{
+namespace test1 {
+namespace A {
+struct S {};
+bool operator==(S, int); // expected-note {{no known conversion from 'int' to 'S' for 1st argument}}
+bool a = 0 == A::S(); // Ok. Operator!= not visible.
+bool operator!=(S, int);
+} // namespace A
+bool a = 0 == A::S(); // expected-error {{invalid operands to binary expression ('int' and 'A::S')}}
+} // namespace test1
+
+namespace test2 {
+namespace B {
+struct Derived {};
+struct Base : Derived {};
+
+bool operator==(Derived& a, Base& b);
+bool operator!=(Derived& a, Base& b);
+} // namespace B
+
+bool foo() {
+ B::Base a,b;
+ return a == b;
+}
+} // namespace test2
+
+
+namespace template_ {
+namespace ns {
+template <class T> struct A {};
+template <class T> struct B : A<T> {};
+
+template <class T> bool operator==(B<T>, A<T>); // expected-note {{candidate template ignored: could not match 'B' against 'A'}}
+template <class T> bool operator!=(B<T>, A<T>);
+}
+
+void test() {
+ ns::A<int> a;
+ ns::B<int> b;
+ a == b; // expected-error {{invalid operands to binary expression}}
+}
+} // namespace test3
+
+namespace using_not_eq {
+namespace A {
+struct S {};
+namespace B {
+bool operator!=(S, int);
+}
+bool operator==(S, int); // expected-note {{candidate}}
+using B::operator!=;
+} // namespace A
+bool a = 0 == A::S(); // expected-error {{invalid operands to binary expression}}
+} // namespace reversed_lookup_not_like_ADL
+
+namespace using_eqeq {
+namespace A {
+struct S {};
+namespace B {
+bool operator==(S, int); // expected-note {{candidate}}
+bool operator!=(S, int);
+}
+using B::operator==;
+} // namespace A
+bool a = 0 == A::S(); // expected-error {{invalid operands to binary expression}}
+}
+
+} //namespace ADL_GH68901
+
+namespace function_scope_operator_eqeq {
+// For non-members, we always lookup for matching operator!= in the namespace scope of
+// operator== (and not in the scope of operator==).
+struct X { operator int(); };
+namespace test1{
+bool h(X x) {
+ bool operator==(X, int); // expected-note {{reversed}}
+ return x == x; // expected-warning {{ambiguous}}
+}
+
+bool g(X x) {
+ bool operator==(X, int); // expected-note {{reversed}}
+ bool operator!=(X, int);
+ return x == x; // expected-warning {{ambiguous}}
+}
+} // namespace test1
+
+namespace test2 {
+bool operator!=(X, int);
+
+bool h(X x) {
+ bool operator==(X, int);
+ return x == x;
+}
+
+bool i(X x) {
+ bool operator==(X, int);
+ bool operator!=(X, int);
+ return x == x;
+}
+} // namespace test2
+} // namespace function_scope_operator_eqeq
+
bool g(X x) {
bool operator==(X, int); // expected-note {{reversed}}
bool operator!=(X, int);
``````````
</details>
https://github.com/llvm/llvm-project/pull/70216
More information about the cfe-commits
mailing list