• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
10 
11 // <copyable-box>& operator=(<copyable-box>&&)
12 
13 // ADDITIONAL_COMPILE_FLAGS: -Wno-self-move
14 
15 #include <ranges>
16 
17 #include <cassert>
18 #include <type_traits>
19 #include <utility> // in_place_t
20 
21 #include "test_macros.h"
22 #include "types.h"
23 
test()24 constexpr bool test() {
25   // Test the primary template
26   {
27     using Box = std::ranges::__copyable_box<CopyConstructible>;
28     static_assert( std::is_move_assignable_v<Box>);
29     static_assert(!std::is_nothrow_move_assignable_v<Box>);
30 
31     {
32       Box x(std::in_place, 5);
33       Box y(std::in_place, 10);
34       Box& result = (x = std::move(y));
35 
36       assert(&result == &x);
37       assert(x.__has_value());
38       assert(y.__has_value());
39       assert((*x).value == 10);
40     }
41     // check self-assignment
42     {
43       Box x(std::in_place, 5);
44       Box& result = (x = std::move(x));
45 
46       assert(&result == &x);
47       assert(x.__has_value());
48       assert((*x).value == 5);
49     }
50   }
51 
52   // Make sure that we use the native move assignment in the primary template if we can.
53   {
54     using Box = std::ranges::__copyable_box<CopyConstructibleMovable>;
55     static_assert(std::is_move_assignable_v<Box>);
56     static_assert(std::is_nothrow_move_assignable_v<Box> == std::is_nothrow_move_assignable_v<CopyConstructibleMovable>);
57 
58     {
59       Box x(std::in_place, 5);
60       Box y(std::in_place, 10);
61       Box& result = (x = std::move(y));
62 
63       assert(&result == &x);
64       assert(x.__has_value());
65       assert(y.__has_value());
66       assert((*x).value == 10);
67       assert((*x).did_move_assign);
68     }
69     // check self-assignment
70     {
71       Box x(std::in_place, 5);
72       Box& result = (x = std::move(x));
73 
74       assert(&result == &x);
75       assert(x.__has_value());
76       assert((*x).value == 5);
77       assert((*x).did_move_assign);
78     }
79   }
80 
81   // Test optimization #1 for move assignment
82   {
83     using Box = std::ranges::__copyable_box<Copyable>;
84     static_assert( std::is_move_assignable_v<Box>);
85     static_assert(!std::is_nothrow_move_assignable_v<Box>);
86 
87     {
88       Box x(std::in_place, 5);
89       Box y(std::in_place, 10);
90       Box& result = (x = std::move(y));
91 
92       assert(&result == &x);
93       assert(x.__has_value());
94       assert(y.__has_value());
95       assert((*x).value == 10);
96       assert((*x).did_move_assign);
97     }
98     // check self-assignment (should use the underlying type's assignment too)
99     {
100       Box x(std::in_place, 5);
101       Box& result = (x = std::move(x));
102 
103       assert(&result == &x);
104       assert(x.__has_value());
105       assert((*x).value == 5);
106       assert((*x).did_move_assign);
107     }
108   }
109 
110   // Test optimization #1 for move assignment with a type that uses optimization #2 for copy assignment
111   {
112     using Box = std::ranges::__copyable_box<MovableNothrowCopyConstructible>;
113     static_assert(std::is_move_assignable_v<Box>);
114     static_assert(std::is_nothrow_move_assignable_v<Box> == std::is_nothrow_move_assignable_v<MovableNothrowCopyConstructible>);
115 
116     {
117       Box x(std::in_place, 5);
118       Box y(std::in_place, 10);
119       Box& result = (x = std::move(y));
120 
121       assert(&result == &x);
122       assert(x.__has_value());
123       assert(y.__has_value());
124       assert((*x).value == 10);
125       assert((*x).did_move_assign);
126     }
127     // check self-assignment (should use the underlying type's assignment too)
128     {
129       Box x(std::in_place, 5);
130       Box& result = (x = std::move(x));
131 
132       assert(&result == &x);
133       assert(x.__has_value());
134       assert((*x).value == 5);
135       assert((*x).did_move_assign);
136     }
137   }
138 
139   // Test optimization #2 for move assignment
140   {
141     using Box = std::ranges::__copyable_box<NothrowCopyConstructible>;
142     static_assert(std::is_move_assignable_v<Box>);
143     static_assert(std::is_nothrow_move_assignable_v<Box>);
144 
145     {
146       Box x(std::in_place, 5);
147       Box y(std::in_place, 10);
148       Box& result = (x = std::move(y));
149 
150       assert(&result == &x);
151       assert(x.__has_value());
152       assert(y.__has_value());
153       assert((*x).value == 10);
154     }
155     // check self-assignment
156     {
157       Box x(std::in_place, 5);
158       Box& result = (x = std::move(x));
159 
160       assert(&result == &x);
161       assert(x.__has_value());
162       assert((*x).value == 5);
163     }
164   }
165 
166   return true;
167 }
168 
169 // Tests for the empty state. Those can't be constexpr, since they are only reached
170 // through throwing an exception.
171 #if !defined(TEST_HAS_NO_EXCEPTIONS)
test_empty_state()172 void test_empty_state() {
173   using Box = std::ranges::__copyable_box<ThrowsOnCopy>;
174 
175   // assign non-empty to empty
176   {
177     Box x = create_empty_box();
178     Box y(std::in_place, 10);
179     Box& result = (x = std::move(y));
180 
181     assert(&result == &x);
182     assert(x.__has_value());
183     assert(y.__has_value());
184     assert((*x).value == 10);
185   }
186   // assign empty to non-empty
187   {
188     Box x(std::in_place, 5);
189     Box y = create_empty_box();
190     Box& result = (x = std::move(y));
191 
192     assert(&result == &x);
193     assert(!x.__has_value());
194     assert(!y.__has_value());
195   }
196   // assign empty to empty
197   {
198     Box x = create_empty_box();
199     Box y = create_empty_box();
200     Box& result = (x = std::move(y));
201 
202     assert(&result == &x);
203     assert(!x.__has_value());
204     assert(!y.__has_value());
205   }
206   // check self-assignment in empty case
207   {
208     Box x = create_empty_box();
209     Box& result = (x = std::move(x));
210 
211     assert(&result == &x);
212     assert(!x.__has_value());
213   }
214 }
215 #endif // !defined(TEST_HAS_NO_EXCEPTIONS)
216 
main(int,char **)217 int main(int, char**) {
218   assert(test());
219   static_assert(test());
220 
221 #if !defined(TEST_HAS_NO_EXCEPTIONS)
222   test_empty_state();
223 #endif
224 
225   return 0;
226 }
227