xref: /llvm-project/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.convert.move.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(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