1 // RUN: %clang_cc1 -std=c++2a -verify %s 2 // RUN: %clang_cc1 -std=c++2a -verify %s -DDEFINE_FIRST 3 4 // As modified by P2002R0: 5 // The exception specification for a comparison operator function (12.6.2) 6 // without a noexcept-specifier that is defaulted on its first declaration is 7 // potentially-throwing if and only if any expression in the implicit 8 // definition is potentially-throwing. 9 10 #define CAT2(a, b) a ## b 11 #define CAT(a, b) CAT2(a, b) 12 13 #ifdef DEFINE_FIRST 14 #define DEF(x) auto CAT(a, __LINE__) = x 15 #else 16 #define DEF(x) 17 #endif 18 19 namespace std { 20 struct strong_ordering { 21 int n; 22 static const strong_ordering equal, less, greater; 23 }; 24 constexpr strong_ordering strong_ordering::equal{0}, 25 strong_ordering::less{-1}, strong_ordering::greater{1}; 26 bool operator!=(std::strong_ordering o, int n) noexcept; 27 } 28 29 namespace Eq { 30 struct A { 31 bool operator==(const A&) const = default; 32 }; 33 DEF(A() == A()); 34 static_assert(noexcept(A() == A())); 35 36 struct B { 37 bool operator==(const B&) const; 38 }; 39 struct C { 40 B b; 41 bool operator==(const C&) const = default; 42 }; 43 DEF(C() == C()); 44 static_assert(!noexcept(C() == C())); 45 46 // Ensure we do not trigger odr-use from exception specification computation. 47 template<typename T> struct D { operator ==Eq::D48 bool operator==(const D &) const { 49 typename T::error error; // expected-error {{no type}} 50 } 51 }; 52 struct E { 53 D<E> d; 54 bool operator==(const E&) const = default; 55 }; 56 static_assert(!noexcept(E() == E())); 57 58 // (but we do when defining the function). 59 struct F { 60 D<F> d; 61 bool operator==(const F&) const = default; // expected-note {{in instantiation}} 62 }; 63 bool equal = F() == F(); 64 static_assert(!noexcept(F() == F())); 65 } 66 67 namespace Spaceship { 68 struct X { 69 friend std::strong_ordering operator<=>(X, X); 70 }; 71 struct Y : X { 72 friend std::strong_ordering operator<=>(Y, Y) = default; 73 }; 74 DEF(Y() <=> Y()); 75 static_assert(!noexcept(Y() <=> Y())); 76 77 struct ThrowingCmpCat { 78 ThrowingCmpCat(std::strong_ordering); 79 operator std::strong_ordering(); 80 }; 81 bool operator!=(ThrowingCmpCat o, int n) noexcept; 82 83 struct A { 84 friend ThrowingCmpCat operator<=>(A, A) noexcept; 85 }; 86 87 struct B { 88 A a; 89 std::strong_ordering operator<=>(const B&) const = default; 90 }; 91 DEF(B() <=> B()); 92 static_assert(!noexcept(B() <=> B())); 93 94 struct C { 95 int n; 96 ThrowingCmpCat operator<=>(const C&) const = default; 97 }; 98 DEF(C() <=> C()); 99 static_assert(!noexcept(C() <=> C())); 100 101 struct D { 102 int n; 103 std::strong_ordering operator<=>(const D&) const = default; 104 }; 105 DEF(D() <=> D()); 106 static_assert(noexcept(D() <=> D())); 107 108 109 struct ThrowingCmpCat2 { 110 ThrowingCmpCat2(std::strong_ordering) noexcept; 111 operator std::strong_ordering() noexcept; 112 }; 113 bool operator!=(ThrowingCmpCat2 o, int n); 114 115 struct E { 116 friend ThrowingCmpCat2 operator<=>(E, E) noexcept; 117 }; 118 119 struct F { 120 E e; 121 std::strong_ordering operator<=>(const F&) const = default; 122 }; 123 DEF(F() <=> F()); 124 static_assert(noexcept(F() <=> F())); 125 126 struct G { 127 int n; 128 ThrowingCmpCat2 operator<=>(const G&) const = default; 129 }; 130 DEF(G() <=> G()); 131 static_assert(!noexcept(G() <=> G())); 132 } 133 134 namespace Synth { 135 struct A { 136 friend bool operator==(A, A) noexcept; 137 friend bool operator<(A, A) noexcept; 138 }; 139 struct B { 140 A a; 141 friend std::strong_ordering operator<=>(B, B) = default; 142 }; 143 std::strong_ordering operator<=>(B, B) noexcept; 144 145 struct C { 146 friend bool operator==(C, C); 147 friend bool operator<(C, C) noexcept; 148 }; 149 struct D { 150 C c; 151 friend std::strong_ordering operator<=>(D, D) = default; // expected-note {{previous}} 152 }; 153 std::strong_ordering operator<=>(D, D) noexcept; // expected-error {{does not match}} 154 155 struct E { 156 friend bool operator==(E, E) noexcept; 157 friend bool operator<(E, E); 158 }; 159 struct F { 160 E e; 161 friend std::strong_ordering operator<=>(F, F) = default; // expected-note {{previous}} 162 }; 163 std::strong_ordering operator<=>(F, F) noexcept; // expected-error {{does not match}} 164 } 165 166 namespace Secondary { 167 struct A { 168 friend bool operator==(A, A); 169 friend bool operator!=(A, A) = default; // expected-note {{previous}} 170 171 friend int operator<=>(A, A); 172 friend bool operator<(A, A) = default; // expected-note {{previous}} 173 friend bool operator<=(A, A) = default; // expected-note {{previous}} 174 friend bool operator>(A, A) = default; // expected-note {{previous}} 175 friend bool operator>=(A, A) = default; // expected-note {{previous}} 176 }; 177 bool operator!=(A, A) noexcept; // expected-error {{does not match}} 178 bool operator<(A, A) noexcept; // expected-error {{does not match}} 179 bool operator<=(A, A) noexcept; // expected-error {{does not match}} 180 bool operator>(A, A) noexcept; // expected-error {{does not match}} 181 bool operator>=(A, A) noexcept; // expected-error {{does not match}} 182 183 struct B { 184 friend bool operator==(B, B) noexcept; 185 friend bool operator!=(B, B) = default; 186 187 friend int operator<=>(B, B) noexcept; 188 friend bool operator<(B, B) = default; 189 friend bool operator<=(B, B) = default; 190 friend bool operator>(B, B) = default; 191 friend bool operator>=(B, B) = default; 192 }; 193 bool operator!=(B, B) noexcept; 194 bool operator<(B, B) noexcept; 195 bool operator<=(B, B) noexcept; 196 bool operator>(B, B) noexcept; 197 bool operator>=(B, B) noexcept; 198 } 199 200 // Check that we attempt to define a defaulted comparison before trying to 201 // compute its exception specification. 202 namespace DefineBeforeComputingExceptionSpec { 203 template<int> struct A { 204 A(); 205 A(const A&) = delete; // expected-note 3{{here}} 206 friend bool operator==(A, A); // expected-note 3{{passing}} 207 friend bool operator!=(const A&, const A&) = default; // expected-error 3{{call to deleted constructor}} 208 }; 209 210 bool a0 = A<0>() != A<0>(); // expected-note {{in defaulted equality comparison operator}} 211 bool a1 = operator!=(A<1>(), A<1>()); // expected-note {{in defaulted equality comparison operator}} 212 213 template struct A<2>; 214 bool operator!=(const A<2>&, const A<2>&) noexcept; // expected-note {{in evaluation of exception specification}} 215 216 template<int> struct B { 217 B(); 218 B(const B&) = delete; // expected-note 3{{here}} 219 friend bool operator==(B, B); // expected-note 3{{passing}} 220 bool operator!=(const B&) const = default; // expected-error 3{{call to deleted constructor}} 221 }; 222 223 bool b0 = B<0>() != B<0>(); // expected-note {{in defaulted equality comparison operator}} 224 bool b1 = B<1>().operator!=(B<1>()); // expected-note {{in defaulted equality comparison operator}} 225 int b2 = sizeof(&B<2>::operator!=); // expected-note {{in evaluation of exception specification}} 226 } 227