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

// 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<int> 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 'Y' to 'X<1>' for 1st argument}}
  // expected-note@#1 5{{candidate function (with reversed parameter order) not viable: no known conversion from '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 '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 'Y' to 'X<1>' for 1st argument}}
  // expected-note@#2 2{{candidate function (with reversed parameter order) not viable: no known conversion from '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 '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();
}

namespace P2468R2 {
// Problem cases prior to P2468R2 but now intentionally rejected.
struct SymmetricNonConst {
  bool operator==(const SymmetricNonConst&); // expected-note {{ambiguity is between a regular call to this operator and a call with the argument order reversed}}
  // expected-note@-1 {{mark 'operator==' as const or add a matching 'operator!=' to resolve the ambiguity}}
};
bool cmp_non_const = SymmetricNonConst() == SymmetricNonConst(); // expected-warning {{ambiguous}}

struct SymmetricConst {
  bool operator==(const SymmetricConst&) const;
};
bool cmp_const = SymmetricConst() == SymmetricConst();

struct SymmetricNonConstWithoutConstRef {
  bool operator==(SymmetricNonConstWithoutConstRef);
};
bool cmp_non_const_wo_ref = SymmetricNonConstWithoutConstRef() == SymmetricNonConstWithoutConstRef();

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}}

// Reversed "3" not used because we find "2".
// Rewrite != from "3" but warn that "chosen rewritten candidate must return cv-bool".
using UBool = signed char;
struct ICUBase {
  virtual UBool operator==(const ICUBase&) const; // 1.
  UBool operator!=(const ICUBase &arg) const { return !operator==(arg); } // 2.
};
struct ICUDerived : ICUBase {
  // 3.
  UBool operator==(const ICUBase&) const override; // expected-note {{declared here}}
};
bool 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')}}
// Accepted by P2468R2.
// 1
struct S {
  bool operator==(const S&) { return true; }
  bool operator!=(const S&) { return false; }
};
bool ts = S{} != S{};
// 2
template<typename T> struct CRTPBase {
  bool operator==(const T&) const;
  bool operator!=(const T&) const;
};
struct CRTP : CRTPBase<CRTP> {};
bool cmp_crtp = CRTP() == CRTP();
bool cmp_crtp2 = CRTP() != CRTP();
// https://github.com/llvm/llvm-project/issues/57711
namespace issue_57711 {
template <class T>
bool compare(T l, T r)
    requires requires { l == r; } {
  return l == r;
}

void test() {
  compare(CRTP(), CRTP()); // previously this was a hard error (due to SFINAE failure).
}
}
// 3
template <bool>
struct GenericIterator {
  using ConstIterator = GenericIterator<true>;
  using NonConstIterator = GenericIterator<false>;
  GenericIterator() = default;
  GenericIterator(const NonConstIterator&);

  bool operator==(ConstIterator) const;
  bool operator!=(ConstIterator) const;
};
using Iterator = GenericIterator<false>;

bool biter = Iterator{} == Iterator{};

// Intentionally rejected by P2468R2
struct ImplicitInt {
  ImplicitInt();
  ImplicitInt(int*);
  bool operator==(const ImplicitInt&) const; // expected-note {{candidate function (with reversed parameter order)}}
  operator int*() const;
};
bool implicit_int = nullptr != ImplicitInt{}; // expected-error {{use of overloaded operator '!=' is ambiguous (with operand types 'std::nullptr_t' and 'ImplicitInt')}}
                                              // expected-note@-1 4 {{built-in candidate operator!=}}

// https://eel.is/c++draft/over.match.oper#example-2
namespace example {
struct A {};
template<typename T> bool operator==(A, T);     // 1. expected-note {{candidate function template not viable: no known conversion from 'int' to 'A' for 1st argument}}
bool a1 = 0 == A();                             // OK, calls reversed 1
template<typename T> bool operator!=(A, T);
bool a2 = 0 == A();  // expected-error {{invalid operands to binary expression ('int' and 'A')}}

struct B {
  bool operator==(const B&);    // 2
  // expected-note@-1 {{ambiguity is between a regular call to this operator and a call with the argument order reversed}}
};
struct C : B {
  C();
  C(B);
  bool operator!=(const B&);    // 3
};
bool c1 = B() == C(); // OK, calls 2; reversed 2 is not a candidate because search for operator!= in C finds 3
bool 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}}

struct D {};
template<typename T> bool operator==(D, T);     // 4
inline namespace N {
  template<typename T> bool operator!=(D, T);   // 5
}
bool d1 = 0 == D();  // OK, calls reversed 4; 5 does not forbid 4 as a rewrite target as "search" does not look inside inline namespaces.
} // namespace example

