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 expected(expected&& rhs) noexcept(see below); 12 // 13 // Constraints: 14 // - is_move_constructible_v<T> is true and 15 // - is_move_constructible_v<E> is true. 16 // 17 // Effects: If rhs.has_value() is true, direct-non-list-initializes val with std::move(*rhs). 18 // Otherwise, direct-non-list-initializes unex with std::move(rhs.error()). 19 // 20 // Postconditions: rhs.has_value() is unchanged; rhs.has_value() == this->has_value() is true. 21 // 22 // Throws: Any exception thrown by the initialization of val or unex. 23 // 24 // Remarks: The exception specification is equivalent to is_nothrow_move_constructible_v<T> && is_nothrow_move_constructible_v<E>. 25 // 26 // This constructor is trivial if 27 // - is_trivially_move_constructible_v<T> is true and 28 // - is_trivially_move_constructible_v<E> is true. 29 30 #include <cassert> 31 #include <expected> 32 #include <type_traits> 33 #include <utility> 34 35 #include "test_macros.h" 36 #include "../../types.h" 37 38 struct NonMovable { 39 NonMovable(NonMovable&&) = delete; 40 }; 41 42 struct MovableNonTrivial { 43 int i; 44 constexpr MovableNonTrivial(int ii) : i(ii) {} 45 constexpr MovableNonTrivial(MovableNonTrivial&& o) : i(o.i) { o.i = 0; } 46 friend constexpr bool operator==(const MovableNonTrivial&, const MovableNonTrivial&) = default; 47 }; 48 49 struct MoveMayThrow { 50 MoveMayThrow(MoveMayThrow&&) {} 51 }; 52 53 // Test Constraints: 54 // - is_move_constructible_v<T> is true and 55 // - is_move_constructible_v<E> is true. 56 static_assert(std::is_move_constructible_v<std::expected<int, int>>); 57 static_assert(std::is_move_constructible_v<std::expected<MovableNonTrivial, int>>); 58 static_assert(std::is_move_constructible_v<std::expected<int, MovableNonTrivial>>); 59 static_assert(std::is_move_constructible_v<std::expected<MovableNonTrivial, MovableNonTrivial>>); 60 static_assert(!std::is_move_constructible_v<std::expected<NonMovable, int>>); 61 static_assert(!std::is_move_constructible_v<std::expected<int, NonMovable>>); 62 static_assert(!std::is_move_constructible_v<std::expected<NonMovable, NonMovable>>); 63 64 // Test: This constructor is trivial if 65 // - is_trivially_move_constructible_v<T> is true and 66 // - is_trivially_move_constructible_v<E> is true. 67 static_assert(std::is_trivially_move_constructible_v<std::expected<int, int>>); 68 static_assert(!std::is_trivially_move_constructible_v<std::expected<MovableNonTrivial, int>>); 69 static_assert(!std::is_trivially_move_constructible_v<std::expected<int, MovableNonTrivial>>); 70 static_assert(!std::is_trivially_move_constructible_v<std::expected<MovableNonTrivial, MovableNonTrivial>>); 71 72 // Test: The exception specification is equivalent to 73 // is_nothrow_move_constructible_v<T> && is_nothrow_move_constructible_v<E>. 74 static_assert(std::is_nothrow_move_constructible_v<std::expected<int, int>>); 75 static_assert(!std::is_nothrow_move_constructible_v<std::expected<MoveMayThrow, int>>); 76 static_assert(!std::is_nothrow_move_constructible_v<std::expected<int, MoveMayThrow>>); 77 static_assert(!std::is_nothrow_move_constructible_v<std::expected<MoveMayThrow, MoveMayThrow>>); 78 79 constexpr bool test() { 80 // move the value non-trivial 81 { 82 std::expected<MovableNonTrivial, int> e1(5); 83 auto e2 = std::move(e1); 84 assert(e2.has_value()); 85 assert(e2.value().i == 5); 86 assert(e1.has_value()); 87 assert(e1.value().i == 0); 88 } 89 90 // move the error non-trivial 91 { 92 std::expected<int, MovableNonTrivial> e1(std::unexpect, 5); 93 auto e2 = std::move(e1); 94 assert(!e2.has_value()); 95 assert(e2.error().i == 5); 96 assert(!e1.has_value()); 97 assert(e1.error().i == 0); 98 } 99 100 // move the value trivial 101 { 102 std::expected<int, int> e1(5); 103 auto e2 = std::move(e1); 104 assert(e2.has_value()); 105 assert(e2.value() == 5); 106 assert(e1.has_value()); 107 } 108 109 // move the error trivial 110 { 111 std::expected<int, int> e1(std::unexpect, 5); 112 auto e2 = std::move(e1); 113 assert(!e2.has_value()); 114 assert(e2.error() == 5); 115 assert(!e1.has_value()); 116 } 117 118 // move TailClobbererNonTrivialMove as value 119 { 120 std::expected<TailClobbererNonTrivialMove<0>, bool> e1; 121 auto e2 = std::move(e1); 122 assert(e2.has_value()); 123 assert(e1.has_value()); 124 } 125 126 // move TailClobbererNonTrivialMove as error 127 { 128 std::expected<bool, TailClobbererNonTrivialMove<1>> e1(std::unexpect); 129 auto e2 = std::move(e1); 130 assert(!e2.has_value()); 131 assert(!e1.has_value()); 132 } 133 134 return true; 135 } 136 137 void testException() { 138 #ifndef TEST_HAS_NO_EXCEPTIONS 139 struct Throwing { 140 Throwing() = default; 141 Throwing(Throwing&&) { throw Except{}; } 142 }; 143 144 // throw on moving value 145 { 146 std::expected<Throwing, int> e1; 147 try { 148 [[maybe_unused]] auto e2 = std::move(e1); 149 assert(false); 150 } catch (Except) { 151 } 152 } 153 154 // throw on moving error 155 { 156 std::expected<int, Throwing> e1(std::unexpect); 157 try { 158 [[maybe_unused]] auto e2 = std::move(e1); 159 assert(false); 160 } catch (Except) { 161 } 162 } 163 164 #endif // TEST_HAS_NO_EXCEPTIONS 165 } 166 167 int main(int, char**) { 168 test(); 169 static_assert(test()); 170 testException(); 171 return 0; 172 } 173