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<E> is true and is_move_constructible_v<E> is true. 15 // 16 // Throws: Any exception thrown by the expressions in the Effects. 17 // 18 // Remarks: The exception specification is equivalent to: 19 // is_nothrow_move_constructible_v<E> && is_nothrow_swappable_v<E>. 20 21 #include <cassert> 22 #include <expected> 23 #include <type_traits> 24 #include <utility> 25 26 #include "../../types.h" 27 #include "test_macros.h" 28 29 // Test Constraints: 30 template <class E> 31 concept HasMemberSwap = requires(std::expected<void, E> x, std::expected<void, E> y) { x.swap(y); }; 32 33 static_assert(HasMemberSwap<int>); 34 35 struct NotSwappable {}; 36 void swap(NotSwappable&, NotSwappable&) = delete; 37 38 // !is_swappable_v<E> 39 static_assert(!HasMemberSwap<NotSwappable>); 40 41 struct NotMoveConstructible { 42 NotMoveConstructible(NotMoveConstructible&&) = delete; 43 friend void swap(NotMoveConstructible&, NotMoveConstructible&) {} 44 }; 45 46 // !is_move_constructible_v<E> 47 static_assert(!HasMemberSwap<NotMoveConstructible>); 48 49 // Test noexcept 50 struct MoveMayThrow { 51 MoveMayThrow(MoveMayThrow&&) noexcept(false); 52 friend void swap(MoveMayThrow&, MoveMayThrow&) noexcept {} 53 }; 54 55 template <class E> 56 concept MemberSwapNoexcept = // 57 requires(std::expected<void, E> x, std::expected<void, E> y) { 58 { x.swap(y) } noexcept; 59 }; 60 61 static_assert(MemberSwapNoexcept<int>); 62 63 // !is_nothrow_move_constructible_v<E> 64 static_assert(!MemberSwapNoexcept<MoveMayThrow>); 65 66 struct SwapMayThrow { 67 friend void swap(SwapMayThrow&, SwapMayThrow&) noexcept(false) {} 68 }; 69 70 // !is_nothrow_swappable_v<E> 71 static_assert(!MemberSwapNoexcept<SwapMayThrow>); 72 73 constexpr bool test() { 74 // this->has_value() && rhs.has_value() 75 { 76 std::expected<void, int> x; 77 std::expected<void, int> y; 78 x.swap(y); 79 80 assert(x.has_value()); 81 assert(y.has_value()); 82 } 83 84 // !this->has_value() && !rhs.has_value() 85 { 86 std::expected<void, ADLSwap> x(std::unexpect, 5); 87 std::expected<void, ADLSwap> y(std::unexpect, 10); 88 x.swap(y); 89 90 assert(!x.has_value()); 91 assert(x.error().i == 10); 92 assert(x.error().adlSwapCalled); 93 assert(!y.has_value()); 94 assert(y.error().i == 5); 95 assert(y.error().adlSwapCalled); 96 } 97 98 // this->has_value() && !rhs.has_value() 99 { 100 Traced::state s{}; 101 std::expected<void, Traced> e1(std::in_place); 102 std::expected<void, Traced> e2(std::unexpect, s, 10); 103 104 e1.swap(e2); 105 106 assert(!e1.has_value()); 107 assert(e1.error().data_ == 10); 108 assert(e2.has_value()); 109 110 assert(s.moveCtorCalled); 111 assert(s.dtorCalled); 112 } 113 114 // !this->has_value() && rhs.has_value() 115 { 116 Traced::state s{}; 117 std::expected<void, Traced> e1(std::unexpect, s, 10); 118 std::expected<void, Traced> e2(std::in_place); 119 120 e1.swap(e2); 121 122 assert(e1.has_value()); 123 assert(!e2.has_value()); 124 assert(e2.error().data_ == 10); 125 126 assert(s.moveCtorCalled); 127 assert(s.dtorCalled); 128 } 129 130 // TailClobberer 131 { 132 std::expected<void, TailClobbererNonTrivialMove<1>> x(std::in_place); 133 std::expected<void, TailClobbererNonTrivialMove<1>> y(std::unexpect); 134 135 x.swap(y); 136 137 // The next line would fail if adjusting the "has value" flag happened 138 // _before_ constructing the member object inside the `swap`. 139 assert(!x.has_value()); 140 assert(y.has_value()); 141 } 142 143 // CheckForInvalidWrites 144 { 145 { 146 CheckForInvalidWrites<true, true> x(std::unexpect); 147 CheckForInvalidWrites<true, true> y; 148 149 x.swap(y); 150 151 assert(x.check()); 152 assert(y.check()); 153 } 154 { 155 CheckForInvalidWrites<false, true> x(std::unexpect); 156 CheckForInvalidWrites<false, true> y; 157 158 x.swap(y); 159 160 assert(x.check()); 161 assert(y.check()); 162 } 163 } 164 165 return true; 166 } 167 168 void testException() { 169 #ifndef TEST_HAS_NO_EXCEPTIONS 170 // !e1.has_value() && e2.has_value() 171 { 172 bool e1Destroyed = false; 173 std::expected<void, ThrowOnMove> e1(std::unexpect, e1Destroyed); 174 std::expected<void, ThrowOnMove> e2(std::in_place); 175 try { 176 e1.swap(e2); 177 assert(false); 178 } catch (Except) { 179 assert(!e1.has_value()); 180 assert(e2.has_value()); 181 assert(!e1Destroyed); 182 } 183 } 184 185 // e1.has_value() && !e2.has_value() 186 { 187 bool e2Destroyed = false; 188 std::expected<void, ThrowOnMove> e1(std::in_place); 189 std::expected<void, ThrowOnMove> e2(std::unexpect, e2Destroyed); 190 try { 191 e1.swap(e2); 192 assert(false); 193 } catch (Except) { 194 assert(e1.has_value()); 195 assert(!e2.has_value()); 196 assert(!e2Destroyed); 197 } 198 } 199 200 // TailClobberer 201 { 202 std::expected<void, TailClobbererNonTrivialMove<0, false, true>> x(std::in_place); 203 std::expected<void, TailClobbererNonTrivialMove<0, false, true>> y(std::unexpect); 204 try { 205 x.swap(y); 206 assert(false); 207 } catch (Except) { 208 // This would fail if `TailClobbererNonTrivialMove<0, false, true>` 209 // clobbered the flag before throwing the exception. 210 assert(x.has_value()); 211 assert(!y.has_value()); 212 } 213 } 214 #endif // TEST_HAS_NO_EXCEPTIONS 215 } 216 217 int main(int, char**) { 218 test(); 219 static_assert(test()); 220 testException(); 221 return 0; 222 } 223