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:
52template <class T1, class Err1, class T2, class Err2>
53concept canCstrFromExpected = std::is_constructible_v<std::expected<T1, Err1>, const std::expected<T2, Err2>&>;
54
55struct CtorFromInt {
56 CtorFromInt(int);
57};
58
59static_assert(canCstrFromExpected<CtorFromInt, int, int, int>);
60
61struct NoCtorFromInt {};
62
63// !is_constructible_v<T, UF>
64static_assert(!canCstrFromExpected<NoCtorFromInt, int, int, int>);
65
66// !is_constructible_v<E, GF>
67static_assert(!canCstrFromExpected<int, NoCtorFromInt, int, int>);
68
69template <class T>
70struct 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>&>
78static_assert(!canCstrFromExpected<CtorFrom<std::expected<int, int>&>, int, int, int>);
79
80// is_constructible_v<T, expected<U, G>>
81static_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
87static_assert(canCstrFromExpected<CtorFrom<std::expected<int, int> const&>, int, int, int>);
88
89// is_constructible_v<T, expected<U, G>>
90static_assert(!canCstrFromExpected<CtorFrom<std::expected<int, int> const&&>, int, int, int>);
91
92template <class T>
93struct 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>
101static_assert(!canCstrFromExpected<ConvertFrom<std::expected<int, int>&>, int, int, int>);
102
103// is_convertible_v<expected<U, G>&&, T>
104static_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
110static_assert(canCstrFromExpected<ConvertFrom<std::expected<int, int> const&>, int, int, int>);
111
112// is_convertible_v<const expected<U, G>&&, T>
113static_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>&>
118static_assert(!canCstrFromExpected<int, CtorFrom<std::expected<int, int>&>, int, int>);
119
120// is_constructible_v<unexpected<E>, expected<U, G>>
121static_assert(!canCstrFromExpected<int, CtorFrom<std::expected<int, int>&&>, int, int>);
122
123// is_constructible_v<unexpected<E>, const expected<U, G>&> is false
124static_assert(!canCstrFromExpected<int, CtorFrom<std::expected<int, int> const&>, int, int>);
125
126// is_constructible_v<unexpected<E>, const expected<U, G>>
127static_assert(!canCstrFromExpected<int, CtorFrom<std::expected<int, int> const&&>, int, int>);
128
129// test explicit
130static_assert(std::is_convertible_v<const std::expected<int, int>&, std::expected<short, long>>);
131
132// !is_convertible_v<UF, T>
133static_assert(std::is_constructible_v<std::expected<CtorFrom<int>, int>, const std::expected<int, int>&>);
134static_assert(!std::is_convertible_v<const std::expected<int, int>&, std::expected<CtorFrom<int>, int>>);
135
136// !is_convertible_v<GF, E>.
137static_assert(std::is_constructible_v<std::expected<int, CtorFrom<int>>, const std::expected<int, int>&>);
138static_assert(!std::is_convertible_v<const std::expected<int, int>&, std::expected<int, CtorFrom<int>>>);
139
140struct Data {
141 int i;
142 constexpr Data(int ii) : i(ii) {}
143};
144
145constexpr 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
177void 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
206int main(int, char**) {
207 test();
208 static_assert(test());
209 testException();
210 return 0;
211}
212

source code of libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.convert.copy.pass.cpp