xref: /llvm-project/libcxx/test/std/utilities/expected/expected.expected/ctor/ctor.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 // constexpr expected(expected&& rhs) noexcept(see below);
12 //
13 // Constraints:
14 // - is_move_constructible_v<T> is true and
15 // - is_move_constructible_v<E> is true.
16 //
17 // Effects: If rhs.has_value() is true, direct-non-list-initializes val with std::move(*rhs).
18 // Otherwise, direct-non-list-initializes unex with std::move(rhs.error()).
19 //
20 // Postconditions: rhs.has_value() is unchanged; rhs.has_value() == this->has_value() is true.
21 //
22 // Throws: Any exception thrown by the initialization of val or unex.
23 //
24 // Remarks: The exception specification is equivalent to is_nothrow_move_constructible_v<T> && is_nothrow_move_constructible_v<E>.
25 //
26 // This constructor is trivial if
27 // - is_trivially_move_constructible_v<T> is true and
28 // - is_trivially_move_constructible_v<E> is true.
29 
30 #include <cassert>
31 #include <expected>
32 #include <type_traits>
33 #include <utility>
34 
35 #include "test_macros.h"
36 #include "../../types.h"
37 
38 struct NonMovable {
39   NonMovable(NonMovable&&) = delete;
40 };
41 
42 struct MovableNonTrivial {
43   int i;
44   constexpr MovableNonTrivial(int ii) : i(ii) {}
45   constexpr MovableNonTrivial(MovableNonTrivial&& o) : i(o.i) { o.i = 0; }
46   friend constexpr bool operator==(const MovableNonTrivial&, const MovableNonTrivial&) = default;
47 };
48 
49 struct MoveMayThrow {
50   MoveMayThrow(MoveMayThrow&&) {}
51 };
52 
53 // Test Constraints:
54 // - is_move_constructible_v<T> is true and
55 // - is_move_constructible_v<E> is true.
56 static_assert(std::is_move_constructible_v<std::expected<int, int>>);
57 static_assert(std::is_move_constructible_v<std::expected<MovableNonTrivial, int>>);
58 static_assert(std::is_move_constructible_v<std::expected<int, MovableNonTrivial>>);
59 static_assert(std::is_move_constructible_v<std::expected<MovableNonTrivial, MovableNonTrivial>>);
60 static_assert(!std::is_move_constructible_v<std::expected<NonMovable, int>>);
61 static_assert(!std::is_move_constructible_v<std::expected<int, NonMovable>>);
62 static_assert(!std::is_move_constructible_v<std::expected<NonMovable, NonMovable>>);
63 
64 // Test: This constructor is trivial if
65 // - is_trivially_move_constructible_v<T> is true and
66 // - is_trivially_move_constructible_v<E> is true.
67 static_assert(std::is_trivially_move_constructible_v<std::expected<int, int>>);
68 static_assert(!std::is_trivially_move_constructible_v<std::expected<MovableNonTrivial, int>>);
69 static_assert(!std::is_trivially_move_constructible_v<std::expected<int, MovableNonTrivial>>);
70 static_assert(!std::is_trivially_move_constructible_v<std::expected<MovableNonTrivial, MovableNonTrivial>>);
71 
72 // Test: The exception specification is equivalent to
73 // is_nothrow_move_constructible_v<T> && is_nothrow_move_constructible_v<E>.
74 static_assert(std::is_nothrow_move_constructible_v<std::expected<int, int>>);
75 static_assert(!std::is_nothrow_move_constructible_v<std::expected<MoveMayThrow, int>>);
76 static_assert(!std::is_nothrow_move_constructible_v<std::expected<int, MoveMayThrow>>);
77 static_assert(!std::is_nothrow_move_constructible_v<std::expected<MoveMayThrow, MoveMayThrow>>);
78 
79 constexpr bool test() {
80   // move the value non-trivial
81   {
82     std::expected<MovableNonTrivial, int> e1(5);
83     auto e2 = std::move(e1);
84     assert(e2.has_value());
85     assert(e2.value().i == 5);
86     assert(e1.has_value());
87     assert(e1.value().i == 0);
88   }
89 
90   // move the error non-trivial
91   {
92     std::expected<int, MovableNonTrivial> e1(std::unexpect, 5);
93     auto e2 = std::move(e1);
94     assert(!e2.has_value());
95     assert(e2.error().i == 5);
96     assert(!e1.has_value());
97     assert(e1.error().i == 0);
98   }
99 
100   // move the value trivial
101   {
102     std::expected<int, int> e1(5);
103     auto e2 = std::move(e1);
104     assert(e2.has_value());
105     assert(e2.value() == 5);
106     assert(e1.has_value());
107   }
108 
109   // move the error trivial
110   {
111     std::expected<int, int> e1(std::unexpect, 5);
112     auto e2 = std::move(e1);
113     assert(!e2.has_value());
114     assert(e2.error() == 5);
115     assert(!e1.has_value());
116   }
117 
118   // move TailClobbererNonTrivialMove as value
119   {
120     std::expected<TailClobbererNonTrivialMove<0>, bool> e1;
121     auto e2 = std::move(e1);
122     assert(e2.has_value());
123     assert(e1.has_value());
124   }
125 
126   // move TailClobbererNonTrivialMove as error
127   {
128     std::expected<bool, TailClobbererNonTrivialMove<1>> e1(std::unexpect);
129     auto e2 = std::move(e1);
130     assert(!e2.has_value());
131     assert(!e1.has_value());
132   }
133 
134   return true;
135 }
136 
137 void testException() {
138 #ifndef TEST_HAS_NO_EXCEPTIONS
139   struct Throwing {
140     Throwing() = default;
141     Throwing(Throwing&&) { throw Except{}; }
142   };
143 
144   // throw on moving value
145   {
146     std::expected<Throwing, int> e1;
147     try {
148       [[maybe_unused]] auto e2 = std::move(e1);
149       assert(false);
150     } catch (Except) {
151     }
152   }
153 
154   // throw on moving error
155   {
156     std::expected<int, Throwing> e1(std::unexpect);
157     try {
158       [[maybe_unused]] auto e2 = std::move(e1);
159       assert(false);
160     } catch (Except) {
161     }
162   }
163 
164 #endif // TEST_HAS_NO_EXCEPTIONS
165 }
166 
167 int main(int, char**) {
168   test();
169   static_assert(test());
170   testException();
171   return 0;
172 }
173