// RUN: %clang_cc1 -std=c++2a -verify %s // RUN: %clang_cc1 -std=c++2a -verify -Wall -DNO_ERRORS %s #ifndef NO_ERRORS namespace bullet3 { // the built-in candidates include all of the candidate operator fnuctions // [...] that, compared to the given operator // - do not have the same parameter-type-list as any non-member candidate enum E { e }; // Suppress both builtin operator<=>(E, E) and operator<(E, E). void operator<=>(E, E); // expected-note {{while rewriting}} bool cmp = e < e; // expected-error {{invalid operands to binary expression ('void' and 'int')}} // None of the other bullets have anything to test here. In principle we // need to suppress both builtin operator@(A, B) and operator@(B, A) when we // see a user-declared reversible operator@(A, B), and we do, but that's // untestable because the only built-in reversible candidates are // operator<=>(E, E) and operator==(E, E) for E an enumeration type, and // those are both symmetric anyway. } namespace bullet4 { // The rewritten candidate set is determined as follows: template struct X {}; X<1> x1; X<2> x2; struct Y { int operator<=>(X<2>) = delete; // #1member bool operator==(X<2>) = delete; // #2member }; Y y; // - For the relational operators, the rewritten candidates include all // non-rewritten candidates for the expression x <=> y. int operator<=>(X<1>, X<2>) = delete; // #1 // expected-note@#1 5{{candidate function has been explicitly deleted}} // expected-note@#1 5{{candidate function (with reversed parameter order) not viable: no known conversion from 'X<1>' to 'X<2>' for 1st argument}} bool lt = x1 < x2; // expected-error {{selected deleted operator '<=>'}} bool le = x1 <= x2; // expected-error {{selected deleted operator '<=>'}} bool gt = x1 > x2; // expected-error {{selected deleted operator '<=>'}} bool ge = x1 >= x2; // expected-error {{selected deleted operator '<=>'}} bool cmp = x1 <=> x2; // expected-error {{selected deleted operator '<=>'}} // expected-note@#1member 5{{candidate function has been explicitly deleted}} // expected-note@#1 5{{candidate function not viable: no known conversion from 'bullet4::Y' to 'X<1>' for 1st argument}} // expected-note@#1 5{{candidate function (with reversed parameter order) not viable: no known conversion from 'bullet4::Y' to 'X<2>' for 1st argument}} bool mem_lt = y < x2; // expected-error {{selected deleted operator '<=>'}} bool mem_le = y <= x2; // expected-error {{selected deleted operator '<=>'}} bool mem_gt = y > x2; // expected-error {{selected deleted operator '<=>'}} bool mem_ge = y >= x2; // expected-error {{selected deleted operator '<=>'}} bool mem_cmp = y <=> x2; // expected-error {{selected deleted operator '<=>'}} // - For the relational and three-way comparison operators, the rewritten // candidates also include a synthesized candidate, with the order of the // two parameters reversed, for each non-rewritten candidate for the // expression y <=> x. // expected-note@#1 5{{candidate function (with reversed parameter order) has been explicitly deleted}} // expected-note@#1 5{{candidate function not viable: no known conversion from 'X<2>' to 'X<1>' for 1st argument}} bool rlt = x2 < x1; // expected-error {{selected deleted operator '<=>'}} bool rle = x2 <= x1; // expected-error {{selected deleted operator '<=>'}} bool rgt = x2 > x1; // expected-error {{selected deleted operator '<=>'}} bool rge = x2 >= x1; // expected-error {{selected deleted operator '<=>'}} bool rcmp = x2 <=> x1; // expected-error {{selected deleted operator '<=>'}} // expected-note@#1member 5{{candidate function (with reversed parameter order) has been explicitly deleted}} // expected-note@#1 5{{candidate function not viable: no known conversion from 'X<2>' to 'X<1>' for 1st argument}} // expected-note@#1 5{{candidate function (with reversed parameter order) not viable: no known conversion from 'bullet4::Y' to 'X<1>' for 2nd argument}} bool mem_rlt = x2 < y; // expected-error {{selected deleted operator '<=>'}} bool mem_rle = x2 <= y; // expected-error {{selected deleted operator '<=>'}} bool mem_rgt = x2 > y; // expected-error {{selected deleted operator '<=>'}} bool mem_rge = x2 >= y; // expected-error {{selected deleted operator '<=>'}} bool mem_rcmp = x2 <=> y; // expected-error {{selected deleted operator '<=>'}} // For the != operator, the rewritten candidates include all non-rewritten // candidates for the expression x == y int operator==(X<1>, X<2>) = delete; // #2 // expected-note@#2 2{{candidate function has been explicitly deleted}} // expected-note@#2 2{{candidate function (with reversed parameter order) not viable: no known conversion from 'X<1>' to 'X<2>' for 1st argument}} bool eq = x1 == x2; // expected-error {{selected deleted operator '=='}} bool ne = x1 != x2; // expected-error {{selected deleted operator '=='}} // expected-note@#2member 2{{candidate function has been explicitly deleted}} // expected-note@#2 2{{candidate function not viable: no known conversion from 'bullet4::Y' to 'X<1>' for 1st argument}} // expected-note@#2 2{{candidate function (with reversed parameter order) not viable: no known conversion from 'bullet4::Y' to 'X<2>' for 1st argument}} bool mem_eq = y == x2; // expected-error {{selected deleted operator '=='}} bool mem_ne = y != x2; // expected-error {{selected deleted operator '=='}} // For the equality operators, the rewritten candidates also include a // synthesized candidate, with the order of the two parameters reversed, for // each non-rewritten candidate for the expression y == x // expected-note@#2 2{{candidate function (with reversed parameter order) has been explicitly deleted}} // expected-note@#2 2{{candidate function not viable: no known conversion from 'X<2>' to 'X<1>' for 1st argument}} bool req = x2 == x1; // expected-error {{selected deleted operator '=='}} bool rne = x2 != x1; // expected-error {{selected deleted operator '=='}} // expected-note@#2member 2{{candidate function (with reversed parameter order) has been explicitly deleted}} // expected-note@#2 2{{candidate function not viable: no known conversion from 'X<2>' to 'X<1>' for 1st argument}} // expected-note@#2 2{{candidate function (with reversed parameter order) not viable: no known conversion from 'bullet4::Y' to 'X<1>' for 2nd argument}} bool mem_req = x2 == y; // expected-error {{selected deleted operator '=='}} bool mem_rne = x2 != y; // expected-error {{selected deleted operator '=='}} // For all other operators, the rewritten candidate set is empty. X<3> operator+(X<1>, X<2>) = delete; // expected-note {{no known conversion from 'X<2>' to 'X<1>'}} X<3> reversed_add = x2 + x1; // expected-error {{invalid operands}} } namespace PR44627 { namespace ADL { struct type {}; bool operator==(type lhs, int rhs) { return true; } } bool b1 = ADL::type() == 0; bool b2 = 0 == ADL::type(); } // Various C++17 cases that are known to be broken by the C++20 rules. namespace problem_cases { // We can have an ambiguity between an operator and its reversed form. This // wasn't intended by the original "consistent comparison" proposal, and we // allow it as extension, picking the non-reversed form. struct A { bool operator==(const A&); // expected-note {{ambiguity is between a regular call to this operator and a call with the argument order reversed}} }; bool cmp_non_const = A() == A(); // expected-warning {{ambiguous}} struct B { virtual bool operator==(const B&) const; }; struct D : B { bool operator==(const B&) const override; // expected-note {{operator}} }; bool cmp_base_derived = D() == D(); // expected-warning {{ambiguous}} template struct CRTPBase { bool operator==(const T&) const; // expected-note {{operator}} expected-note {{reversed}} bool operator!=(const T&) const; // expected-note {{non-reversed}} }; struct CRTP : CRTPBase {}; bool cmp_crtp = CRTP() == CRTP(); // expected-warning-re {{ambiguous despite there being a unique best viable function{{$}}}}}} bool cmp_crtp2 = CRTP() != CRTP(); // expected-warning {{ambiguous despite there being a unique best viable function with non-reversed arguments}} // Given a choice between a rewritten and non-rewritten function with the // same parameter types, where the rewritten function is reversed and each // has a better conversion for one of the two arguments, prefer the // non-rewritten one. using UBool = signed char; // ICU uses this. struct ICUBase { virtual UBool operator==(const ICUBase&) const; UBool operator!=(const ICUBase &arg) const { return !operator==(arg); } }; struct ICUDerived : ICUBase { UBool operator==(const ICUBase&) const override; // expected-note {{declared here}} expected-note {{ambiguity is between}} }; bool cmp_icu = ICUDerived() != ICUDerived(); // expected-warning {{ambiguous}} expected-warning {{'bool', not 'problem_cases::UBool'}} } #else // NO_ERRORS namespace problem_cases { // We can select a reversed candidate where we used to select a non-reversed // one, and in the worst case this can dramatically change the meaning of the // program. Make sure we at least warn on the worst cases under -Wall. struct iterator; struct const_iterator { const_iterator(iterator); bool operator==(const const_iterator&) const; }; struct iterator { bool operator==(const const_iterator &o) const { // expected-warning {{all paths through this function will call itself}} return o == *this; } }; } #endif // NO_ERRORS