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(const 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 "test_macros.h" |
48 | #include "../../types.h" |
49 | |
50 | // Test Constraints: |
51 | template <class T1, class Err1, class T2, class Err2> |
52 | concept canCstrFromExpected = std::is_constructible_v<std::expected<T1, Err1>, const std::expected<T2, Err2>&>; |
53 | |
54 | struct CtorFromInt { |
55 | CtorFromInt(int); |
56 | }; |
57 | |
58 | static_assert(canCstrFromExpected<CtorFromInt, int, int, int>); |
59 | |
60 | struct NoCtorFromInt {}; |
61 | |
62 | // !is_constructible_v<T, UF> |
63 | static_assert(!canCstrFromExpected<NoCtorFromInt, int, int, int>); |
64 | |
65 | // !is_constructible_v<E, GF> |
66 | static_assert(!canCstrFromExpected<int, NoCtorFromInt, int, int>); |
67 | |
68 | template <class T> |
69 | struct CtorFrom { |
70 | explicit CtorFrom(int) |
71 | requires(!std::same_as<T, int>); |
72 | explicit CtorFrom(T); |
73 | explicit CtorFrom(auto&&) = delete; |
74 | }; |
75 | |
76 | // is_constructible_v<T, expected<U, G>&> |
77 | static_assert(!canCstrFromExpected<CtorFrom<std::expected<int, int>&>, int, int, int>); |
78 | |
79 | // is_constructible_v<T, expected<U, G>> |
80 | static_assert(!canCstrFromExpected<CtorFrom<std::expected<int, int>&&>, int, int, int>); |
81 | |
82 | // is_constructible_v<T, expected<U, G>&> |
83 | // note that this is true because it is covered by the other overload |
84 | // template<class U = T> constexpr explicit(see below) expected(U&& v); |
85 | // The fact that it is not ambiguous proves that the overload under testing is removed |
86 | static_assert(canCstrFromExpected<CtorFrom<std::expected<int, int> const&>, int, int, int>); |
87 | |
88 | // is_constructible_v<T, expected<U, G>> |
89 | static_assert(!canCstrFromExpected<CtorFrom<std::expected<int, int> const&&>, int, int, int>); |
90 | |
91 | template <class T> |
92 | struct ConvertFrom { |
93 | ConvertFrom(int) |
94 | requires(!std::same_as<T, int>); |
95 | ConvertFrom(T); |
96 | ConvertFrom(auto&&) = delete; |
97 | }; |
98 | |
99 | // is_convertible_v<expected<U, G>&, T> |
100 | static_assert(!canCstrFromExpected<ConvertFrom<std::expected<int, int>&>, int, int, int>); |
101 | |
102 | // is_convertible_v<expected<U, G>&&, T> |
103 | static_assert(!canCstrFromExpected<ConvertFrom<std::expected<int, int>&&>, int, int, int>); |
104 | |
105 | // is_convertible_v<const expected<U, G>&, T> |
106 | // note that this is true because it is covered by the other overload |
107 | // template<class U = T> constexpr explicit(see below) expected(U&& v); |
108 | // The fact that it is not ambiguous proves that the overload under testing is removed |
109 | static_assert(canCstrFromExpected<ConvertFrom<std::expected<int, int> const&>, int, int, int>); |
110 | |
111 | // is_convertible_v<const expected<U, G>&&, T> |
112 | static_assert(!canCstrFromExpected<ConvertFrom<std::expected<int, int> const&&>, int, int, int>); |
113 | |
114 | // Note for below 4 tests, because their E is constructible from cvref of std::expected<int, int>, |
115 | // unexpected<E> will be constructible from cvref of std::expected<int, int> |
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<const 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>, const std::expected<int, int>&>); |
133 | static_assert(!std::is_convertible_v<const 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>>, const std::expected<int, int>&>); |
137 | static_assert(!std::is_convertible_v<const std::expected<int, int>&, std::expected<int, CtorFrom<int>>>); |
138 | |
139 | struct Data { |
140 | int i; |
141 | constexpr Data(int ii) : i(ii) {} |
142 | }; |
143 | |
144 | constexpr bool test() { |
145 | // convert the value |
146 | { |
147 | const std::expected<int, int> e1(5); |
148 | std::expected<Data, int> e2 = e1; |
149 | assert(e2.has_value()); |
150 | assert(e2.value().i == 5); |
151 | assert(e1.has_value()); |
152 | assert(e1.value() == 5); |
153 | } |
154 | |
155 | // convert the error |
156 | { |
157 | const std::expected<int, int> e1(std::unexpect, 5); |
158 | std::expected<int, Data> e2 = e1; |
159 | assert(!e2.has_value()); |
160 | assert(e2.error().i == 5); |
161 | assert(!e1.has_value()); |
162 | assert(e1.error() == 5); |
163 | } |
164 | |
165 | // convert TailClobberer |
166 | { |
167 | const std::expected<TailClobbererNonTrivialMove<0>, char> e1; |
168 | std::expected<TailClobberer<0>, char> e2 = 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 | |