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// The following compilers don't generate constexpr special members correctly.
14// XFAIL: clang-3.5, clang-3.6, clang-3.7, clang-3.8
15// XFAIL: apple-clang-6, apple-clang-7, apple-clang-8.0
16
17// XFAIL: with_system_cxx_lib=macosx10.12
18// XFAIL: with_system_cxx_lib=macosx10.11
19// XFAIL: with_system_cxx_lib=macosx10.10
20// XFAIL: with_system_cxx_lib=macosx10.9
21// XFAIL: with_system_cxx_lib=macosx10.7
22// XFAIL: with_system_cxx_lib=macosx10.8
23
24// <variant>
25
26// template <class ...Types> class variant;
27
28// variant& operator=(variant&&) noexcept(see below);
29
30#include <cassert>
31#include <string>
32#include <type_traits>
33#include <utility>
34#include <variant>
35
36#include "test_macros.h"
37#include "variant_test_helpers.hpp"
38
39struct NoCopy {
40  NoCopy(const NoCopy &) = delete;
41  NoCopy &operator=(const NoCopy &) = default;
42};
43
44struct CopyOnly {
45  CopyOnly(const CopyOnly &) = default;
46  CopyOnly(CopyOnly &&) = delete;
47  CopyOnly &operator=(const CopyOnly &) = default;
48  CopyOnly &operator=(CopyOnly &&) = delete;
49};
50
51struct MoveOnly {
52  MoveOnly(const MoveOnly &) = delete;
53  MoveOnly(MoveOnly &&) = default;
54  MoveOnly &operator=(const MoveOnly &) = delete;
55  MoveOnly &operator=(MoveOnly &&) = default;
56};
57
58struct MoveOnlyNT {
59  MoveOnlyNT(const MoveOnlyNT &) = delete;
60  MoveOnlyNT(MoveOnlyNT &&) {}
61  MoveOnlyNT &operator=(const MoveOnlyNT &) = delete;
62  MoveOnlyNT &operator=(MoveOnlyNT &&) = default;
63};
64
65struct MoveOnlyOddNothrow {
66  MoveOnlyOddNothrow(MoveOnlyOddNothrow &&) noexcept(false) {}
67  MoveOnlyOddNothrow(const MoveOnlyOddNothrow &) = delete;
68  MoveOnlyOddNothrow &operator=(MoveOnlyOddNothrow &&) noexcept = default;
69  MoveOnlyOddNothrow &operator=(const MoveOnlyOddNothrow &) = delete;
70};
71
72struct MoveAssignOnly {
73  MoveAssignOnly(MoveAssignOnly &&) = delete;
74  MoveAssignOnly &operator=(MoveAssignOnly &&) = default;
75};
76
77struct MoveAssign {
78  static int move_construct;
79  static int move_assign;
80  static void reset() { move_construct = move_assign = 0; }
81  MoveAssign(int v) : value(v) {}
82  MoveAssign(MoveAssign &&o) : value(o.value) {
83    ++move_construct;
84    o.value = -1;
85  }
86  MoveAssign &operator=(MoveAssign &&o) {
87    value = o.value;
88    ++move_assign;
89    o.value = -1;
90    return *this;
91  }
92  int value;
93};
94
95int MoveAssign::move_construct = 0;
96int MoveAssign::move_assign = 0;
97
98struct NTMoveAssign {
99  constexpr NTMoveAssign(int v) : value(v) {}
100  NTMoveAssign(const NTMoveAssign &) = default;
101  NTMoveAssign(NTMoveAssign &&) = default;
102  NTMoveAssign &operator=(const NTMoveAssign &that) = default;
103  NTMoveAssign &operator=(NTMoveAssign &&that) {
104    value = that.value;
105    that.value = -1;
106    return *this;
107  };
108  int value;
109};
110
111static_assert(!std::is_trivially_move_assignable<NTMoveAssign>::value, "");
112static_assert(std::is_move_assignable<NTMoveAssign>::value, "");
113
114struct TMoveAssign {
115  constexpr TMoveAssign(int v) : value(v) {}
116  TMoveAssign(const TMoveAssign &) = delete;
117  TMoveAssign(TMoveAssign &&) = default;
118  TMoveAssign &operator=(const TMoveAssign &) = delete;
119  TMoveAssign &operator=(TMoveAssign &&) = default;
120  int value;
121};
122
123static_assert(std::is_trivially_move_assignable<TMoveAssign>::value, "");
124
125struct TMoveAssignNTCopyAssign {
126  constexpr TMoveAssignNTCopyAssign(int v) : value(v) {}
127  TMoveAssignNTCopyAssign(const TMoveAssignNTCopyAssign &) = default;
128  TMoveAssignNTCopyAssign(TMoveAssignNTCopyAssign &&) = default;
129  TMoveAssignNTCopyAssign &operator=(const TMoveAssignNTCopyAssign &that) {
130    value = that.value;
131    return *this;
132  }
133  TMoveAssignNTCopyAssign &operator=(TMoveAssignNTCopyAssign &&) = default;
134  int value;
135};
136
137static_assert(std::is_trivially_move_assignable_v<TMoveAssignNTCopyAssign>, "");
138
139struct TrivialCopyNontrivialMove {
140  TrivialCopyNontrivialMove(TrivialCopyNontrivialMove const&) = default;
141  TrivialCopyNontrivialMove(TrivialCopyNontrivialMove&&) noexcept {}
142  TrivialCopyNontrivialMove& operator=(TrivialCopyNontrivialMove const&) = default;
143  TrivialCopyNontrivialMove& operator=(TrivialCopyNontrivialMove&&) noexcept {
144    return *this;
145  }
146};
147
148static_assert(std::is_trivially_copy_assignable_v<TrivialCopyNontrivialMove>, "");
149static_assert(!std::is_trivially_move_assignable_v<TrivialCopyNontrivialMove>, "");
150
151
152void test_move_assignment_noexcept() {
153  {
154    using V = std::variant<int>;
155    static_assert(std::is_nothrow_move_assignable<V>::value, "");
156  }
157  {
158    using V = std::variant<MoveOnly>;
159    static_assert(std::is_nothrow_move_assignable<V>::value, "");
160  }
161  {
162    using V = std::variant<int, long>;
163    static_assert(std::is_nothrow_move_assignable<V>::value, "");
164  }
165  {
166    using V = std::variant<int, MoveOnly>;
167    static_assert(std::is_nothrow_move_assignable<V>::value, "");
168  }
169  {
170    using V = std::variant<MoveOnlyNT>;
171    static_assert(!std::is_nothrow_move_assignable<V>::value, "");
172  }
173  {
174    using V = std::variant<MoveOnlyOddNothrow>;
175    static_assert(!std::is_nothrow_move_assignable<V>::value, "");
176  }
177}
178
179void test_move_assignment_sfinae() {
180  {
181    using V = std::variant<int, long>;
182    static_assert(std::is_move_assignable<V>::value, "");
183  }
184  {
185    using V = std::variant<int, CopyOnly>;
186    static_assert(std::is_move_assignable<V>::value, "");
187  }
188  {
189    using V = std::variant<int, NoCopy>;
190    static_assert(!std::is_move_assignable<V>::value, "");
191  }
192  {
193    using V = std::variant<int, MoveOnly>;
194    static_assert(std::is_move_assignable<V>::value, "");
195  }
196  {
197    using V = std::variant<int, MoveOnlyNT>;
198    static_assert(std::is_move_assignable<V>::value, "");
199  }
200  {
201    // variant only provides move assignment when the types also provide
202    // a move constructor.
203    using V = std::variant<int, MoveAssignOnly>;
204    static_assert(!std::is_move_assignable<V>::value, "");
205  }
206
207  // The following tests are for not-yet-standardized behavior (P0602):
208  {
209    using V = std::variant<int, long>;
210    static_assert(std::is_trivially_move_assignable<V>::value, "");
211  }
212  {
213    using V = std::variant<int, NTMoveAssign>;
214    static_assert(!std::is_trivially_move_assignable<V>::value, "");
215    static_assert(std::is_move_assignable<V>::value, "");
216  }
217  {
218    using V = std::variant<int, TMoveAssign>;
219    static_assert(std::is_trivially_move_assignable<V>::value, "");
220  }
221  {
222    using V = std::variant<int, TMoveAssignNTCopyAssign>;
223    static_assert(std::is_trivially_move_assignable<V>::value, "");
224  }
225  {
226    using V = std::variant<int, TrivialCopyNontrivialMove>;
227    static_assert(!std::is_trivially_move_assignable<V>::value, "");
228  }
229  {
230    using V = std::variant<int, CopyOnly>;
231    static_assert(std::is_trivially_move_assignable<V>::value, "");
232  }
233}
234
235void test_move_assignment_empty_empty() {
236#ifndef TEST_HAS_NO_EXCEPTIONS
237  using MET = MakeEmptyT;
238  {
239    using V = std::variant<int, long, MET>;
240    V v1(std::in_place_index<0>);
241    makeEmpty(v1);
242    V v2(std::in_place_index<0>);
243    makeEmpty(v2);
244    V &vref = (v1 = std::move(v2));
245    assert(&vref == &v1);
246    assert(v1.valueless_by_exception());
247    assert(v1.index() == std::variant_npos);
248  }
249#endif // TEST_HAS_NO_EXCEPTIONS
250}
251
252void test_move_assignment_non_empty_empty() {
253#ifndef TEST_HAS_NO_EXCEPTIONS
254  using MET = MakeEmptyT;
255  {
256    using V = std::variant<int, MET>;
257    V v1(std::in_place_index<0>, 42);
258    V v2(std::in_place_index<0>);
259    makeEmpty(v2);
260    V &vref = (v1 = std::move(v2));
261    assert(&vref == &v1);
262    assert(v1.valueless_by_exception());
263    assert(v1.index() == std::variant_npos);
264  }
265  {
266    using V = std::variant<int, MET, std::string>;
267    V v1(std::in_place_index<2>, "hello");
268    V v2(std::in_place_index<0>);
269    makeEmpty(v2);
270    V &vref = (v1 = std::move(v2));
271    assert(&vref == &v1);
272    assert(v1.valueless_by_exception());
273    assert(v1.index() == std::variant_npos);
274  }
275#endif // TEST_HAS_NO_EXCEPTIONS
276}
277
278void test_move_assignment_empty_non_empty() {
279#ifndef TEST_HAS_NO_EXCEPTIONS
280  using MET = MakeEmptyT;
281  {
282    using V = std::variant<int, MET>;
283    V v1(std::in_place_index<0>);
284    makeEmpty(v1);
285    V v2(std::in_place_index<0>, 42);
286    V &vref = (v1 = std::move(v2));
287    assert(&vref == &v1);
288    assert(v1.index() == 0);
289    assert(std::get<0>(v1) == 42);
290  }
291  {
292    using V = std::variant<int, MET, std::string>;
293    V v1(std::in_place_index<0>);
294    makeEmpty(v1);
295    V v2(std::in_place_type<std::string>, "hello");
296    V &vref = (v1 = std::move(v2));
297    assert(&vref == &v1);
298    assert(v1.index() == 2);
299    assert(std::get<2>(v1) == "hello");
300  }
301#endif // TEST_HAS_NO_EXCEPTIONS
302}
303
304template <typename T> struct Result { size_t index; T value; };
305
306void test_move_assignment_same_index() {
307  {
308    using V = std::variant<int>;
309    V v1(43);
310    V v2(42);
311    V &vref = (v1 = std::move(v2));
312    assert(&vref == &v1);
313    assert(v1.index() == 0);
314    assert(std::get<0>(v1) == 42);
315  }
316  {
317    using V = std::variant<int, long, unsigned>;
318    V v1(43l);
319    V v2(42l);
320    V &vref = (v1 = std::move(v2));
321    assert(&vref == &v1);
322    assert(v1.index() == 1);
323    assert(std::get<1>(v1) == 42);
324  }
325  {
326    using V = std::variant<int, MoveAssign, unsigned>;
327    V v1(std::in_place_type<MoveAssign>, 43);
328    V v2(std::in_place_type<MoveAssign>, 42);
329    MoveAssign::reset();
330    V &vref = (v1 = std::move(v2));
331    assert(&vref == &v1);
332    assert(v1.index() == 1);
333    assert(std::get<1>(v1).value == 42);
334    assert(MoveAssign::move_construct == 0);
335    assert(MoveAssign::move_assign == 1);
336  }
337#ifndef TEST_HAS_NO_EXCEPTIONS
338  using MET = MakeEmptyT;
339  {
340    using V = std::variant<int, MET, std::string>;
341    V v1(std::in_place_type<MET>);
342    MET &mref = std::get<1>(v1);
343    V v2(std::in_place_type<MET>);
344    try {
345      v1 = std::move(v2);
346      assert(false);
347    } catch (...) {
348    }
349    assert(v1.index() == 1);
350    assert(&std::get<1>(v1) == &mref);
351  }
352#endif // TEST_HAS_NO_EXCEPTIONS
353
354  // The following tests are for not-yet-standardized behavior (P0602):
355  {
356    struct {
357      constexpr Result<int> operator()() const {
358        using V = std::variant<int>;
359        V v(43);
360        V v2(42);
361        v = std::move(v2);
362        return {v.index(), std::get<0>(v)};
363      }
364    } test;
365    constexpr auto result = test();
366    static_assert(result.index == 0, "");
367    static_assert(result.value == 42, "");
368  }
369  {
370    struct {
371      constexpr Result<long> operator()() const {
372        using V = std::variant<int, long, unsigned>;
373        V v(43l);
374        V v2(42l);
375        v = std::move(v2);
376        return {v.index(), std::get<1>(v)};
377      }
378    } test;
379    constexpr auto result = test();
380    static_assert(result.index == 1, "");
381    static_assert(result.value == 42l, "");
382  }
383  {
384    struct {
385      constexpr Result<int> operator()() const {
386        using V = std::variant<int, TMoveAssign, unsigned>;
387        V v(std::in_place_type<TMoveAssign>, 43);
388        V v2(std::in_place_type<TMoveAssign>, 42);
389        v = std::move(v2);
390        return {v.index(), std::get<1>(v).value};
391      }
392    } test;
393    constexpr auto result = test();
394    static_assert(result.index == 1, "");
395    static_assert(result.value == 42, "");
396  }
397}
398
399void test_move_assignment_different_index() {
400  {
401    using V = std::variant<int, long, unsigned>;
402    V v1(43);
403    V v2(42l);
404    V &vref = (v1 = std::move(v2));
405    assert(&vref == &v1);
406    assert(v1.index() == 1);
407    assert(std::get<1>(v1) == 42);
408  }
409  {
410    using V = std::variant<int, MoveAssign, unsigned>;
411    V v1(std::in_place_type<unsigned>, 43);
412    V v2(std::in_place_type<MoveAssign>, 42);
413    MoveAssign::reset();
414    V &vref = (v1 = std::move(v2));
415    assert(&vref == &v1);
416    assert(v1.index() == 1);
417    assert(std::get<1>(v1).value == 42);
418    assert(MoveAssign::move_construct == 1);
419    assert(MoveAssign::move_assign == 0);
420  }
421#ifndef TEST_HAS_NO_EXCEPTIONS
422  using MET = MakeEmptyT;
423  {
424    using V = std::variant<int, MET, std::string>;
425    V v1(std::in_place_type<int>);
426    V v2(std::in_place_type<MET>);
427    try {
428      v1 = std::move(v2);
429      assert(false);
430    } catch (...) {
431    }
432    assert(v1.valueless_by_exception());
433    assert(v1.index() == std::variant_npos);
434  }
435  {
436    using V = std::variant<int, MET, std::string>;
437    V v1(std::in_place_type<MET>);
438    V v2(std::in_place_type<std::string>, "hello");
439    V &vref = (v1 = std::move(v2));
440    assert(&vref == &v1);
441    assert(v1.index() == 2);
442    assert(std::get<2>(v1) == "hello");
443  }
444#endif // TEST_HAS_NO_EXCEPTIONS
445
446  // The following tests are for not-yet-standardized behavior (P0602):
447  {
448    struct {
449      constexpr Result<long> operator()() const {
450        using V = std::variant<int, long, unsigned>;
451        V v(43);
452        V v2(42l);
453        v = std::move(v2);
454        return {v.index(), std::get<1>(v)};
455      }
456    } test;
457    constexpr auto result = test();
458    static_assert(result.index == 1, "");
459    static_assert(result.value == 42l, "");
460  }
461  {
462    struct {
463      constexpr Result<long> operator()() const {
464        using V = std::variant<int, TMoveAssign, unsigned>;
465        V v(std::in_place_type<unsigned>, 43);
466        V v2(std::in_place_type<TMoveAssign>, 42);
467        v = std::move(v2);
468        return {v.index(), std::get<1>(v).value};
469      }
470    } test;
471    constexpr auto result = test();
472    static_assert(result.index == 1, "");
473    static_assert(result.value == 42, "");
474  }
475}
476
477template <size_t NewIdx, class ValueType>
478constexpr bool test_constexpr_assign_extension_imp(
479    std::variant<long, void*, int>&& v, ValueType&& new_value)
480{
481  std::variant<long, void*, int> v2(
482      std::forward<ValueType>(new_value));
483  const auto cp = v2;
484  v = std::move(v2);
485  return v.index() == NewIdx &&
486        std::get<NewIdx>(v) == std::get<NewIdx>(cp);
487}
488
489void test_constexpr_move_assignment_extension() {
490  // The following tests are for not-yet-standardized behavior (P0602):
491  using V = std::variant<long, void*, int>;
492  static_assert(std::is_trivially_copyable<V>::value, "");
493  static_assert(std::is_trivially_move_assignable<V>::value, "");
494  static_assert(test_constexpr_assign_extension_imp<0>(V(42l), 101l), "");
495  static_assert(test_constexpr_assign_extension_imp<0>(V(nullptr), 101l), "");
496  static_assert(test_constexpr_assign_extension_imp<1>(V(42l), nullptr), "");
497  static_assert(test_constexpr_assign_extension_imp<2>(V(42l), 101), "");
498}
499
500int main() {
501  test_move_assignment_empty_empty();
502  test_move_assignment_non_empty_empty();
503  test_move_assignment_empty_non_empty();
504  test_move_assignment_same_index();
505  test_move_assignment_different_index();
506  test_move_assignment_sfinae();
507  test_move_assignment_noexcept();
508  test_constexpr_move_assignment_extension();
509}
510