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

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