Skip to content

Commit 76c7e0e

Browse files
usx95tru
authored andcommitted
Use the correct namespace for looking up matching operator!= (llvm#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 llvm#68901 (cherry picked from commit 9330261)
1 parent b5cbb35 commit 76c7e0e

File tree

4 files changed

+199
-12
lines changed

4 files changed

+199
-12
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@ C/C++ Language Potentially Breaking Changes
5656
array members for structs that contain them. This change is more consistent
5757
with the behavior of GCC.
5858

59+
- Fixed a bug in finding matching `operator!=` while adding reversed `operator==` as
60+
outlined in "The Equality Operator You Are Looking For" (`P2468 <http://wg21.link/p2468r2>`_).
61+
Fixes (`#68901: <https://github.com/llvm/llvm-project/issues/68901>`_).
62+
5963
C++ Specific Potentially Breaking Changes
6064
-----------------------------------------
6165
- Clang won't search for coroutine_traits in std::experimental namespace any more.

clang/lib/Sema/SemaOverload.cpp

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -957,18 +957,13 @@ static bool shouldAddReversedEqEq(Sema &S, SourceLocation OpLoc,
957957
return true;
958958
}
959959
// Otherwise the search scope is the namespace scope of which F is a member.
960-
LookupResult NonMembers(S, NotEqOp, OpLoc,
961-
Sema::LookupNameKind::LookupOperatorName);
962-
S.LookupName(NonMembers,
963-
S.getScopeForContext(EqFD->getEnclosingNamespaceContext()));
964-
NonMembers.suppressDiagnostics();
965-
for (NamedDecl *Op : NonMembers) {
966-
auto *FD = Op->getAsFunction();
967-
if(auto* UD = dyn_cast<UsingShadowDecl>(Op))
968-
FD = UD->getUnderlyingDecl()->getAsFunction();
969-
if (FunctionsCorrespond(S.Context, EqFD, FD) &&
970-
declaresSameEntity(cast<Decl>(EqFD->getDeclContext()),
971-
cast<Decl>(Op->getDeclContext())))
960+
for (NamedDecl *Op : EqFD->getEnclosingNamespaceContext()->lookup(NotEqOp)) {
961+
auto *NotEqFD = Op->getAsFunction();
962+
if (auto *UD = dyn_cast<UsingShadowDecl>(Op))
963+
NotEqFD = UD->getUnderlyingDecl()->getAsFunction();
964+
if (FunctionsCorrespond(S.Context, EqFD, NotEqFD) && S.isVisible(NotEqFD) &&
965+
declaresSameEntity(cast<Decl>(EqFD->getEnclosingNamespaceContext()),
966+
cast<Decl>(Op->getLexicalDeclContext())))
972967
return false;
973968
}
974969
return true;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// RUN: rm -rf %t
2+
// RUN: mkdir %t
3+
// RUN: split-file %s %t
4+
//
5+
// RUN: %clang_cc1 -std=c++20 %t/A.cppm -emit-module-interface -o %t/A.pcm
6+
// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t -I%t %t/p2468r2.cpp -verify
7+
8+
//--- A.cppm
9+
module;
10+
export module A;
11+
export {
12+
namespace NS {
13+
struct S {};
14+
bool operator==(S, int);
15+
} // namespace NS
16+
}
17+
18+
namespace NS { bool operator!=(S, int); } // Not visible.
19+
20+
21+
//--- p2468r2.cpp
22+
// expected-no-diagnostics
23+
import A;
24+
bool x = 0 == NS::S(); // Ok. operator!= from module A is not visible.

clang/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,170 @@ bool x = X() == X(); // expected-warning {{ambiguous}}
324324
}
325325
} // namespace P2468R2
326326