namespace template_tests {
namespace template_head_does_not_match {
struct A {};
template<typename T, class U = int> bool operator==(A, T);
template <class T> bool operator!=(A, T);
bool x = 0 == A(); // Ok. Use rewritten candidate.
}

namespace template_with_different_param_name_are_equivalent {
struct A {};
template<typename T> bool operator==(A, T); // expected-note {{candidate function template not viable: no known conversion from 'int' to 'A' for 1st argument}}
template <typename U> bool operator!=(A, U);
bool x = 0 == A(); // expected-error {{invalid operands to binary expression ('int' and 'A')}}
}

namespace template_and_non_template {
struct A {
template<typename T> bool operator==(const T&);
// expected-note@-1{{mark 'operator==' as const or add a matching 'operator!=' to resolve the ambiguity}}
// expected-note@-2{{ambiguity is between a regular call to this operator and a call with the argument order reversed}}
};
bool a = A() == A(); // expected-warning {{ambiguous despite there being a unique best viable function}}

struct B {
template<typename T> bool operator==(const T&) const;
bool operator!=(const B&);
};
bool b = B() == B(); // ok. No rewrite due to const.

struct C {};
template <class T=int>
bool operator==(C, int);
bool operator!=(C, int);
bool c = 0 == C(); // Ok. Use rewritten candidate as the non-template 'operator!=' does not correspond to template 'operator=='
}
} // template_tests

namespace using_decls {
namespace simple {
struct C {};
bool operator==(C, int); // expected-note {{candidate function not viable: no known conversion from 'int' to 'C' for 1st argument}}
bool a = 0 == C(); // Ok. Use rewritten candidate.
namespace other_ns { bool operator!=(C, int); }
bool b = 0 == C(); // Ok. Use rewritten candidate.
using other_ns::operator!=;
bool c = 0 == C(); // Rewrite not possible. expected-error {{invalid operands to binary expression ('int' and 'C')}}
}
namespace templated {
struct C {};
template<typename T>
bool operator==(C, T); // expected-note {{candidate function template not viable: no known conversion from 'int' to 'C' for 1st argument}}
bool a = 0 == C(); // Ok. Use rewritten candidate.
namespace other_ns { template<typename T> bool operator!=(C, T); }
bool b = 0 == C(); // Ok. Use rewritten candidate.
using other_ns::operator!=;
bool c = 0 == C(); // Rewrite not possible. expected-error {{invalid operands to binary expression ('int' and 'C')}}
} // templated
} // using_decls

// FIXME(GH58185): Match requires clause.
namespace match_requires_clause {
template<int x>
struct A {
bool operator==(int) requires (x==1); // 1.
bool operator!=(int) requires (x==2); // 2.
};
int a1 = 0 == A<1>(); // Should not find 2 as the requires clause does not match. \
                      // expected-error {{invalid operands to binary expression ('int' and 'A<1>')}}
}

namespace static_operators {
// Verify no crash.
struct X {
  bool operator ==(X const&); // expected-note {{ambiguity is between a regular call}}
                              // expected-note@-1 {{mark 'operator==' as const or add a matching 'operator!=' to resolve the ambiguity}}
  static bool operator !=(X const&, X const&); // expected-error {{overloaded 'operator!=' cannot be a static member function}}
};
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&) { return true; } // 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&) { return true; } // 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_class_template {
template<class S>
struct P {
  template <class T>
  friend bool operator==(const T&, const P&) { // expected-note 2 {{candidate}}
    return true;
  }
};
struct A : public P<int> {};
struct B : public P<bool> {};
bool check(A a, B b) { return a == b; } // expected-warning {{ambiguous}}
}

namespace friend_template_fixme {
// FIXME(GH70210): This should not rewrite operator== and definitely not a hard error.
struct P {
  template <class T>
  friend bool operator==(const T &, const P &) { return true; } // expected-note 2 {{candidate}}
  template <class T>
  friend bool operator!=(const T &, const P &) { return true; } // expected-note {{candidate}}
};
struct A : public P {};
struct B : public P {};
bool check(A a, B b) { return a != b; } // expected-error{{ambiguous}}
}

namespace member_template_1 {
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 member_template

namespace member_template_2{
template <typename T>
class Foo {
 public:
  template <typename U = T>
  bool operator==(const Foo& other) const;
};
bool x = Foo<int>{} == Foo<int>{};
} // namespace template_member_opeqeq

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 non_member_template_1

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}}
} // namespace non_member_template_2

namespace class_and_member_template {
template <class T>
struct S {
    template <typename OtherT>
    bool operator==(const OtherT &rhs); // expected-note {{candidate}} \
                                        // expected-note {{reversed arguments}}
};
struct A : S<int> {};
struct B : S<bool> {};
bool x = A{} == B{}; // expected-warning {{ambiguous}}
} // namespace class_and_member_template

namespace ambiguous_case {
template <class T>
struct Foo {};
template <class T, class U> bool operator==(Foo<U>, Foo<T*>); // expected-note{{candidate}}
template <class T, class U> bool operator==(Foo<T*>, Foo<U>); // expected-note{{candidate}}

void test() {
    Foo<int*>() == Foo<int*>(); // expected-error{{ambiguous}}
}
} // namespace ambiguous_case
} // namespace
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 'ns::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 {
  // 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