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(const 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 "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>, const 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 | static_assert(!canCstrFromExpected<CtorFrom<std::expected<int, int>&&>, int, int, int>); |
82 | |
83 | // is_constructible_v<T, expected<U, G>&> |
84 | // note that this is true because it is covered by the other overload |
85 | // template<class U = T> constexpr explicit(see below) expected(U&& v); |
86 | // The fact that it is not ambiguous proves that the overload under testing is removed |
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 | static_assert(!canCstrFromExpected<ConvertFrom<std::expected<int, int>&&>, int, int, int>); |
105 | |
106 | // is_convertible_v<const expected<U, G>&, T> |
107 | // note that this is true because it is covered by the other overload |
108 | // template<class U = T> constexpr explicit(see below) expected(U&& v); |
109 | // The fact that it is not ambiguous proves that the overload under testing is removed |
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 | // Note for below 4 tests, because their E is constructible from cvref of std::expected<int, int>, |
116 | // unexpected<E> will be constructible from cvref of std::expected<int, int> |
117 | // is_constructible_v<unexpected<E>, expected<U, G>&> |
118 | static_assert(!canCstrFromExpected<int, CtorFrom<std::expected<int, int>&>, int, int>); |
119 | |
120 | // is_constructible_v<unexpected<E>, expected<U, G>> |
121 | static_assert(!canCstrFromExpected<int, CtorFrom<std::expected<int, int>&&>, int, int>); |
122 | |
123 | // is_constructible_v<unexpected<E>, const expected<U, G>&> is false |
124 | static_assert(!canCstrFromExpected<int, CtorFrom<std::expected<int, int> const&>, int, int>); |
125 | |
126 | // is_constructible_v<unexpected<E>, const expected<U, G>> |
127 | static_assert(!canCstrFromExpected<int, CtorFrom<std::expected<int, int> const&&>, int, int>); |
128 | |
129 | // test explicit |
130 | static_assert(std::is_convertible_v<const std::expected<int, int>&, std::expected<short, long>>); |
131 | |
132 | // !is_convertible_v<UF, T> |
133 | static_assert(std::is_constructible_v<std::expected<CtorFrom<int>, int>, const std::expected<int, int>&>); |
134 | static_assert(!std::is_convertible_v<const std::expected<int, int>&, std::expected<CtorFrom<int>, int>>); |
135 | |
136 | // !is_convertible_v<GF, E>. |
137 | static_assert(std::is_constructible_v<std::expected<int, CtorFrom<int>>, const std::expected<int, int>&>); |
138 | static_assert(!std::is_convertible_v<const std::expected<int, int>&, std::expected<int, CtorFrom<int>>>); |
139 | |
140 | struct Data { |
141 | int i; |
142 | constexpr Data(int ii) : i(ii) {} |
143 | }; |
144 | |
145 | constexpr bool test() { |
146 | // convert the value |
147 | { |
148 | const std::expected<int, int> e1(5); |
149 | std::expected<Data, int> e2 = e1; |
150 | assert(e2.has_value()); |
151 | assert(e2.value().i == 5); |
152 | assert(e1.has_value()); |
153 | assert(e1.value() == 5); |
154 | } |
155 | |
156 | // convert the error |
157 | { |
158 | const std::expected<int, int> e1(std::unexpect, 5); |
159 | std::expected<int, Data> e2 = e1; |
160 | assert(!e2.has_value()); |
161 | assert(e2.error().i == 5); |
162 | assert(!e1.has_value()); |
163 | assert(e1.error() == 5); |
164 | } |
165 | |
166 | // convert TailClobberer |
167 | { |
168 | const std::expected<TailClobbererNonTrivialMove<0>, char> e1; |
169 | std::expected<TailClobberer<0>, char> e2 = e1; |
170 | assert(e2.has_value()); |
171 | assert(e1.has_value()); |
172 | } |
173 | |
174 | return true; |
175 | } |
176 | |
177 | void testException() { |
178 | #ifndef TEST_HAS_NO_EXCEPTIONS |
179 | struct ThrowingInt { |
180 | ThrowingInt(int) { throw Except{}; } |
181 | }; |
182 | |
183 | // throw on converting value |
184 | { |
185 | const std::expected<int, int> e1; |
186 | try { |
187 | [[maybe_unused]] std::expected<ThrowingInt, int> e2 = e1; |
188 | assert(false); |
189 | } catch (Except) { |
190 | } |
191 | } |
192 | |
193 | // throw on converting error |
194 | { |
195 | const std::expected<int, int> e1(std::unexpect); |
196 | try { |
197 | [[maybe_unused]] std::expected<int, ThrowingInt> e2 = e1; |
198 | assert(false); |
199 | } catch (Except) { |
200 | } |
201 | } |
202 | |
203 | #endif // TEST_HAS_NO_EXCEPTIONS |
204 | } |
205 | |
206 | int main(int, char**) { |
207 | test(); |
208 | static_assert(test()); |
209 | testException(); |
210 | return 0; |
211 | } |
212 | |