xref: /llvm-project/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.convert.copy.pass.cpp (revision 6a54dfbfe534276d644d7f9c027f0deeb748dd53)
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