[llvm-branch-commits] [clang] 76c7e0e - Use the correct namespace for looking up matching operator!= (#68922)
Tobias Hieta via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Mon Nov 20 00:53:59 PST 2023
Author: Utkarsh Saxena
Date: 2023-11-20T09:50:43+01:00
New Revision: 76c7e0e1e2d15706cb9ad28b607e937df5e00c24
URL: https://github.com/llvm/llvm-project/commit/76c7e0e1e2d15706cb9ad28b607e937df5e00c24
DIFF: https://github.com/llvm/llvm-project/commit/76c7e0e1e2d15706cb9ad28b607e937df5e00c24.diff
LOG: Use the correct namespace for looking up matching operator!= (#68922)
`S.getScopeForContext` determins the **active** scope associated with
the given `declContext`.
This fails to find the matching `operator!=` if candidate `operator==`
was found via ADL since that scope is not active.
Instead, just directly lookup using the namespace decl of `operator==`
Fixes #68901
(cherry picked from commit 9330261143ccbe947ef0687fd20747ba47f26879)
Added:
clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p2468R2.cppm
Modified:
clang/docs/ReleaseNotes.rst
clang/lib/Sema/SemaOverload.cpp
clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 879721c71add390..a1143e14562e34c 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -56,6 +56,10 @@ C/C++ Language Potentially Breaking Changes
array members for structs that contain them. This change is more consistent
with the behavior of GCC.
+- Fixed a bug in finding matching `operator!=` while adding reversed `operator==` as
+ outlined in "The Equality Operator You Are Looking For" (`P2468 <http://wg21.link/p2468r2>`_).
+ Fixes (`#68901: <https://github.com/llvm/llvm-project/issues/68901>`_).
+
C++ Specific Potentially Breaking Changes
-----------------------------------------
- Clang won't search for coroutine_traits in std::experimental namespace any more.
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index a3d9abb1537789f..aef8dc58a48dbcf 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -957,18 +957,13 @@ static bool shouldAddReversedEqEq(Sema &S, SourceLocation OpLoc,
return true;
}
// Otherwise the search scope is the namespace scope of which F is a member.
- LookupResult NonMembers(S, NotEqOp, OpLoc,
- Sema::LookupNameKind::LookupOperatorName);
- S.LookupName(NonMembers,
- S.getScopeForContext(EqFD->getEnclosingNamespaceContext()));
- NonMembers.suppressDiagnostics();
- for (NamedDecl *Op : NonMembers) {
- auto *FD = Op->getAsFunction();
- if(auto* UD = dyn_cast<UsingShadowDecl>(Op))
- FD = UD->getUnderlyingDecl()->getAsFunction();
- if (FunctionsCorrespond(S.Context, EqFD, FD) &&
- declaresSameEntity(cast<Decl>(EqFD->getDeclContext()),
- cast<Decl>(Op->getDeclContext())))
+ for (NamedDecl *Op : EqFD->getEnclosingNamespaceContext()->lookup(NotEqOp)) {
+ auto *NotEqFD = Op->getAsFunction();
+ 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())))
return false;
}
return true;
diff --git a/clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p2468R2.cppm b/clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p2468R2.cppm
new file mode 100644
index 000000000000000..05891618991da07
--- /dev/null
+++ b/clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p2468R2.cppm
@@ -0,0 +1,24 @@
+// RUN: rm -rf %t
+// RUN: mkdir %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 %t/A.cppm -emit-module-interface -o %t/A.pcm
+// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -I%t %t/p2468r2.cpp -verify
+
+//--- A.cppm
+module;
+export module A;
+export {
+namespace NS {
+struct S {};
+bool operator==(S, int);
+} // namespace NS
+}
+
+namespace NS { bool operator!=(S, int); } // Not visible.
+
+
+//--- p2468r2.cpp
+// expected-no-diagnostics
+import A;
+bool x = 0 == NS::S(); // Ok. operator!= from module A is not visible.
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..016eaf7f52876d5 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,170 @@ bool x = X() == X(); // expected-warning {{ambiguous}}
}
} // namespace P2468R2
+namespace GH53954{
+namespace friend_template_1 {
+struct P {
+ template <class T>
+ friend bool operator==(const P&, const T&); // 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 friend_template_2 {
+struct P {
+ template <class T>
+ friend bool operator==(const T&, 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}}
+}
+
+namespace member_template {
+struct P {
+ template<class S>
+ bool operator==(const S &) const; // 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_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 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
+
+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
namespace problem_cases {
More information about the llvm-branch-commits
mailing list