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 | |