// 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 struct D { bool operator==(const D &) const { typename T::error error; // expected-error {{no type}} } }; struct E { D d; bool operator==(const E&) const = default; }; static_assert(!noexcept(E() == E())); // (but we do when defining the function). struct F { D 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 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 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}} }