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