565 lines · cpp
1// RUN: %clang_cc1 -std=c++2a -verify %s2// RUN: %clang_cc1 -std=c++2a -verify -Wall -DNO_ERRORS %s3 4#ifndef NO_ERRORS5namespace bullet3 {6 // the built-in candidates include all of the candidate operator fnuctions7 // [...] that, compared to the given operator8 9 // - do not have the same parameter-type-list as any non-member candidate10 11 enum E { e };12 13 // Suppress both builtin operator<=>(E, E) and operator<(E, E).14 void operator<=>(E, E); // expected-note {{while rewriting}}15 bool cmp = e < e; // expected-error {{invalid operands to binary expression ('void' and 'int')}}16 17 // None of the other bullets have anything to test here. In principle we18 // need to suppress both builtin operator@(A, B) and operator@(B, A) when we19 // see a user-declared reversible operator@(A, B), and we do, but that's20 // untestable because the only built-in reversible candidates are21 // operator<=>(E, E) and operator==(E, E) for E an enumeration type, and22 // those are both symmetric anyway.23}24 25namespace bullet4 {26 // The rewritten candidate set is determined as follows:27 28 template<int> struct X {};29 X<1> x1;30 X<2> x2;31 32 struct Y {33 int operator<=>(X<2>) = delete; // #1member34 bool operator==(X<2>) = delete; // #2member35 };36 Y y;37 38 // - For the relational operators, the rewritten candidates include all39 // non-rewritten candidates for the expression x <=> y.40 int operator<=>(X<1>, X<2>) = delete; // #141 42 // expected-note@#1 5{{candidate function has been explicitly deleted}}43 // expected-note@#1 5{{candidate function (with reversed parameter order) not viable: no known conversion from 'X<1>' to 'X<2>' for 1st argument}}44 bool lt = x1 < x2; // expected-error {{selected deleted operator '<=>'}}45 bool le = x1 <= x2; // expected-error {{selected deleted operator '<=>'}}46 bool gt = x1 > x2; // expected-error {{selected deleted operator '<=>'}}47 bool ge = x1 >= x2; // expected-error {{selected deleted operator '<=>'}}48 bool cmp = x1 <=> x2; // expected-error {{selected deleted operator '<=>'}}49 50 // expected-note@#1member 5{{candidate function has been explicitly deleted}}51 // expected-note@#1 5{{candidate function not viable: no known conversion from 'Y' to 'X<1>' for 1st argument}}52 // expected-note@#1 5{{candidate function (with reversed parameter order) not viable: no known conversion from 'Y' to 'X<2>' for 1st argument}}53 bool mem_lt = y < x2; // expected-error {{selected deleted operator '<=>'}}54 bool mem_le = y <= x2; // expected-error {{selected deleted operator '<=>'}}55 bool mem_gt = y > x2; // expected-error {{selected deleted operator '<=>'}}56 bool mem_ge = y >= x2; // expected-error {{selected deleted operator '<=>'}}57 bool mem_cmp = y <=> x2; // expected-error {{selected deleted operator '<=>'}}58 59 // - For the relational and three-way comparison operators, the rewritten60 // candidates also include a synthesized candidate, with the order of the61 // two parameters reversed, for each non-rewritten candidate for the62 // expression y <=> x.63 64 // expected-note@#1 5{{candidate function (with reversed parameter order) has been explicitly deleted}}65 // expected-note@#1 5{{candidate function not viable: no known conversion from 'X<2>' to 'X<1>' for 1st argument}}66 bool rlt = x2 < x1; // expected-error {{selected deleted operator '<=>'}}67 bool rle = x2 <= x1; // expected-error {{selected deleted operator '<=>'}}68 bool rgt = x2 > x1; // expected-error {{selected deleted operator '<=>'}}69 bool rge = x2 >= x1; // expected-error {{selected deleted operator '<=>'}}70 bool rcmp = x2 <=> x1; // expected-error {{selected deleted operator '<=>'}}71 72 // expected-note@#1member 5{{candidate function (with reversed parameter order) has been explicitly deleted}}73 // expected-note@#1 5{{candidate function not viable: no known conversion from 'X<2>' to 'X<1>' for 1st argument}}74 // expected-note@#1 5{{candidate function (with reversed parameter order) not viable: no known conversion from 'Y' to 'X<1>' for 2nd argument}}75 bool mem_rlt = x2 < y; // expected-error {{selected deleted operator '<=>'}}76 bool mem_rle = x2 <= y; // expected-error {{selected deleted operator '<=>'}}77 bool mem_rgt = x2 > y; // expected-error {{selected deleted operator '<=>'}}78 bool mem_rge = x2 >= y; // expected-error {{selected deleted operator '<=>'}}79 bool mem_rcmp = x2 <=> y; // expected-error {{selected deleted operator '<=>'}}80 81 // For the != operator, the rewritten candidates include all non-rewritten82 // candidates for the expression x == y83 int operator==(X<1>, X<2>) = delete; // #284 85 // expected-note@#2 2{{candidate function has been explicitly deleted}}86 // expected-note@#2 2{{candidate function (with reversed parameter order) not viable: no known conversion from 'X<1>' to 'X<2>' for 1st argument}}87 bool eq = x1 == x2; // expected-error {{selected deleted operator '=='}}88 bool ne = x1 != x2; // expected-error {{selected deleted operator '=='}}89 90 // expected-note@#2member 2{{candidate function has been explicitly deleted}}91 // expected-note@#2 2{{candidate function not viable: no known conversion from 'Y' to 'X<1>' for 1st argument}}92 // expected-note@#2 2{{candidate function (with reversed parameter order) not viable: no known conversion from 'Y' to 'X<2>' for 1st argument}}93 bool mem_eq = y == x2; // expected-error {{selected deleted operator '=='}}94 bool mem_ne = y != x2; // expected-error {{selected deleted operator '=='}}95 96 // For the equality operators, the rewritten candidates also include a97 // synthesized candidate, with the order of the two parameters reversed, for98 // each non-rewritten candidate for the expression y == x99 100 // expected-note@#2 2{{candidate function (with reversed parameter order) has been explicitly deleted}}101 // expected-note@#2 2{{candidate function not viable: no known conversion from 'X<2>' to 'X<1>' for 1st argument}}102 bool req = x2 == x1; // expected-error {{selected deleted operator '=='}}103 bool rne = x2 != x1; // expected-error {{selected deleted operator '=='}}104 105 // expected-note@#2member 2{{candidate function (with reversed parameter order) has been explicitly deleted}}106 // expected-note@#2 2{{candidate function not viable: no known conversion from 'X<2>' to 'X<1>' for 1st argument}}107 // expected-note@#2 2{{candidate function (with reversed parameter order) not viable: no known conversion from 'Y' to 'X<1>' for 2nd argument}}108 bool mem_req = x2 == y; // expected-error {{selected deleted operator '=='}}109 bool mem_rne = x2 != y; // expected-error {{selected deleted operator '=='}}110 111 // For all other operators, the rewritten candidate set is empty.112 X<3> operator+(X<1>, X<2>) = delete; // expected-note {{no known conversion from 'X<2>' to 'X<1>'}}113 X<3> reversed_add = x2 + x1; // expected-error {{invalid operands}}114}115 116namespace PR44627 {117 namespace ADL {118 struct type {};119 bool operator==(type lhs, int rhs) {120 return true;121 }122 }123 124 bool b1 = ADL::type() == 0;125 bool b2 = 0 == ADL::type();126}127 128namespace P2468R2 {129// Problem cases prior to P2468R2 but now intentionally rejected.130struct SymmetricNonConst {131 bool operator==(const SymmetricNonConst&); // expected-note {{ambiguity is between a regular call to this operator and a call with the argument order reversed}}132 // expected-note@-1 {{mark 'operator==' as const or add a matching 'operator!=' to resolve the ambiguity}}133};134bool cmp_non_const = SymmetricNonConst() == SymmetricNonConst(); // expected-warning {{ambiguous}}135 136struct SymmetricConst {137 bool operator==(const SymmetricConst&) const;138};139bool cmp_const = SymmetricConst() == SymmetricConst();140 141struct SymmetricNonConstWithoutConstRef {142 bool operator==(SymmetricNonConstWithoutConstRef);143};144bool cmp_non_const_wo_ref = SymmetricNonConstWithoutConstRef() == SymmetricNonConstWithoutConstRef();145 146struct B {147 virtual bool operator==(const B&) const;148};149struct D : B {150 bool operator==(const B&) const override; // expected-note {{operator}}151};152bool cmp_base_derived = D() == D(); // expected-warning {{ambiguous}}153 154// Reversed "3" not used because we find "2".155// Rewrite != from "3" but warn that "chosen rewritten candidate must return cv-bool".156using UBool = signed char;157struct ICUBase {158 virtual UBool operator==(const ICUBase&) const; // 1.159 UBool operator!=(const ICUBase &arg) const { return !operator==(arg); } // 2.160};161struct ICUDerived : ICUBase {162 // 3.163 UBool operator==(const ICUBase&) const override; // expected-note {{declared here}}164};165bool cmp_icu = ICUDerived() != ICUDerived(); // expected-warning {{ISO C++20 requires return type of selected 'operator==' function for rewritten '!=' comparison to be 'bool', not 'UBool' (aka 'signed char')}}166// Accepted by P2468R2.167// 1168struct S {169 bool operator==(const S&) { return true; }170 bool operator!=(const S&) { return false; }171};172bool ts = S{} != S{};173// 2174template<typename T> struct CRTPBase {175 bool operator==(const T&) const;176 bool operator!=(const T&) const;177};178struct CRTP : CRTPBase<CRTP> {};179bool cmp_crtp = CRTP() == CRTP();180bool cmp_crtp2 = CRTP() != CRTP();181// https://github.com/llvm/llvm-project/issues/57711182namespace issue_57711 {183template <class T>184bool compare(T l, T r)185 requires requires { l == r; } {186 return l == r;187}188 189void test() {190 compare(CRTP(), CRTP()); // previously this was a hard error (due to SFINAE failure).191}192}193// 3194template <bool>195struct GenericIterator {196 using ConstIterator = GenericIterator<true>;197 using NonConstIterator = GenericIterator<false>;198 GenericIterator() = default;199 GenericIterator(const NonConstIterator&);200 201 bool operator==(ConstIterator) const;202 bool operator!=(ConstIterator) const;203};204using Iterator = GenericIterator<false>;205 206bool biter = Iterator{} == Iterator{};207 208// Intentionally rejected by P2468R2209struct ImplicitInt {210 ImplicitInt();211 ImplicitInt(int*);212 bool operator==(const ImplicitInt&) const; // expected-note {{candidate function (with reversed parameter order)}}213 operator int*() const;214};215bool implicit_int = nullptr != ImplicitInt{}; // expected-error {{use of overloaded operator '!=' is ambiguous (with operand types 'std::nullptr_t' and 'ImplicitInt')}}216 // expected-note@-1 4 {{built-in candidate operator!=}}217 218// https://eel.is/c++draft/over.match.oper#example-2219namespace example {220struct A {};221template<typename T> bool operator==(A, T); // 1. expected-note {{candidate function template not viable: no known conversion from 'int' to 'A' for 1st argument}}222bool a1 = 0 == A(); // OK, calls reversed 1223template<typename T> bool operator!=(A, T);224bool a2 = 0 == A(); // expected-error {{invalid operands to binary expression ('int' and 'A')}}225 226struct B {227 bool operator==(const B&); // 2228 // expected-note@-1 {{ambiguity is between a regular call to this operator and a call with the argument order reversed}}229};230struct C : B {231 C();232 C(B);233 bool operator!=(const B&); // 3234};235bool c1 = B() == C(); // OK, calls 2; reversed 2 is not a candidate because search for operator!= in C finds 3236bool c2 = C() == B(); // Search for operator!= inside B never finds 3. expected-warning {{ISO C++20 considers use of overloaded operator '==' (with operand types 'C' and 'B') to be ambiguous despite there being a unique best viable function}}237 238struct D {};239template<typename T> bool operator==(D, T); // 4240inline namespace N {241 template<typename T> bool operator!=(D, T); // 5242}243bool d1 = 0 == D(); // OK, calls reversed 4; 5 does not forbid 4 as a rewrite target as "search" does not look inside inline namespaces.244} // namespace example245 246namespace template_tests {247namespace template_head_does_not_match {248struct A {};249template<typename T, class U = int> bool operator==(A, T);250template <class T> bool operator!=(A, T);251bool x = 0 == A(); // Ok. Use rewritten candidate.252}253 254namespace template_with_different_param_name_are_equivalent {255struct A {};256template<typename T> bool operator==(A, T); // expected-note {{candidate function template not viable: no known conversion from 'int' to 'A' for 1st argument}}257template <typename U> bool operator!=(A, U);258bool x = 0 == A(); // expected-error {{invalid operands to binary expression ('int' and 'A')}}259}260 261namespace template_and_non_template {262struct A {263template<typename T> bool operator==(const T&);264// expected-note@-1{{mark 'operator==' as const or add a matching 'operator!=' to resolve the ambiguity}}265// expected-note@-2{{ambiguity is between a regular call to this operator and a call with the argument order reversed}}266};267bool a = A() == A(); // expected-warning {{ambiguous despite there being a unique best viable function}}268 269struct B {270template<typename T> bool operator==(const T&) const;271bool operator!=(const B&);272};273bool b = B() == B(); // ok. No rewrite due to const.274 275struct C {};276template <class T=int>277bool operator==(C, int);278bool operator!=(C, int);279bool c = 0 == C(); // Ok. Use rewritten candidate as the non-template 'operator!=' does not correspond to template 'operator=='280}281} // template_tests282 283namespace using_decls {284namespace simple {285struct C {};286bool operator==(C, int); // expected-note {{candidate function not viable: no known conversion from 'int' to 'C' for 1st argument}}287bool a = 0 == C(); // Ok. Use rewritten candidate.288namespace other_ns { bool operator!=(C, int); }289bool b = 0 == C(); // Ok. Use rewritten candidate.290using other_ns::operator!=;291bool c = 0 == C(); // Rewrite not possible. expected-error {{invalid operands to binary expression ('int' and 'C')}}292}293namespace templated {294struct C {};295template<typename T>296bool operator==(C, T); // expected-note {{candidate function template not viable: no known conversion from 'int' to 'C' for 1st argument}}297bool a = 0 == C(); // Ok. Use rewritten candidate.298namespace other_ns { template<typename T> bool operator!=(C, T); }299bool b = 0 == C(); // Ok. Use rewritten candidate.300using other_ns::operator!=;301bool c = 0 == C(); // Rewrite not possible. expected-error {{invalid operands to binary expression ('int' and 'C')}}302} // templated303} // using_decls304 305// FIXME(GH58185): Match requires clause.306namespace match_requires_clause {307template<int x>308struct A {309bool operator==(int) requires (x==1); // 1.310bool operator!=(int) requires (x==2); // 2.311};312int a1 = 0 == A<1>(); // Should not find 2 as the requires clause does not match. \313 // expected-error {{invalid operands to binary expression ('int' and 'A<1>')}}314}315 316namespace static_operators {317// Verify no crash.318struct X {319 bool operator ==(X const&); // expected-note {{ambiguity is between a regular call}}320 // expected-note@-1 {{mark 'operator==' as const or add a matching 'operator!=' to resolve the ambiguity}}321 static bool operator !=(X const&, X const&); // expected-error {{overloaded 'operator!=' cannot be a static member function}}322};323bool x = X() == X(); // expected-warning {{ambiguous}}324}325} // namespace P2468R2326 327namespace GH53954{328namespace friend_template_1 {329struct P {330 template <class T>331 friend bool operator==(const P&, const T&) { return true; } // expected-note {{candidate}} \332 // expected-note {{ambiguous candidate function with reversed arguments}}333};334struct A : public P {};335struct B : public P {};336bool 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 339namespace friend_template_2 {340struct P {341 template <class T>342 friend bool operator==(const T&, const P&) { return true; } // expected-note {{candidate}} \343 // expected-note {{ambiguous candidate function with reversed arguments}}344};345struct A : public P {};346struct B : public P {};347bool 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 350namespace friend_template_class_template {351template<class S>352struct P {353 template <class T>354 friend bool operator==(const T&, const P&) { // expected-note 2 {{candidate}}355 return true;356 }357};358struct A : public P<int> {};359struct B : public P<bool> {};360bool check(A a, B b) { return a == b; } // expected-warning {{ambiguous}}361}362 363namespace friend_template_fixme {364// FIXME(GH70210): This should not rewrite operator== and definitely not a hard error.365struct P {366 template <class T>367 friend bool operator==(const T &, const P &) { return true; } // expected-note 2 {{candidate}}368 template <class T>369 friend bool operator!=(const T &, const P &) { return true; } // expected-note {{candidate}}370};371struct A : public P {};372struct B : public P {};373bool check(A a, B b) { return a != b; } // expected-error{{ambiguous}}374}375 376namespace member_template_1 {377struct P {378 template<class S>379 bool operator==(const S &) const; // expected-note {{candidate}} \380 // expected-note {{ambiguous candidate function with reversed arguments}}381};382struct A : public P {};383struct B : public P {};384bool check(A a, B b) { return a == b; } // expected-warning {{use of overloaded operator '==' (with operand types 'A' and 'B') to be ambiguous}}385} // namespace member_template386 387namespace member_template_2{388template <typename T>389class Foo {390 public:391 template <typename U = T>392 bool operator==(const Foo& other) const;393};394bool x = Foo<int>{} == Foo<int>{};395} // namespace template_member_opeqeq396 397namespace non_member_template_1 {398struct P {};399template<class S>400bool operator==(const P&, const S &); // expected-note {{candidate}} \401 // expected-note {{ambiguous candidate function with reversed arguments}}402 403struct A : public P {};404struct B : public P {};405bool check(A a, B b) { return a == b; } // expected-warning {{use of overloaded operator '==' (with operand types 'A' and 'B') to be ambiguous}}406 407template<class S> bool operator!=(const P&, const S &);408bool fine(A a, B b) { return a == b; } // Ok. Found a matching operator!=.409} // namespace non_member_template_1410 411namespace non_member_template_2 {412struct P {};413template<class S>414bool operator==(const S&, const P&); // expected-note {{candidate}} \415 // expected-note {{ambiguous candidate function with reversed arguments}}416 417struct A : public P {};418struct B : public P {};419bool check(A a, B b) { return a == b; } // expected-warning {{use of overloaded operator '==' (with operand types 'A' and 'B') to be ambiguous}}420} // namespace non_member_template_2421 422namespace class_and_member_template {423template <class T>424struct S {425 template <typename OtherT>426 bool operator==(const OtherT &rhs); // expected-note {{candidate}} \427 // expected-note {{reversed arguments}}428};429struct A : S<int> {};430struct B : S<bool> {};431bool x = A{} == B{}; // expected-warning {{ambiguous}}432} // namespace class_and_member_template433 434namespace ambiguous_case {435template <class T>436struct Foo {};437template <class T, class U> bool operator==(Foo<U>, Foo<T*>); // expected-note{{candidate}}438template <class T, class U> bool operator==(Foo<T*>, Foo<U>); // expected-note{{candidate}}439 440void test() {441 Foo<int*>() == Foo<int*>(); // expected-error{{ambiguous}}442}443} // namespace ambiguous_case444} // namespace445namespace ADL_GH68901{446namespace test1 {447namespace A {448struct S {};449bool operator==(S, int); // expected-note {{no known conversion from 'int' to 'S' for 1st argument}}450bool a = 0 == A::S(); // Ok. Operator!= not visible.451bool operator!=(S, int);452} // namespace A453bool a = 0 == A::S(); // expected-error {{invalid operands to binary expression ('int' and 'A::S')}}454} // namespace test1455 456namespace test2 {457namespace B {458struct Derived {};459struct Base : Derived {};460 461bool operator==(Derived& a, Base& b);462bool operator!=(Derived& a, Base& b);463} // namespace B464 465bool foo() {466 B::Base a,b;467 return a == b;468}469} // namespace test2470 471 472namespace template_ {473namespace ns {474template <class T> struct A {};475template <class T> struct B : A<T> {};476 477template <class T> bool operator==(B<T>, A<T>); // expected-note {{candidate template ignored: could not match 'B' against 'ns::A'}}478template <class T> bool operator!=(B<T>, A<T>);479}480 481void test() {482 ns::A<int> a;483 ns::B<int> b;484 a == b; // expected-error {{invalid operands to binary expression}}485}486} // namespace test3487 488namespace using_not_eq {489namespace A {490struct S {};491namespace B {492bool operator!=(S, int);493}494bool operator==(S, int); // expected-note {{candidate}}495using B::operator!=;496} // namespace A497bool a = 0 == A::S(); // expected-error {{invalid operands to binary expression}}498} // namespace reversed_lookup_not_like_ADL499 500namespace using_eqeq {501namespace A {502struct S {};503namespace B {504bool operator==(S, int); // expected-note {{candidate}}505bool operator!=(S, int);506}507using B::operator==;508} // namespace A509bool a = 0 == A::S(); // expected-error {{invalid operands to binary expression}}510}511 512} // namespace ADL_GH68901513 514namespace function_scope_operator_eqeq {515// For non-members, we always lookup for matching operator!= in the namespace scope of516// operator== (and not in the scope of operator==).517struct X { operator int(); };518namespace test1{519bool h(X x) {520 bool operator==(X, int); // expected-note {{reversed}}521 return x == x; // expected-warning {{ambiguous}}522}523 524bool g(X x) {525 bool operator==(X, int); // expected-note {{reversed}}526 bool operator!=(X, int);527 return x == x; // expected-warning {{ambiguous}}528}529} // namespace test1530 531namespace test2 {532bool operator!=(X, int);533 534bool h(X x) {535 bool operator==(X, int);536 return x == x;537}538 539bool i(X x) {540 bool operator==(X, int);541 bool operator!=(X, int);542 return x == x;543}544} // namespace test2545} // namespace function_scope_operator_eqeq546 547#else // NO_ERRORS548 549namespace problem_cases {550 // We can select a reversed candidate where we used to select a non-reversed551 // one, and in the worst case this can dramatically change the meaning of the552 // program. Make sure we at least warn on the worst cases under -Wall.553 struct iterator;554 struct const_iterator {555 const_iterator(iterator);556 bool operator==(const const_iterator&) const;557 };558 struct iterator {559 bool operator==(const const_iterator &o) const { // expected-warning {{all paths through this function will call itself}}560 return o == *this;561 }562 };563}564#endif // NO_ERRORS565