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(!is_convertible_v<G, E>) expected(expected<U, G>&& rhs); |
12 | // |
13 | // Let GF be G |
14 | // |
15 | // Constraints: |
16 | // - is_void_v<U> is true; and |
17 | // - is_constructible_v<E, GF> is true; and |
18 | // - is_constructible_v<unexpected<E>, expected<U, G>&> is false; and |
19 | // - is_constructible_v<unexpected<E>, expected<U, G>> is false; and |
20 | // - is_constructible_v<unexpected<E>, const expected<U, G>&> is false; and |
21 | // - is_constructible_v<unexpected<E>, const expected<U, G>> is false. |
22 | // |
23 | // Effects: If rhs.has_value() is false, direct-non-list-initializes unex with std::forward<GF>(rhs.error()). |
24 | // |
25 | // Postconditions: rhs.has_value() is unchanged; rhs.has_value() == this->has_value() is true. |
26 | // |
27 | // Throws: Any exception thrown by the initialization of unex. |
28 | |
29 | #include <cassert> |
30 | #include <concepts> |
31 | #include <expected> |
32 | #include <type_traits> |
33 | #include <utility> |
34 | |
35 | #include "MoveOnly.h" |
36 | #include "test_macros.h" |
37 | #include "../../types.h" |
38 | |
39 | // Test Constraints: |
40 | template <class T1, class Err1, class T2, class Err2> |
41 | concept canCstrFromExpected = std::is_constructible_v<std::expected<T1, Err1>, std::expected<T2, Err2>&&>; |
42 | |
43 | struct CtorFromInt { |
44 | CtorFromInt(int); |
45 | }; |
46 | |
47 | static_assert(canCstrFromExpected<void, CtorFromInt, void, int>); |
48 | |
49 | struct NoCtorFromInt {}; |
50 | |
51 | // !is_void_v<U> |
52 | static_assert(!canCstrFromExpected<void, int, int, int>); |
53 | |
54 | // !is_constructible_v<E, GF> |
55 | static_assert(!canCstrFromExpected<void, NoCtorFromInt, void, int>); |
56 | |
57 | template <class T> |
58 | struct CtorFrom { |
59 | explicit CtorFrom(int) |
60 | requires(!std::same_as<T, int>); |
61 | explicit CtorFrom(T); |
62 | explicit CtorFrom(auto&&) = delete; |
63 | }; |
64 | |
65 | // Note for below 4 tests, because their E is constructible from cvref of std::expected<void, int>, |
66 | // unexpected<E> will be constructible from cvref of std::expected<void, int> |
67 | // is_constructible_v<unexpected<E>, expected<U, G>&> |
68 | static_assert(!canCstrFromExpected<void, CtorFrom<std::expected<void, int>&>, void, int>); |
69 | |
70 | // is_constructible_v<unexpected<E>, expected<U, G>> |
71 | static_assert(!canCstrFromExpected<void, CtorFrom<std::expected<void, int>&&>, void, int>); |
72 | |
73 | // is_constructible_v<unexpected<E>, const expected<U, G>&> is false |
74 | static_assert(!canCstrFromExpected<void, CtorFrom<std::expected<void, int> const&>, void, int>); |
75 | |
76 | // is_constructible_v<unexpected<E>, const expected<U, G>> |
77 | static_assert(!canCstrFromExpected<void, CtorFrom<std::expected<void, int> const&&>, void, int>); |
78 | |
79 | // test explicit |
80 | static_assert(std::is_convertible_v<std::expected<void, int>&&, std::expected<void, long>>); |
81 | |
82 | // !is_convertible_v<GF, E>. |
83 | static_assert(std::is_constructible_v<std::expected<void, CtorFrom<int>>, std::expected<void, int>&&>); |
84 | static_assert(!std::is_convertible_v<std::expected<void, int>&&, std::expected<void, CtorFrom<int>>>); |
85 | |
86 | struct Data { |
87 | MoveOnly data; |
88 | constexpr Data(MoveOnly&& m) : data(std::move(m)) {} |
89 | }; |
90 | |
91 | constexpr bool test() { |
92 | // convert the error |
93 | { |
94 | std::expected<void, MoveOnly> e1(std::unexpect, 5); |
95 | std::expected<void, Data> e2 = std::move(e1); |
96 | assert(!e2.has_value()); |
97 | assert(e2.error().data.get() == 5); |
98 | assert(!e1.has_value()); |
99 | assert(e1.error().get() == 0); |
100 | } |
101 | |
102 | // convert TailClobberer |
103 | { |
104 | std::expected<void, TailClobbererNonTrivialMove<1>> e1(std::unexpect); |
105 | std::expected<void, TailClobberer<1>> e2 = std::move(e1); |
106 | assert(!e2.has_value()); |
107 | assert(!e1.has_value()); |
108 | } |
109 | |
110 | return true; |
111 | } |
112 | |
113 | void testException() { |
114 | #ifndef TEST_HAS_NO_EXCEPTIONS |
115 | struct ThrowingInt { |
116 | ThrowingInt(int) { throw Except{}; } |
117 | }; |
118 | |
119 | // throw on converting error |
120 | { |
121 | const std::expected<void, int> e1(std::unexpect); |
122 | try { |
123 | [[maybe_unused]] std::expected<void, ThrowingInt> e2 = e1; |
124 | assert(false); |
125 | } catch (Except) { |
126 | } |
127 | } |
128 | |
129 | #endif // TEST_HAS_NO_EXCEPTIONS |
130 | } |
131 | |
132 | int main(int, char**) { |
133 | test(); |
134 | static_assert(test()); |
135 | testException(); |
136 | return 0; |
137 | } |
138 | |