1// -*- C++ -*-
2//===----------------------------------------------------------------------===//
3//
4//                     The LLVM Compiler Infrastructure
5//
6// This file is dual licensed under the MIT and the University of Illinois Open
7// Source Licenses. See LICENSE.TXT for details.
8//
9//===----------------------------------------------------------------------===//
10
11// UNSUPPORTED: c++98, c++03, c++11, c++14
12
13// <variant>
14
15// template <class ...Types> class variant;
16
17// variant& operator=(variant&&) noexcept(see below);
18
19#include <cassert>
20#include <string>
21#include <type_traits>
22#include <utility>
23#include <variant>
24
25#include "test_macros.h"
26#include "variant_test_helpers.hpp"
27
28struct NoCopy {
29  NoCopy(const NoCopy &) = delete;
30  NoCopy &operator=(const NoCopy &) = default;
31};
32
33struct CopyOnly {
34  CopyOnly(const CopyOnly &) = default;
35  CopyOnly(CopyOnly &&) = delete;
36  CopyOnly &operator=(const CopyOnly &) = default;
37  CopyOnly &operator=(CopyOnly &&) = delete;
38};
39
40struct MoveOnly {
41  MoveOnly(const MoveOnly &) = delete;
42  MoveOnly(MoveOnly &&) = default;
43  MoveOnly &operator=(const MoveOnly &) = delete;
44  MoveOnly &operator=(MoveOnly &&) = default;
45};
46
47struct MoveOnlyNT {
48  MoveOnlyNT(const MoveOnlyNT &) = delete;
49  MoveOnlyNT(MoveOnlyNT &&) {}
50  MoveOnlyNT &operator=(const MoveOnlyNT &) = delete;
51  MoveOnlyNT &operator=(MoveOnlyNT &&) = default;
52};
53
54struct MoveOnlyOddNothrow {
55  MoveOnlyOddNothrow(MoveOnlyOddNothrow &&) noexcept(false) {}
56  MoveOnlyOddNothrow(const MoveOnlyOddNothrow &) = delete;
57  MoveOnlyOddNothrow &operator=(MoveOnlyOddNothrow &&) noexcept = default;
58  MoveOnlyOddNothrow &operator=(const MoveOnlyOddNothrow &) = delete;
59};
60
61struct MoveAssignOnly {
62  MoveAssignOnly(MoveAssignOnly &&) = delete;
63  MoveAssignOnly &operator=(MoveAssignOnly &&) = default;
64};
65
66struct MoveAssign {
67  static int move_construct;
68  static int move_assign;
69  static void reset() { move_construct = move_assign = 0; }
70  MoveAssign(int v) : value(v) {}
71  MoveAssign(MoveAssign &&o) : value(o.value) {
72    ++move_construct;
73    o.value = -1;
74  }
75  MoveAssign &operator=(MoveAssign &&o) {
76    value = o.value;
77    ++move_assign;
78    o.value = -1;
79    return *this;
80  }
81  int value;
82};
83
84int MoveAssign::move_construct = 0;
85int MoveAssign::move_assign = 0;
86
87void test_move_assignment_noexcept() {
88  {
89    using V = std::variant<int>;
90    static_assert(std::is_nothrow_move_assignable<V>::value, "");
91  }
92  {
93    using V = std::variant<MoveOnly>;
94    static_assert(std::is_nothrow_move_assignable<V>::value, "");
95  }
96  {
97    using V = std::variant<int, long>;
98    static_assert(std::is_nothrow_move_assignable<V>::value, "");
99  }
100  {
101    using V = std::variant<int, MoveOnly>;
102    static_assert(std::is_nothrow_move_assignable<V>::value, "");
103  }
104  {
105    using V = std::variant<MoveOnlyNT>;
106    static_assert(!std::is_nothrow_move_assignable<V>::value, "");
107  }
108  {
109    using V = std::variant<MoveOnlyOddNothrow>;
110    static_assert(!std::is_nothrow_move_assignable<V>::value, "");
111  }
112}
113
114void test_move_assignment_sfinae() {
115  {
116    using V = std::variant<int, long>;
117    static_assert(std::is_move_assignable<V>::value, "");
118  }
119  {
120    // variant only provides move assignment when both the move constructor
121    // and move assignment operator are well formed.
122    using V = std::variant<int, CopyOnly>;
123    static_assert(!std::is_move_assignable<V>::value, "");
124  }
125  {
126    using V = std::variant<int, NoCopy>;
127    static_assert(!std::is_move_assignable<V>::value, "");
128  }
129  {
130    using V = std::variant<int, MoveOnly>;
131    static_assert(std::is_move_assignable<V>::value, "");
132  }
133  {
134    using V = std::variant<int, MoveOnlyNT>;
135    static_assert(std::is_move_assignable<V>::value, "");
136  }
137  {
138    // variant only provides move assignment when the types also provide
139    // a move constructor.
140    using V = std::variant<int, MoveAssignOnly>;
141    static_assert(!std::is_move_assignable<V>::value, "");
142  }
143}
144
145void test_move_assignment_empty_empty() {
146#ifndef TEST_HAS_NO_EXCEPTIONS
147  using MET = MakeEmptyT;
148  {
149    using V = std::variant<int, long, MET>;
150    V v1(std::in_place_index<0>);
151    makeEmpty(v1);
152    V v2(std::in_place_index<0>);
153    makeEmpty(v2);
154    V &vref = (v1 = std::move(v2));
155    assert(&vref == &v1);
156    assert(v1.valueless_by_exception());
157    assert(v1.index() == std::variant_npos);
158  }
159#endif
160}
161
162void test_move_assignment_non_empty_empty() {
163#ifndef TEST_HAS_NO_EXCEPTIONS
164  using MET = MakeEmptyT;
165  {
166    using V = std::variant<int, MET>;
167    V v1(std::in_place_index<0>, 42);
168    V v2(std::in_place_index<0>);
169    makeEmpty(v2);
170    V &vref = (v1 = std::move(v2));
171    assert(&vref == &v1);
172    assert(v1.valueless_by_exception());
173    assert(v1.index() == std::variant_npos);
174  }
175  {
176    using V = std::variant<int, MET, std::string>;
177    V v1(std::in_place_index<2>, "hello");
178    V v2(std::in_place_index<0>);
179    makeEmpty(v2);
180    V &vref = (v1 = std::move(v2));
181    assert(&vref == &v1);
182    assert(v1.valueless_by_exception());
183    assert(v1.index() == std::variant_npos);
184  }
185#endif
186}
187
188void test_move_assignment_empty_non_empty() {
189#ifndef TEST_HAS_NO_EXCEPTIONS
190  using MET = MakeEmptyT;
191  {
192    using V = std::variant<int, MET>;
193    V v1(std::in_place_index<0>);
194    makeEmpty(v1);
195    V v2(std::in_place_index<0>, 42);
196    V &vref = (v1 = std::move(v2));
197    assert(&vref == &v1);
198    assert(v1.index() == 0);
199    assert(std::get<0>(v1) == 42);
200  }
201  {
202    using V = std::variant<int, MET, std::string>;
203    V v1(std::in_place_index<0>);
204    makeEmpty(v1);
205    V v2(std::in_place_type<std::string>, "hello");
206    V &vref = (v1 = std::move(v2));
207    assert(&vref == &v1);
208    assert(v1.index() == 2);
209    assert(std::get<2>(v1) == "hello");
210  }
211#endif
212}
213
214void test_move_assignment_same_index() {
215  {
216    using V = std::variant<int>;
217    V v1(43);
218    V v2(42);
219    V &vref = (v1 = std::move(v2));
220    assert(&vref == &v1);
221    assert(v1.index() == 0);
222    assert(std::get<0>(v1) == 42);
223  }
224  {
225    using V = std::variant<int, long, unsigned>;
226    V v1(43l);
227    V v2(42l);
228    V &vref = (v1 = std::move(v2));
229    assert(&vref == &v1);
230    assert(v1.index() == 1);
231    assert(std::get<1>(v1) == 42);
232  }
233  {
234    using V = std::variant<int, MoveAssign, unsigned>;
235    V v1(std::in_place_type<MoveAssign>, 43);
236    V v2(std::in_place_type<MoveAssign>, 42);
237    MoveAssign::reset();
238    V &vref = (v1 = std::move(v2));
239    assert(&vref == &v1);
240    assert(v1.index() == 1);
241    assert(std::get<1>(v1).value == 42);
242    assert(MoveAssign::move_construct == 0);
243    assert(MoveAssign::move_assign == 1);
244  }
245#ifndef TEST_HAS_NO_EXCEPTIONS
246  using MET = MakeEmptyT;
247  {
248    using V = std::variant<int, MET, std::string>;
249    V v1(std::in_place_type<MET>);
250    MET &mref = std::get<1>(v1);
251    V v2(std::in_place_type<MET>);
252    try {
253      v1 = std::move(v2);
254      assert(false);
255    } catch (...) {
256    }
257    assert(v1.index() == 1);
258    assert(&std::get<1>(v1) == &mref);
259  }
260#endif
261}
262
263void test_move_assignment_different_index() {
264  {
265    using V = std::variant<int, long, unsigned>;
266    V v1(43);
267    V v2(42l);
268    V &vref = (v1 = std::move(v2));
269    assert(&vref == &v1);
270    assert(v1.index() == 1);
271    assert(std::get<1>(v1) == 42);
272  }
273  {
274    using V = std::variant<int, MoveAssign, unsigned>;
275    V v1(std::in_place_type<unsigned>, 43);
276    V v2(std::in_place_type<MoveAssign>, 42);
277    MoveAssign::reset();
278    V &vref = (v1 = std::move(v2));
279    assert(&vref == &v1);
280    assert(v1.index() == 1);
281    assert(std::get<1>(v1).value == 42);
282    assert(MoveAssign::move_construct == 1);
283    assert(MoveAssign::move_assign == 0);
284  }
285#ifndef TEST_HAS_NO_EXCEPTIONS
286  using MET = MakeEmptyT;
287  {
288    using V = std::variant<int, MET, std::string>;
289    V v1(std::in_place_type<int>);
290    V v2(std::in_place_type<MET>);
291    try {
292      v1 = std::move(v2);
293      assert(false);
294    } catch (...) {
295    }
296    assert(v1.valueless_by_exception());
297    assert(v1.index() == std::variant_npos);
298  }
299  {
300    using V = std::variant<int, MET, std::string>;
301    V v1(std::in_place_type<MET>);
302    V v2(std::in_place_type<std::string>, "hello");
303    V &vref = (v1 = std::move(v2));
304    assert(&vref == &v1);
305    assert(v1.index() == 2);
306    assert(std::get<2>(v1) == "hello");
307  }
308#endif
309}
310
311int main() {
312  test_move_assignment_empty_empty();
313  test_move_assignment_non_empty_empty();
314  test_move_assignment_empty_non_empty();
315  test_move_assignment_same_index();
316  test_move_assignment_different_index();
317  test_move_assignment_sfinae();
318  test_move_assignment_noexcept();
319}
320