327+
namespace GH53954{
328+
namespace friend_template_1 {
329+
struct P {
330+
template <class T>
331+
friend bool operator==(const P&, const T&); // expected-note {{candidate}} \
332+
// expected-note {{ambiguous candidate function with reversed arguments}}
333+
};
334+
struct A : public P {};
335+
struct B : public P {};
336+
bool check(A a, B b) { return a == b; } // expected-warning {{use of overloaded operator '==' (with operand types 'A' and 'B') to be ambiguous}}
337+
}
338+
339+
namespace friend_template_2 {
340+
struct P {
341+
template <class T>
342+
friend bool operator==(const T&, const P&); // expected-note {{candidate}} \
343+
// expected-note {{ambiguous candidate function with reversed arguments}}
344+
};
345+
struct A : public P {};
346+
struct B : public P {};
347+
bool check(A a, B b) { return a == b; } // expected-warning {{use of overloaded operator '==' (with operand types 'A' and 'B') to be ambiguous}}
348+
}
349+
350+
namespace member_template {
351+
struct P {
352+
template<class S>
353+
bool operator==(const S &) const; // expected-note {{candidate}} \
354+
// expected-note {{ambiguous candidate function with reversed arguments}}
355+
};
356+
struct A : public P {};
357+
struct B : public P {};
358+
bool check(A a, B b) { return a == b; } // expected-warning {{use of overloaded operator '==' (with operand types 'A' and 'B') to be ambiguous}}
359+
}
360+
361+
namespace non_member_template_1 {
362+
struct P {};
363+
template<class S>
364+
bool operator==(const P&, const S &); // expected-note {{candidate}} \
365+
// expected-note {{ambiguous candidate function with reversed arguments}}
366+
367+
struct A : public P {};
368+
struct B : public P {};
369+
bool check(A a, B b) { return a == b; } // expected-warning {{use of overloaded operator '==' (with operand types 'A' and 'B') to be ambiguous}}
370+
371+
template<class S>
372+
bool operator!=(const P&, const S &);
373+
bool fine(A a, B b) { return a == b; } // Ok. Found a matching operator!=.
374+
}
375+
}
376+
377+
378+
namespace ADL_GH68901{
379+
namespace test1 {
380+
namespace A {
381+
struct S {};
382+
bool operator==(S, int); // expected-note {{no known conversion from 'int' to 'S' for 1st argument}}
383+
bool a = 0 == A::S(); // Ok. Operator!= not visible.
384+
bool operator!=(S, int);
385+
} // namespace A
386+
bool a = 0 == A::S(); // expected-error {{invalid operands to binary expression ('int' and 'A::S')}}
387+
} // namespace test1
388+
389+
namespace test2 {
390+
namespace B {
391+
struct Derived {};
392+
struct Base : Derived {};
393+
394+
bool operator==(Derived& a, Base& b);
395+
bool operator!=(Derived& a, Base& b);
396+
} // namespace B
397+
398+
bool foo() {
399+
B::Base a,b;
400+
return a == b;
401+
}
402+
} // namespace test2
403+
404+
405+
namespace template_ {
406+
namespace ns {
407+
template <class T> struct A {};
408+
template <class T> struct B : A<T> {};
409+
410+
template <class T> bool operator==(B<T>, A<T>); // expected-note {{candidate template ignored: could not match 'B' against 'A'}}
411+
template <class T> bool operator!=(B<T>, A<T>);
412+
}
413+
414+
void test() {
415+
ns::A<int> a;
416+
ns::B<int> b;
417+
a == b; // expected-error {{invalid operands to binary expression}}
418+
}
419+
} // namespace test3
420+
421+
namespace using_not_eq {
422+
namespace A {
423+
struct S {};
424+
namespace B {
425+
bool operator!=(S, int);
426+
}
427+
bool operator==(S, int); // expected-note {{candidate}}
428+
using B::operator!=;
429+
} // namespace A
430+
bool a = 0 == A::S(); // expected-error {{invalid operands to binary expression}}
431+
} // namespace reversed_lookup_not_like_ADL
432+
433+
namespace using_eqeq {
434+
namespace A {
435+
struct S {};
436+
namespace B {
437+
bool operator==(S, int); // expected-note {{candidate}}
438+
bool operator!=(S, int);
439+
}
440+
using B::operator==;
441+
} // namespace A
442+
bool a = 0 == A::S(); // expected-error {{invalid operands to binary expression}}
443+
}
444+
445+
} //namespace ADL_GH68901
446+
447+
namespace function_scope_operator_eqeq {
448+
// For non-members, we always lookup for matching operator!= in the namespace scope of
449+
// operator== (and not in the scope of operator==).
450+
struct X { operator int(); };
451+
namespace test1{
452+
bool h(X x) {
453+
bool operator==(X, int); // expected-note {{reversed}}
454+
return x == x; // expected-warning {{ambiguous}}
455+
}
456+
457+
bool g(X x) {
458+
bool operator==(X, int); // expected-note {{reversed}}
459+
bool operator!=(X, int);
460+
return x == x; // expected-warning {{ambiguous}}
461+
}
462+
} // namespace test1
463+
464+
namespace test2 {
465+
bool operator!=(X, int);
466+
467+
bool h(X x) {
468+
bool operator==(X, int);
469+
return x == x;
470+
}
471+
472+
bool i(X x) {
473+
bool operator==(X, int);
474+
bool operator!=(X, int);
475+
return x == x;
476+
}
477+
} // namespace test2
478+
} // namespace function_scope_operator_eqeq
479+
480+
namespace non_member_template_2 {
481+
struct P {};
482+
template<class S>
483+
bool operator==(const S&, const P&); // expected-note {{candidate}} \
484+
// expected-note {{ambiguous candidate function with reversed arguments}}
485+
486+
struct A : public P {};
487+
struct B : public P {};
488+
bool check(A a, B b) { return a == b; } // expected-warning {{use of overloaded operator '==' (with operand types 'A' and 'B') to be ambiguous}}
489+
}
490+
327491
#else // NO_ERRORS
328492

329493
namespace problem_cases {

0 commit comments

Comments
 (0)