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 // friend constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))); 12 13 #include <cassert> 14 #include <expected> 15 #include <type_traits> 16 #include <utility> 17 18 #include "../../types.h" 19 #include "test_macros.h" 20 21 // Test Constraints: 22 struct NotSwappable { 23 NotSwappable operator=(const NotSwappable&) = delete; 24 }; 25 void swap(NotSwappable&, NotSwappable&) = delete; 26 27 static_assert(std::is_swappable_v<std::expected<int, int>>); 28 29 // !is_swappable_v<T> 30 static_assert(!std::is_swappable_v<std::expected<NotSwappable, int>>); 31 32 // !is_swappable_v<E> 33 static_assert(!std::is_swappable_v<std::expected<int, NotSwappable>>); 34 35 struct NotMoveConstructible { 36 NotMoveConstructible(NotMoveConstructible&&) = delete; 37 friend void swap(NotMoveConstructible&, NotMoveConstructible&) {} 38 }; 39 40 // !is_move_constructible_v<T> 41 static_assert(!std::is_swappable_v<std::expected<NotMoveConstructible, int>>); 42 43 // !is_move_constructible_v<E> 44 static_assert(!std::is_swappable_v<std::expected<int, NotMoveConstructible>>); 45 46 struct MoveMayThrow { 47 MoveMayThrow(MoveMayThrow&&) noexcept(false); 48 friend void swap(MoveMayThrow&, MoveMayThrow&) noexcept {} 49 }; 50 51 // !is_nothrow_move_constructible_v<T> && is_nothrow_move_constructible_v<E> 52 static_assert(std::is_swappable_v<std::expected<MoveMayThrow, int>>); 53 54 // is_nothrow_move_constructible_v<T> && !is_nothrow_move_constructible_v<E> 55 static_assert(std::is_swappable_v<std::expected<int, MoveMayThrow>>); 56 57 // !is_nothrow_move_constructible_v<T> && !is_nothrow_move_constructible_v<E> 58 static_assert(!std::is_swappable_v<std::expected<MoveMayThrow, MoveMayThrow>>); 59 60 // Test noexcept 61 static_assert(std::is_nothrow_swappable_v<std::expected<int, int>>); 62 63 // !is_nothrow_move_constructible_v<T> 64 static_assert(!std::is_nothrow_swappable_v<std::expected<MoveMayThrow, int>>); 65 66 // !is_nothrow_move_constructible_v<E> 67 static_assert(!std::is_nothrow_swappable_v<std::expected<int, MoveMayThrow>>); 68 69 struct SwapMayThrow { 70 friend void swap(SwapMayThrow&, SwapMayThrow&) noexcept(false) {} 71 }; 72 73 // !is_nothrow_swappable_v<T> 74 static_assert(!std::is_nothrow_swappable_v<std::expected<SwapMayThrow, int>>); 75 76 // !is_nothrow_swappable_v<E> 77 static_assert(!std::is_nothrow_swappable_v<std::expected<int, SwapMayThrow>>); 78 79 constexpr bool test() { 80 // this->has_value() && rhs.has_value() 81 { 82 std::expected<ADLSwap, int> x(std::in_place, 5); 83 std::expected<ADLSwap, int> y(std::in_place, 10); 84 swap(x, y); 85 86 assert(x.has_value()); 87 assert(x->i == 10); 88 assert(x->adlSwapCalled); 89 assert(y.has_value()); 90 assert(y->i == 5); 91 assert(y->adlSwapCalled); 92 } 93 94 // !this->has_value() && !rhs.has_value() 95 { 96 std::expected<int, ADLSwap> x(std::unexpect, 5); 97 std::expected<int, ADLSwap> y(std::unexpect, 10); 98 swap(x, y); 99 100 assert(!x.has_value()); 101 assert(x.error().i == 10); 102 assert(x.error().adlSwapCalled); 103 assert(!y.has_value()); 104 assert(y.error().i == 5); 105 assert(y.error().adlSwapCalled); 106 } 107 108 // this->has_value() && !rhs.has_value() 109 // && is_nothrow_move_constructible_v<E> 110 { 111 std::expected<TrackedMove<true>, TrackedMove<true>> e1(std::in_place, 5); 112 std::expected<TrackedMove<true>, TrackedMove<true>> e2(std::unexpect, 10); 113 114 swap(e1, e2); 115 116 assert(!e1.has_value()); 117 assert(e1.error().i == 10); 118 assert(e2.has_value()); 119 assert(e2->i == 5); 120 121 assert(e1.error().numberOfMoves == 2); 122 assert(!e1.error().swapCalled); 123 assert(e2->numberOfMoves == 1); 124 assert(!e2->swapCalled); 125 } 126 127 // this->has_value() && !rhs.has_value() 128 // && !is_nothrow_move_constructible_v<E> 129 { 130 std::expected<TrackedMove<true>, TrackedMove<false>> e1(std::in_place, 5); 131 std::expected<TrackedMove<true>, TrackedMove<false>> e2(std::unexpect, 10); 132 133 swap(e1, e2); 134 135 assert(!e1.has_value()); 136 assert(e1.error().i == 10); 137 assert(e2.has_value()); 138 assert(e2->i == 5); 139 140 assert(e1.error().numberOfMoves == 1); 141 assert(!e1.error().swapCalled); 142 assert(e2->numberOfMoves == 2); 143 assert(!e2->swapCalled); 144 } 145 146 // !this->has_value() && rhs.has_value() 147 // && is_nothrow_move_constructible_v<E> 148 { 149 std::expected<TrackedMove<true>, TrackedMove<true>> e1(std::unexpect, 10); 150 std::expected<TrackedMove<true>, TrackedMove<true>> e2(std::in_place, 5); 151 152 swap(e1, e2); 153 154 assert(e1.has_value()); 155 assert(e1->i == 5); 156 assert(!e2.has_value()); 157 assert(e2.error().i == 10); 158 159 assert(e1->numberOfMoves == 1); 160 assert(!e1->swapCalled); 161 assert(e2.error().numberOfMoves == 2); 162 assert(!e2.error().swapCalled); 163 } 164 165 // !this->has_value() && rhs.has_value() 166 // && !is_nothrow_move_constructible_v<E> 167 { 168 std::expected<TrackedMove<true>, TrackedMove<false>> e1(std::unexpect, 10); 169 std::expected<TrackedMove<true>, TrackedMove<false>> e2(std::in_place, 5); 170 171 swap(e1, e2); 172 173 assert(e1.has_value()); 174 assert(e1->i == 5); 175 assert(!e2.has_value()); 176 assert(e2.error().i == 10); 177 178 assert(e1->numberOfMoves == 2); 179 assert(!e1->swapCalled); 180 assert(e2.error().numberOfMoves == 1); 181 assert(!e2.error().swapCalled); 182 } 183 184 // TailClobberer 185 { 186 // is_nothrow_move_constructible_v<E> 187 { 188 std::expected<TailClobbererNonTrivialMove<0, true>, TailClobbererNonTrivialMove<1, true>> x(std::in_place); 189 std::expected<TailClobbererNonTrivialMove<0, true>, TailClobbererNonTrivialMove<1, true>> y(std::unexpect); 190 191 swap(x, y); 192 193 // Both of these would fail if adjusting the "has value" flags happened 194 // _before_ constructing the member objects inside the `swap`. 195 assert(!x.has_value()); 196 assert(y.has_value()); 197 } 198 199 // !is_nothrow_move_constructible_v<E> 200 { 201 std::expected<TailClobbererNonTrivialMove<0, true>, TailClobbererNonTrivialMove<1, false>> x(std::in_place); 202 std::expected<TailClobbererNonTrivialMove<0, true>, TailClobbererNonTrivialMove<1, false>> y(std::unexpect); 203 204 swap(x, y); 205 206 // Both of these would fail if adjusting the "has value" flags happened 207 // _before_ constructing the member objects inside the `swap`. 208 assert(!x.has_value()); 209 assert(y.has_value()); 210 } 211 } 212 213 return true; 214 } 215 216 void testException() { 217 #ifndef TEST_HAS_NO_EXCEPTIONS 218 // !e1.has_value() && e2.has_value() 219 { 220 std::expected<ThrowOnMoveConstruct, int> e1(std::unexpect, 5); 221 std::expected<ThrowOnMoveConstruct, int> e2(std::in_place); 222 try { 223 swap(e1, e2); 224 assert(false); 225 } catch (Except) { 226 assert(!e1.has_value()); 227 assert(e1.error() == 5); 228 } 229 } 230 231 // e1.has_value() && !e2.has_value() 232 { 233 std::expected<int, ThrowOnMoveConstruct> e1(5); 234 std::expected<int, ThrowOnMoveConstruct> e2(std::unexpect); 235 try { 236 swap(e1, e2); 237 assert(false); 238 } catch (Except) { 239 assert(e1.has_value()); 240 assert(*e1 == 5); 241 } 242 } 243 244 // TailClobberer 245 { 246 // is_nothrow_move_constructible_v<E> 247 { 248 std::expected<TailClobbererNonTrivialMove<0, false, true>, TailClobbererNonTrivialMove<1>> x(std::in_place); 249 std::expected<TailClobbererNonTrivialMove<0, false, true>, TailClobbererNonTrivialMove<1>> y(std::unexpect); 250 try { 251 swap(x, y); 252 assert(false); 253 } catch (Except) { 254 assert(x.has_value()); 255 // This would fail if `TailClobbererNonTrivialMove<1>` clobbered the 256 // flag when rolling back the swap. 257 assert(!y.has_value()); 258 } 259 } 260 261 // !is_nothrow_move_constructible_v<E> 262 { 263 std::expected<TailClobbererNonTrivialMove<0>, TailClobbererNonTrivialMove<1, false, true>> x(std::in_place); 264 std::expected<TailClobbererNonTrivialMove<0>, TailClobbererNonTrivialMove<1, false, true>> y(std::unexpect); 265 try { 266 swap(x, y); 267 assert(false); 268 } catch (Except) { 269 // This would fail if `TailClobbererNonTrivialMove<0>` clobbered the 270 // flag when rolling back the swap. 271 assert(x.has_value()); 272 assert(!y.has_value()); 273 } 274 } 275 } 276 #endif // TEST_HAS_NO_EXCEPTIONS 277 } 278 279 int main(int, char**) { 280 test(); 281 static_assert(test()); 282 testException(); 283 return 0; 284 } 285