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 // template<class U, class G> 12 // constexpr explicit(see below) expected(expected<U, G>&&); 13 // 14 // Let: 15 // - UF be const U 16 // - GF be const G 17 // 18 // Constraints: 19 // - is_constructible_v<T, UF> is true; and 20 // - is_constructible_v<E, GF> is true; and 21 // - is_constructible_v<T, expected<U, G>&> is false; and 22 // - is_constructible_v<T, expected<U, G>> is false; and 23 // - is_constructible_v<T, const expected<U, G>&> is false; and 24 // - is_constructible_v<T, const expected<U, G>> is false; and 25 // - is_convertible_v<expected<U, G>&, T> is false; and 26 // - is_convertible_v<expected<U, G>&&, T> is false; and 27 // - is_convertible_v<const expected<U, G>&, T> is false; and 28 // - is_convertible_v<const expected<U, G>&&, T> is false; and 29 // - is_constructible_v<unexpected<E>, expected<U, G>&> is false; and 30 // - is_constructible_v<unexpected<E>, expected<U, G>> is false; and 31 // - is_constructible_v<unexpected<E>, const expected<U, G>&> is false; and 32 // - is_constructible_v<unexpected<E>, const expected<U, G>> is false. 33 // 34 // Effects: If rhs.has_value(), direct-non-list-initializes val with std::forward<UF>(*rhs). Otherwise, direct-non-list-initializes unex with std::forward<GF>(rhs.error()). 35 // 36 // Postconditions: rhs.has_value() is unchanged; rhs.has_value() == this->has_value() is true. 37 // 38 // Throws: Any exception thrown by the initialization of val or unex. 39 // 40 // Remarks: The expression inside explicit is equivalent to !is_convertible_v<UF, T> || !is_convertible_v<GF, E>. 41 42 #include <cassert> 43 #include <concepts> 44 #include <expected> 45 #include <type_traits> 46 #include <utility> 47 48 #include "MoveOnly.h" 49 #include "test_macros.h" 50 #include "../../types.h" 51 52 // Test Constraints: 53 template <class T1, class Err1, class T2, class Err2> 54 concept canCstrFromExpected = std::is_constructible_v<std::expected<T1, Err1>, std::expected<T2, Err2>&&>; 55 56 struct CtorFromInt { 57 CtorFromInt(int); 58 }; 59 60 static_assert(canCstrFromExpected<CtorFromInt, int, int, int>); 61 62 struct NoCtorFromInt {}; 63 64 // !is_constructible_v<T, UF> 65 static_assert(!canCstrFromExpected<NoCtorFromInt, int, int, int>); 66 67 // !is_constructible_v<E, GF> 68 static_assert(!canCstrFromExpected<int, NoCtorFromInt, int, int>); 69 70 template <class T> 71 struct CtorFrom { 72 explicit CtorFrom(int) 73 requires(!std::same_as<T, int>); 74 explicit CtorFrom(T); 75 explicit CtorFrom(auto&&) = delete; 76 }; 77 78 // is_constructible_v<T, expected<U, G>&> 79 static_assert(!canCstrFromExpected<CtorFrom<std::expected<int, int>&>, int, int, int>); 80 81 // is_constructible_v<T, expected<U, G>> 82 // note that this is true because it is covered by the other overload 83 // template<class U = T> constexpr explicit(see below) expected(U&& v); 84 // The fact that it is not ambiguous proves that the overload under testing is removed 85 static_assert(canCstrFromExpected<CtorFrom<std::expected<int, int>&&>, int, int, int>); 86 87 // is_constructible_v<T, expected<U, G>&> 88 static_assert(!canCstrFromExpected<CtorFrom<std::expected<int, int> const&>, int, int, int>); 89 90 // is_constructible_v<T, expected<U, G>> 91 static_assert(!canCstrFromExpected<CtorFrom<std::expected<int, int> const&&>, int, int, int>); 92 93 template <class T> 94 struct ConvertFrom { 95 ConvertFrom(int) 96 requires(!std::same_as<T, int>); 97 ConvertFrom(T); 98 ConvertFrom(auto&&) = delete; 99 }; 100 101 // is_convertible_v<expected<U, G>&, T> 102 static_assert(!canCstrFromExpected<ConvertFrom<std::expected<int, int>&>, int, int, int>); 103 104 // is_convertible_v<expected<U, G>&&, T> 105 // note that this is true because it is covered by the other overload 106 // template<class U = T> constexpr explicit(see below) expected(U&& v); 107 // The fact that it is not ambiguous proves that the overload under testing is removed 108 static_assert(canCstrFromExpected<ConvertFrom<std::expected<int, int>&&>, int, int, int>); 109 110 // is_convertible_v<const expected<U, G>&, T> 111 static_assert(!canCstrFromExpected<ConvertFrom<std::expected<int, int> const&>, int, int, int>); 112 113 // is_convertible_v<const expected<U, G>&&, T> 114 static_assert(!canCstrFromExpected<ConvertFrom<std::expected<int, int> const&&>, int, int, int>); 115 116 // is_constructible_v<unexpected<E>, expected<U, G>&> 117 static_assert(!canCstrFromExpected<int, CtorFrom<std::expected<int, int>&>, int, int>); 118 119 // is_constructible_v<unexpected<E>, expected<U, G>> 120 static_assert(!canCstrFromExpected<int, CtorFrom<std::expected<int, int>&&>, int, int>); 121 122 // is_constructible_v<unexpected<E>, const expected<U, G>&> is false 123 static_assert(!canCstrFromExpected<int, CtorFrom<std::expected<int, int> const&>, int, int>); 124 125 // is_constructible_v<unexpected<E>, const expected<U, G>> 126 static_assert(!canCstrFromExpected<int, CtorFrom<std::expected<int, int> const&&>, int, int>); 127 128 // test explicit 129 static_assert(std::is_convertible_v<std::expected<int, int>&&, std::expected<short, long>>); 130 131 // !is_convertible_v<UF, T> 132 static_assert(std::is_constructible_v<std::expected<CtorFrom<int>, int>, std::expected<int, int>&&>); 133 static_assert(!std::is_convertible_v<std::expected<int, int>&&, std::expected<CtorFrom<int>, int>>); 134 135 // !is_convertible_v<GF, E>. 136 static_assert(std::is_constructible_v<std::expected<int, CtorFrom<int>>, std::expected<int, int>&&>); 137 static_assert(!std::is_convertible_v<std::expected<int, int>&&, std::expected<int, CtorFrom<int>>>); 138 139 struct Data { 140 MoveOnly data; 141 constexpr Data(MoveOnly&& m) : data(std::move(m)) {} 142 }; 143 144 constexpr bool test() { 145 // convert the value 146 { 147 std::expected<MoveOnly, int> e1(5); 148 std::expected<Data, int> e2 = std::move(e1); 149 assert(e2.has_value()); 150 assert(e2.value().data.get() == 5); 151 assert(e1.has_value()); 152 assert(e1.value().get() == 0); 153 } 154 155 // convert the error 156 { 157 std::expected<int, MoveOnly> e1(std::unexpect, 5); 158 std::expected<int, Data> e2 = std::move(e1); 159 assert(!e2.has_value()); 160 assert(e2.error().data.get() == 5); 161 assert(!e1.has_value()); 162 assert(e1.error().get() == 0); 163 } 164 165 // convert TailClobberer 166 { 167 std::expected<TailClobbererNonTrivialMove<0>, char> e1; 168 std::expected<TailClobberer<0>, char> e2 = std::move(e1); 169 assert(e2.has_value()); 170 assert(e1.has_value()); 171 } 172 173 return true; 174 } 175 176 void testException() { 177 #ifndef TEST_HAS_NO_EXCEPTIONS 178 struct ThrowingInt { 179 ThrowingInt(int) { throw Except{}; } 180 }; 181 182 // throw on converting value 183 { 184 const std::expected<int, int> e1; 185 try { 186 [[maybe_unused]] std::expected<ThrowingInt, int> e2 = e1; 187 assert(false); 188 } catch (Except) { 189 } 190 } 191 192 // throw on converting error 193 { 194 const std::expected<int, int> e1(std::unexpect); 195 try { 196 [[maybe_unused]] std::expected<int, ThrowingInt> e2 = e1; 197 assert(false); 198 } catch (Except) { 199 } 200 } 201 202 #endif // TEST_HAS_NO_EXCEPTIONS 203 } 204 205 int main(int, char**) { 206 test(); 207 static_assert(test()); 208 testException(); 209 return 0; 210 } 211