Compiler projects using llvm
// RUN: %clang_cc1 -std=c++2a -verify %s

namespace std {
  class strong_ordering {
    int n;
    constexpr strong_ordering(int n) : n(n) {}
  public:
    static const strong_ordering less, equal, greater;
    bool operator!=(int) { return n != 0; }
  };
  constexpr strong_ordering strong_ordering::less{-1},
      strong_ordering::equal{0}, strong_ordering::greater{1};

  class weak_ordering {
    int n;
    constexpr weak_ordering(int n) : n(n) {}
  public:
    constexpr weak_ordering(strong_ordering o);
    static const weak_ordering less, equivalent, greater;
    bool operator!=(int) { return n != 0; }
  };
  constexpr weak_ordering weak_ordering::less{-1},
      weak_ordering::equivalent{0}, weak_ordering::greater{1};

  class partial_ordering {
    int n;
    constexpr partial_ordering(int n) : n(n) {}
  public:
    constexpr partial_ordering(strong_ordering o);
    constexpr partial_ordering(weak_ordering o);
    static const partial_ordering less, equivalent, greater, unordered;
    bool operator!=(int) { return n != 0; }
  };
  constexpr partial_ordering partial_ordering::less{-1},
      partial_ordering::equivalent{0}, partial_ordering::greater{1},
      partial_ordering::unordered{2};
}

namespace DeducedNotCat {
  struct A {
    A operator<=>(const A&) const; // expected-note {{selected 'operator<=>' for member 'a' declared here}}
  };
  struct B {
    A a; // expected-note {{return type 'DeducedNotCat::A' of three-way comparison for member 'a' is not a standard comparison category type}}
    auto operator<=>(const B&) const = default; // expected-warning {{implicitly deleted}}
  };
}

namespace DeducedVsSynthesized {
  struct A {
    bool operator==(const A&) const;
    bool operator<(const A&) const;
  };
  struct B {
    A a; // expected-note {{no viable three-way comparison function for member 'a'}}
    auto operator<=>(const B&) const = default; // expected-warning {{implicitly deleted}}
  };
}

namespace Deduction {
  template<typename T> struct wrap {
    T t;
    friend auto operator<=>(const wrap&, const wrap&) = default;
  };

  using strong = wrap<int>;
  using strong2 = wrap<int*>;
  struct weak {
    friend std::weak_ordering operator<=>(weak, weak);
  };
  using partial = wrap<float>;

  template<typename ...T> struct A : T... {
    friend auto operator<=>(const A&, const A&) = default;
  };

  template<typename Expected, typename ...Ts> void f() {
    using T = Expected; // expected-note {{previous}}
    using T = decltype(A<Ts...>() <=> A<Ts...>()); // expected-error {{different type}}
    void(A<Ts...>() <=> A<Ts...>()); // trigger synthesis of body
  }

  template void f<std::strong_ordering>();
  template void f<std::strong_ordering, strong>();
  template void f<std::strong_ordering, strong, strong2>();

  template void f<std::weak_ordering, weak>();
  template void f<std::weak_ordering, weak, strong>();
  template void f<std::weak_ordering, strong, weak>();

  template void f<std::partial_ordering, partial>();
  template void f<std::partial_ordering, weak, partial>();
  template void f<std::partial_ordering, strong, partial>();
  template void f<std::partial_ordering, partial, weak>();
  template void f<std::partial_ordering, partial, strong>();
  template void f<std::partial_ordering, weak, partial, strong>();

  // Check that the above mechanism works.
  template void f<std::strong_ordering, weak>(); // expected-note {{instantiation of}}

  std::strong_ordering x = A<strong>() <=> A<strong>();
}

namespace PR44723 {
  // Make sure we trigger return type deduction for a callee 'operator<=>'
  // before inspecting its return type.
  template<int> struct a {
    friend constexpr auto operator<=>(a const &lhs, a const &rhs) {
      return std::strong_ordering::equal;
    }
  };
  struct b {
    friend constexpr auto operator<=>(b const &, b const &) = default;
    a<0> m_value;
  };
  std::strong_ordering cmp_b = b() <=> b();

  struct c {
    auto operator<=>(const c&) const&; // expected-note {{selected 'operator<=>' for base class 'c' declared here}}
  };
  struct d : c { // expected-note {{base class 'c' declared here}}
    friend auto operator<=>(const d&, const d&) = default; // #d
    // expected-error@#d {{return type of defaulted 'operator<=>' cannot be deduced because three-way comparison for base class 'c' has a deduced return type and is not yet defined}}
    // expected-warning@#d {{implicitly deleted}}
  };
  auto c::operator<=>(const c&) const& { // #c
    return std::strong_ordering::equal;
  }
  // expected-error@+1 {{overload resolution selected deleted operator '<=>'}}
  std::strong_ordering cmp_d = d() <=> d();
  // expected-note@#c 2{{candidate}}
  // expected-note@#d {{candidate function has been implicitly deleted}}
}

namespace BadDeducedType {
  struct A {
    // expected-error@+1 {{deduced return type for defaulted three-way comparison operator must be 'auto', not 'auto &'}}
    friend auto &operator<=>(const A&, const A&) = default;
  };

  struct B {
    // expected-error@+1 {{deduced return type for defaulted three-way comparison operator must be 'auto', not 'const auto'}}
    friend const auto operator<=>(const B&, const B&) = default;
  };

  template<typename T> struct X {}; // expected-note {{here}}
  struct C {
    // expected-error@+1 {{deduction not allowed in function return type}}
    friend X operator<=>(const C&, const C&) = default;
  };

  template<typename T> concept CmpCat = true;
  struct D {
    // expected-error@+1 {{deduced return type for defaulted three-way comparison operator must be 'auto', not 'CmpCat auto'}}
    friend CmpCat auto operator<=>(const D&, const D&) = default;
  };
}

namespace PR48856 {
  struct A {
    auto operator<=>(const A &) const = default; // expected-warning {{implicitly deleted}}
    void (*x)();                                 // expected-note {{because there is no viable three-way comparison function for member 'x'}}
  };

  struct B {
    auto operator<=>(const B &) const = default; // expected-warning {{implicitly deleted}}
    void (B::*x)();                              // expected-note {{because there is no viable three-way comparison function for member 'x'}}
  };

  struct C {
    auto operator<=>(const C &) const = default; // expected-warning {{implicitly deleted}}
    int C::*x;                                   // expected-note {{because there is no viable three-way comparison function for member 'x'}}
  };
}

namespace PR50591 {
  struct a1 {
    operator int() const;
  };
  struct b1 {
    auto operator<=>(b1 const &) const = default;
    a1 f;
  };
  std::strong_ordering cmp_b1 = b1() <=> b1();

  struct a2 {
    operator float() const;
  };
  struct b2 {
    auto operator<=>(b2 const &) const = default;
    a2 f;
  };
  std::partial_ordering cmp_b2 = b2() <=> b2();

  using fp = void (*)();

  struct a3 {
    operator fp() const;
  };
  struct b3 {
    auto operator<=>(b3 const &) const = default; // expected-warning {{implicitly deleted}}
    a3 f;                                         // expected-note {{because there is no viable three-way comparison function}}
  };

  struct a4 { // Test that function pointer conversion operator here is ignored for this overload resolution.
    operator int() const;
    operator fp() const;
  };
  struct b4 {
    auto operator<=>(b4 const &) const = default;
    a4 f;
  };
  std::strong_ordering cmp_b4 = b4() <=> b4();
}