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