| // RUN: %clang_cc1 -std=c++2a -verify %s |
| // RUN: %clang_cc1 -std=c++2a -verify %s -DDEFINE_FIRST |
| |
| // As modified by P2002R0: |
| // The exception specification for a comparison operator function (12.6.2) |
| // without a noexcept-specifier that is defaulted on its first declaration is |
| // potentially-throwing if and only if any expression in the implicit |
| // definition is potentially-throwing. |
| |
| #define CAT2(a, b) a ## b |
| #define CAT(a, b) CAT2(a, b) |
| |
| #ifdef DEFINE_FIRST |
| #define DEF(x) auto CAT(a, __LINE__) = x |
| #else |
| #define DEF(x) |
| #endif |
| |
| namespace std { |
| struct strong_ordering { |
| int n; |
| static const strong_ordering equal, less, greater; |
| }; |
| constexpr strong_ordering strong_ordering::equal{0}, |
| strong_ordering::less{-1}, strong_ordering::greater{1}; |
| bool operator!=(std::strong_ordering o, int n) noexcept; |
| } |
| |
| namespace Eq { |
| struct A { |
| bool operator==(const A&) const = default; |
| }; |
| DEF(A() == A()); |
| static_assert(noexcept(A() == A())); |
| |
| struct B { |
| bool operator==(const B&) const; |
| }; |
| struct C { |
| B b; |
| bool operator==(const C&) const = default; |
| }; |
| DEF(C() == C()); |
| static_assert(!noexcept(C() == C())); |
| |
| // Ensure we do not trigger odr-use from exception specification computation. |
| template<typename T> struct D { |
| bool operator==(const D &) const { |
| typename T::error error; // expected-error {{no type}} |
| } |
| }; |
| struct E { |
| D<E> d; |
| bool operator==(const E&) const = default; |
| }; |
| static_assert(!noexcept(E() == E())); |
| |
| // (but we do when defining the function). |
| struct F { |
| D<F> d; |
| bool operator==(const F&) const = default; // expected-note {{in instantiation}} |
| }; |
| bool equal = F() == F(); |
| static_assert(!noexcept(F() == F())); |
| } |
| |
| namespace Spaceship { |
| struct X { |
| friend std::strong_ordering operator<=>(X, X); |
| }; |
| struct Y : X { |
| friend std::strong_ordering operator<=>(Y, Y) = default; |
| }; |
| DEF(Y() <=> Y()); |
| static_assert(!noexcept(Y() <=> Y())); |
| |
| struct ThrowingCmpCat { |
| ThrowingCmpCat(std::strong_ordering); |
| operator std::strong_ordering(); |
| }; |
| bool operator!=(ThrowingCmpCat o, int n) noexcept; |
| |
| struct A { |
| friend ThrowingCmpCat operator<=>(A, A) noexcept; |
| }; |
| |
| struct B { |
| A a; |
| std::strong_ordering operator<=>(const B&) const = default; |
| }; |
| DEF(B() <=> B()); |
| static_assert(!noexcept(B() <=> B())); |
| |
| struct C { |
| int n; |
| ThrowingCmpCat operator<=>(const C&) const = default; |
| }; |
| DEF(C() <=> C()); |
| static_assert(!noexcept(C() <=> C())); |
| |
| struct D { |
| int n; |
| std::strong_ordering operator<=>(const D&) const = default; |
| }; |
| DEF(D() <=> D()); |
| static_assert(noexcept(D() <=> D())); |
| |
| |
| struct ThrowingCmpCat2 { |
| ThrowingCmpCat2(std::strong_ordering) noexcept; |
| operator std::strong_ordering() noexcept; |
| }; |
| bool operator!=(ThrowingCmpCat2 o, int n); |
| |
| struct E { |
| friend ThrowingCmpCat2 operator<=>(E, E) noexcept; |
| }; |
| |
| struct F { |
| E e; |
| std::strong_ordering operator<=>(const F&) const = default; |
| }; |
| DEF(F() <=> F()); |
| static_assert(noexcept(F() <=> F())); |
| |
| struct G { |
| int n; |
| ThrowingCmpCat2 operator<=>(const G&) const = default; |
| }; |
| DEF(G() <=> G()); |
| static_assert(!noexcept(G() <=> G())); |
| } |
| |
| namespace Synth { |
| struct A { |
| friend bool operator==(A, A) noexcept; |
| friend bool operator<(A, A) noexcept; |
| }; |
| struct B { |
| A a; |
| friend std::strong_ordering operator<=>(B, B) = default; |
| }; |
| std::strong_ordering operator<=>(B, B) noexcept; |
| |
| struct C { |
| friend bool operator==(C, C); |
| friend bool operator<(C, C) noexcept; |
| }; |
| struct D { |
| C c; |
| friend std::strong_ordering operator<=>(D, D) = default; // expected-note {{previous}} |
| }; |
| std::strong_ordering operator<=>(D, D) noexcept; // expected-error {{does not match}} |
| |
| struct E { |
| friend bool operator==(E, E) noexcept; |
| friend bool operator<(E, E); |
| }; |
| struct F { |
| E e; |
| friend std::strong_ordering operator<=>(F, F) = default; // expected-note {{previous}} |
| }; |
| std::strong_ordering operator<=>(F, F) noexcept; // expected-error {{does not match}} |
| } |
| |
| namespace Secondary { |
| struct A { |
| friend bool operator==(A, A); |
| friend bool operator!=(A, A) = default; // expected-note {{previous}} |
| |
| friend int operator<=>(A, A); |
| friend bool operator<(A, A) = default; // expected-note {{previous}} |
| friend bool operator<=(A, A) = default; // expected-note {{previous}} |
| friend bool operator>(A, A) = default; // expected-note {{previous}} |
| friend bool operator>=(A, A) = default; // expected-note {{previous}} |
| }; |
| bool operator!=(A, A) noexcept; // expected-error {{does not match}} |
| bool operator<(A, A) noexcept; // expected-error {{does not match}} |
| bool operator<=(A, A) noexcept; // expected-error {{does not match}} |
| bool operator>(A, A) noexcept; // expected-error {{does not match}} |
| bool operator>=(A, A) noexcept; // expected-error {{does not match}} |
| |
| struct B { |
| friend bool operator==(B, B) noexcept; |
| friend bool operator!=(B, B) = default; |
| |
| friend int operator<=>(B, B) noexcept; |
| friend bool operator<(B, B) = default; |
| friend bool operator<=(B, B) = default; |
| friend bool operator>(B, B) = default; |
| friend bool operator>=(B, B) = default; |
| }; |
| bool operator!=(B, B) noexcept; |
| bool operator<(B, B) noexcept; |
| bool operator<=(B, B) noexcept; |
| bool operator>(B, B) noexcept; |
| bool operator>=(B, B) noexcept; |
| } |
| |
| // Check that we attempt to define a defaulted comparison before trying to |
| // compute its exception specification. |
| namespace DefineBeforeComputingExceptionSpec { |
| template<int> struct A { |
| A(); |
| A(const A&) = delete; // expected-note 3{{here}} |
| friend bool operator==(A, A); // expected-note 3{{passing}} |
| friend bool operator!=(const A&, const A&) = default; // expected-error 3{{call to deleted constructor}} |
| }; |
| |
| bool a0 = A<0>() != A<0>(); // expected-note {{in defaulted equality comparison operator}} |
| bool a1 = operator!=(A<1>(), A<1>()); // expected-note {{in defaulted equality comparison operator}} |
| |
| template struct A<2>; |
| bool operator!=(const A<2>&, const A<2>&) noexcept; // expected-note {{in evaluation of exception specification}} |
| |
| template<int> struct B { |
| B(); |
| B(const B&) = delete; // expected-note 3{{here}} |
| friend bool operator==(B, B); // expected-note 3{{passing}} |
| bool operator!=(const B&) const = default; // expected-error 3{{call to deleted constructor}} |
| }; |
| |
| bool b0 = B<0>() != B<0>(); // expected-note {{in defaulted equality comparison operator}} |
| bool b1 = B<1>().operator!=(B<1>()); // expected-note {{in defaulted equality comparison operator}} |
| int b2 = sizeof(&B<2>::operator!=); // expected-note {{in evaluation of exception specification}} |
| } |