Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PR for llvm/llvm-project#68901 #778

Merged
merged 2 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
19 changes: 7 additions & 12 deletions clang/lib/Sema/SemaOverload.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,108 @@ bool x = X() == X(); // expected-warning {{ambiguous}}
}
} // namespace P2468R2

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

#else // NO_ERRORS

namespace problem_cases {
Expand Down
Loading