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