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 |
10 | |
11 | // <any> |
12 | |
13 | // template <class ValueType> |
14 | // any& operator=(ValueType&&); |
15 | |
16 | // Test value copy and move assignment. |
17 | |
18 | #include <any> |
19 | #include <cassert> |
20 | |
21 | #include "any_helpers.h" |
22 | #include "count_new.h" |
23 | #include "test_macros.h" |
24 | |
25 | template <class LHS, class RHS> |
26 | void test_assign_value() { |
27 | assert(LHS::count == 0); |
28 | assert(RHS::count == 0); |
29 | LHS::reset(); |
30 | RHS::reset(); |
31 | { |
32 | std::any lhs = LHS(1); |
33 | const std::any rhs = RHS(2); |
34 | |
35 | assert(LHS::count == 1); |
36 | assert(RHS::count == 1); |
37 | assert(RHS::copied == 0); |
38 | |
39 | lhs = rhs; |
40 | |
41 | assert(RHS::copied == 1); |
42 | assert(LHS::count == 0); |
43 | assert(RHS::count == 2); |
44 | |
45 | assertContains<RHS>(lhs, 2); |
46 | assertContains<RHS>(rhs, 2); |
47 | } |
48 | assert(LHS::count == 0); |
49 | assert(RHS::count == 0); |
50 | LHS::reset(); |
51 | RHS::reset(); |
52 | { |
53 | std::any lhs = LHS(1); |
54 | std::any rhs = RHS(2); |
55 | |
56 | assert(LHS::count == 1); |
57 | assert(RHS::count == 1); |
58 | assert(RHS::moved == 1); |
59 | |
60 | lhs = std::move(rhs); |
61 | |
62 | assert(RHS::moved >= 1); |
63 | assert(RHS::copied == 0); |
64 | assert(LHS::count == 0); |
65 | assert(RHS::count == 1 + rhs.has_value()); |
66 | LIBCPP_ASSERT(!rhs.has_value()); |
67 | |
68 | assertContains<RHS>(lhs, 2); |
69 | if (rhs.has_value()) |
70 | assertContains<RHS>(rhs, 0); |
71 | } |
72 | assert(LHS::count == 0); |
73 | assert(RHS::count == 0); |
74 | } |
75 | |
76 | template <class RHS> |
77 | void test_assign_value_empty() { |
78 | assert(RHS::count == 0); |
79 | RHS::reset(); |
80 | { |
81 | std::any lhs; |
82 | RHS rhs(42); |
83 | assert(RHS::count == 1); |
84 | assert(RHS::copied == 0); |
85 | |
86 | lhs = rhs; |
87 | |
88 | assert(RHS::count == 2); |
89 | assert(RHS::copied == 1); |
90 | assert(RHS::moved >= 0); |
91 | assertContains<RHS>(lhs, 42); |
92 | } |
93 | assert(RHS::count == 0); |
94 | RHS::reset(); |
95 | { |
96 | std::any lhs; |
97 | RHS rhs(42); |
98 | assert(RHS::count == 1); |
99 | assert(RHS::moved == 0); |
100 | |
101 | lhs = std::move(rhs); |
102 | |
103 | assert(RHS::count == 2); |
104 | assert(RHS::copied == 0); |
105 | assert(RHS::moved >= 1); |
106 | assertContains<RHS>(lhs, 42); |
107 | } |
108 | assert(RHS::count == 0); |
109 | RHS::reset(); |
110 | } |
111 | |
112 | |
113 | template <class Tp, bool Move = false> |
114 | void test_assign_throws() { |
115 | #if !defined(TEST_HAS_NO_EXCEPTIONS) |
116 | auto try_throw = |
117 | [](std::any& lhs, Tp& rhs) { |
118 | try { |
119 | Move ? lhs = std::move(rhs) |
120 | : lhs = rhs; |
121 | assert(false); |
122 | } catch (const my_any_exception&) { |
123 | // do nothing |
124 | } catch (...) { |
125 | assert(false); |
126 | } |
127 | }; |
128 | // const lvalue to empty |
129 | { |
130 | std::any lhs; |
131 | Tp rhs(1); |
132 | assert(Tp::count == 1); |
133 | |
134 | try_throw(lhs, rhs); |
135 | |
136 | assert(Tp::count == 1); |
137 | assertEmpty<Tp>(lhs); |
138 | } |
139 | { |
140 | std::any lhs = small(2); |
141 | Tp rhs(1); |
142 | assert(small::count == 1); |
143 | assert(Tp::count == 1); |
144 | |
145 | try_throw(lhs, rhs); |
146 | |
147 | assert(small::count == 1); |
148 | assert(Tp::count == 1); |
149 | assertContains<small>(lhs, 2); |
150 | } |
151 | { |
152 | std::any lhs = large(2); |
153 | Tp rhs(1); |
154 | assert(large::count == 1); |
155 | assert(Tp::count == 1); |
156 | |
157 | try_throw(lhs, rhs); |
158 | |
159 | assert(large::count == 1); |
160 | assert(Tp::count == 1); |
161 | assertContains<large>(lhs, 2); |
162 | } |
163 | #endif |
164 | } |
165 | |
166 | |
167 | // Test that any& operator=(ValueType&&) is *never* selected for: |
168 | // * std::in_place type. |
169 | // * Non-copyable types |
170 | void test_sfinae_constraints() { |
171 | { // Only the constructors are required to SFINAE on in_place_t |
172 | using Tag = std::in_place_type_t<int>; |
173 | using RawTag = std::remove_reference_t<Tag>; |
174 | static_assert(std::is_assignable<std::any, RawTag&&>::value, "" ); |
175 | } |
176 | { |
177 | struct Dummy { Dummy() = delete; }; |
178 | using T = std::in_place_type_t<Dummy>; |
179 | static_assert(std::is_assignable<std::any, T>::value, "" ); |
180 | } |
181 | { |
182 | // Test that the ValueType&& constructor SFINAE's away when the |
183 | // argument is non-copyable |
184 | struct NoCopy { |
185 | NoCopy() = default; |
186 | NoCopy(NoCopy const&) = delete; |
187 | NoCopy(NoCopy&&) = default; |
188 | }; |
189 | static_assert(!std::is_assignable<std::any, NoCopy>::value, "" ); |
190 | static_assert(!std::is_assignable<std::any, NoCopy&>::value, "" ); |
191 | } |
192 | } |
193 | |
194 | int main(int, char**) { |
195 | test_assign_value<small1, small2>(); |
196 | test_assign_value<large1, large2>(); |
197 | test_assign_value<small, large>(); |
198 | test_assign_value<large, small>(); |
199 | test_assign_value_empty<small>(); |
200 | test_assign_value_empty<large>(); |
201 | test_assign_throws<small_throws_on_copy>(); |
202 | test_assign_throws<large_throws_on_copy>(); |
203 | test_assign_throws<throws_on_move, /* Move = */ true>(); |
204 | test_sfinae_constraints(); |
205 | |
206 | return 0; |
207 | } |
208 | |