1 //===----------------------------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 // UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 10 11 // constexpr void swap(expected& rhs) noexcept(see below); 12 // 13 // Constraints: 14 // is_swappable_v<T> is true and 15 // is_swappable_v<E> is true and 16 // is_move_constructible_v<T> && is_move_constructible_v<E> is true, and 17 // is_nothrow_move_constructible_v<T> || is_nothrow_move_constructible_v<E> is true. 18 // 19 // Throws: Any exception thrown by the expressions in the Effects. 20 // Remarks: The exception specification is equivalent to: 21 // is_nothrow_move_constructible_v<T> && is_nothrow_swappable_v<T> && 22 // is_nothrow_move_constructible_v<E> && is_nothrow_swappable_v<E> 23 24 #include <cassert> 25 #include <expected> 26 #include <type_traits> 27 #include <utility> 28 29 #include "../../types.h" 30 #include "test_macros.h" 31 32 // Test Constraints: 33 template <class T, class E> 34 concept HasMemberSwap = requires(std::expected<T, E> x, std::expected<T, E> y) { x.swap(y); }; 35 36 static_assert(HasMemberSwap<int, int>); 37 38 struct NotSwappable {}; 39 void swap(NotSwappable&, NotSwappable&) = delete; 40 41 // !is_swappable_v<T> 42 static_assert(!HasMemberSwap<NotSwappable, int>); 43 44 // !is_swappable_v<E> 45 static_assert(!HasMemberSwap<int, NotSwappable>); 46 47 struct NotMoveConstructible { 48 NotMoveConstructible(NotMoveConstructible&&) = delete; 49 friend void swap(NotMoveConstructible&, NotMoveConstructible&) {} 50 }; 51 52 // !is_move_constructible_v<T> 53 static_assert(!HasMemberSwap<NotMoveConstructible, int>); 54 55 // !is_move_constructible_v<E> 56 static_assert(!HasMemberSwap<int, NotMoveConstructible>); 57 58 struct MoveMayThrow { 59 MoveMayThrow(MoveMayThrow&&) noexcept(false); 60 friend void swap(MoveMayThrow&, MoveMayThrow&) noexcept {} 61 }; 62 63 // !is_nothrow_move_constructible_v<T> && is_nothrow_move_constructible_v<E> 64 static_assert(HasMemberSwap<MoveMayThrow, int>); 65 66 // is_nothrow_move_constructible_v<T> && !is_nothrow_move_constructible_v<E> 67 static_assert(HasMemberSwap<int, MoveMayThrow>); 68 69 // !is_nothrow_move_constructible_v<T> && !is_nothrow_move_constructible_v<E> 70 static_assert(!HasMemberSwap<MoveMayThrow, MoveMayThrow>); 71 72 // Test noexcept 73 template <class T, class E> 74 concept MemberSwapNoexcept = // 75 requires(std::expected<T, E> x, std::expected<T, E> y) { 76 { x.swap(y) } noexcept; 77 }; 78 79 static_assert(MemberSwapNoexcept<int, int>); 80 81 // !is_nothrow_move_constructible_v<T> 82 static_assert(!MemberSwapNoexcept<MoveMayThrow, int>); 83 84 // !is_nothrow_move_constructible_v<E> 85 static_assert(!MemberSwapNoexcept<int, MoveMayThrow>); 86 87 struct SwapMayThrow { 88 friend void swap(SwapMayThrow&, SwapMayThrow&) noexcept(false) {} 89 }; 90 91 // !is_nothrow_swappable_v<T> 92 static_assert(!MemberSwapNoexcept<SwapMayThrow, int>); 93 94 // !is_nothrow_swappable_v<E> 95 static_assert(!MemberSwapNoexcept<int, SwapMayThrow>); 96 97 constexpr bool test() { 98 // this->has_value() && rhs.has_value() 99 { 100 std::expected<ADLSwap, int> x(std::in_place, 5); 101 std::expected<ADLSwap, int> y(std::in_place, 10); 102 x.swap(y); 103 104 assert(x.has_value()); 105 assert(x->i == 10); 106 assert(x->adlSwapCalled); 107 assert(y.has_value()); 108 assert(y->i == 5); 109 assert(y->adlSwapCalled); 110 } 111 112 // !this->has_value() && !rhs.has_value() 113 { 114 std::expected<int, ADLSwap> x(std::unexpect, 5); 115 std::expected<int, ADLSwap> y(std::unexpect, 10); 116 x.swap(y); 117 118 assert(!x.has_value()); 119 assert(x.error().i == 10); 120 assert(x.error().adlSwapCalled); 121 assert(!y.has_value()); 122 assert(y.error().i == 5); 123 assert(y.error().adlSwapCalled); 124 } 125 126 // this->has_value() && !rhs.has_value() 127 // && is_nothrow_move_constructible_v<E> 128 { 129 std::expected<TrackedMove<true>, TrackedMove<true>> e1(std::in_place, 5); 130 std::expected<TrackedMove<true>, TrackedMove<true>> e2(std::unexpect, 10); 131 132 e1.swap(e2); 133 134 assert(!e1.has_value()); 135 assert(e1.error().i == 10); 136 assert(e2.has_value()); 137 assert(e2->i == 5); 138 139 assert(e1.error().numberOfMoves == 2); 140 assert(!e1.error().swapCalled); 141 assert(e2->numberOfMoves == 1); 142 assert(!e2->swapCalled); 143 } 144 145 // this->has_value() && !rhs.has_value() 146 // && !is_nothrow_move_constructible_v<E> 147 { 148 std::expected<TrackedMove<true>, TrackedMove<false>> e1(std::in_place, 5); 149 std::expected<TrackedMove<true>, TrackedMove<false>> e2(std::unexpect, 10); 150 151 e1.swap(e2); 152 153 assert(!e1.has_value()); 154 assert(e1.error().i == 10); 155 assert(e2.has_value()); 156 assert(e2->i == 5); 157 158 assert(e1.error().numberOfMoves == 1); 159 assert(!e1.error().swapCalled); 160 assert(e2->numberOfMoves == 2); 161 assert(!e2->swapCalled); 162 } 163 164 // !this->has_value() && rhs.has_value() 165 // && is_nothrow_move_constructible_v<E> 166 { 167 std::expected<TrackedMove<true>, TrackedMove<true>> e1(std::unexpect, 10); 168 std::expected<TrackedMove<true>, TrackedMove<true>> e2(std::in_place, 5); 169 170 e1.swap(e2); 171 172 assert(e1.has_value()); 173 assert(e1->i == 5); 174 assert(!e2.has_value()); 175 assert(e2.error().i == 10); 176 177 assert(e1->numberOfMoves == 1); 178 assert(!e1->swapCalled); 179 assert(e2.error().numberOfMoves == 2); 180 assert(!e2.error().swapCalled); 181 } 182 183 // !this->has_value() && rhs.has_value() 184 // && !is_nothrow_move_constructible_v<E> 185 { 186 std::expected<TrackedMove<true>, TrackedMove<false>> e1(std::unexpect, 10); 187 std::expected<TrackedMove<true>, TrackedMove<false>> e2(std::in_place, 5); 188 189 e1.swap(e2); 190 191 assert(e1.has_value()); 192 assert(e1->i == 5); 193 assert(!e2.has_value()); 194 assert(e2.error().i == 10); 195 196 assert(e1->numberOfMoves == 2); 197 assert(!e1->swapCalled); 198 assert(e2.error().numberOfMoves == 1); 199 assert(!e2.error().swapCalled); 200 } 201 202 // TailClobberer 203 { 204 // is_nothrow_move_constructible_v<E> 205 { 206 std::expected<TailClobbererNonTrivialMove<0, true>, TailClobbererNonTrivialMove<1, true>> x(std::in_place); 207 std::expected<TailClobbererNonTrivialMove<0, true>, TailClobbererNonTrivialMove<1, true>> y(std::unexpect); 208 209 x.swap(y); 210 211 // Both of these would fail if adjusting the "has value" flags happened 212 // _before_ constructing the member objects inside the `swap`. 213 assert(!x.has_value()); 214 assert(y.has_value()); 215 } 216 217 // !is_nothrow_move_constructible_v<E> 218 { 219 std::expected<TailClobbererNonTrivialMove<0, true>, TailClobbererNonTrivialMove<1, false>> x(std::in_place); 220 std::expected<TailClobbererNonTrivialMove<0, true>, TailClobbererNonTrivialMove<1, false>> y(std::unexpect); 221 222 x.swap(y); 223 224 // Both of these would fail if adjusting the "has value" flags happened 225 // _before_ constructing the member objects inside the `swap`. 226 assert(!x.has_value()); 227 assert(y.has_value()); 228 } 229 } 230 231 // CheckForInvalidWrites 232 { 233 { 234 CheckForInvalidWrites<true> x(std::unexpect); 235 CheckForInvalidWrites<true> y; 236 237 x.swap(y); 238 239 assert(x.check()); 240 assert(y.check()); 241 } 242 { 243 CheckForInvalidWrites<false> x(std::unexpect); 244 CheckForInvalidWrites<false> y; 245 246 x.swap(y); 247 248 assert(x.check()); 249 assert(y.check()); 250 } 251 } 252 253 return true; 254 } 255 256 void testException() { 257 #ifndef TEST_HAS_NO_EXCEPTIONS 258 // !e1.has_value() && e2.has_value() 259 { 260 std::expected<ThrowOnMoveConstruct, int> e1(std::unexpect, 5); 261 std::expected<ThrowOnMoveConstruct, int> e2(std::in_place); 262 try { 263 e1.swap(e2); 264 assert(false); 265 } catch (Except) { 266 assert(!e1.has_value()); 267 assert(e1.error() == 5); 268 } 269 } 270 271 // e1.has_value() && !e2.has_value() 272 { 273 std::expected<int, ThrowOnMoveConstruct> e1(5); 274 std::expected<int, ThrowOnMoveConstruct> e2(std::unexpect); 275 try { 276 e1.swap(e2); 277 assert(false); 278 } catch (Except) { 279 assert(e1.has_value()); 280 assert(*e1 == 5); 281 } 282 } 283 284 // TailClobberer 285 { 286 // is_nothrow_move_constructible_v<E> 287 { 288 std::expected<TailClobbererNonTrivialMove<0, false, true>, TailClobbererNonTrivialMove<1>> x(std::in_place); 289 std::expected<TailClobbererNonTrivialMove<0, false, true>, TailClobbererNonTrivialMove<1>> y(std::unexpect); 290 try { 291 x.swap(y); 292 assert(false); 293 } catch (Except) { 294 assert(x.has_value()); 295 // This would fail if `TailClobbererNonTrivialMove<1>` clobbered the 296 // flag when rolling back the swap. 297 assert(!y.has_value()); 298 } 299 } 300 301 // !is_nothrow_move_constructible_v<E> 302 { 303 std::expected<TailClobbererNonTrivialMove<0>, TailClobbererNonTrivialMove<1, false, true>> x(std::in_place); 304 std::expected<TailClobbererNonTrivialMove<0>, TailClobbererNonTrivialMove<1, false, true>> y(std::unexpect); 305 try { 306 x.swap(y); 307 assert(false); 308 } catch (Except) { 309 // This would fail if `TailClobbererNonTrivialMove<0>` clobbered the 310 // flag when rolling back the swap. 311 assert(x.has_value()); 312 assert(!y.has_value()); 313 } 314 } 315 } 316 #endif // TEST_HAS_NO_EXCEPTIONS 317 } 318 319 int main(int, char**) { 320 test(); 321 static_assert(test()); 322 testException(); 323 return 0; 324 } 325