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 const&);
29
30#include <cassert>
31#include <string>
32#include <type_traits>
33#include <variant>
34
35#include "test_macros.h"
36
37struct NoCopy {
38  NoCopy(const NoCopy &) = delete;
39  NoCopy &operator=(const NoCopy &) = default;
40};
41
42struct CopyOnly {
43  CopyOnly(const CopyOnly &) = default;
44  CopyOnly(CopyOnly &&) = delete;
45  CopyOnly &operator=(const CopyOnly &) = default;
46  CopyOnly &operator=(CopyOnly &&) = delete;
47};
48
49struct MoveOnly {
50  MoveOnly(const MoveOnly &) = delete;
51  MoveOnly(MoveOnly &&) = default;
52  MoveOnly &operator=(const MoveOnly &) = default;
53};
54
55struct MoveOnlyNT {
56  MoveOnlyNT(const MoveOnlyNT &) = delete;
57  MoveOnlyNT(MoveOnlyNT &&) {}
58  MoveOnlyNT &operator=(const MoveOnlyNT &) = default;
59};
60
61struct CopyAssign {
62  static int alive;
63  static int copy_construct;
64  static int copy_assign;
65  static int move_construct;
66  static int move_assign;
67  static void reset() {
68    copy_construct = copy_assign = move_construct = move_assign = alive = 0;
69  }
70  CopyAssign(int v) : value(v) { ++alive; }
71  CopyAssign(const CopyAssign &o) : value(o.value) {
72    ++alive;
73    ++copy_construct;
74  }
75  CopyAssign(CopyAssign &&o) noexcept : value(o.value) {
76    o.value = -1;
77    ++alive;
78    ++move_construct;
79  }
80  CopyAssign &operator=(const CopyAssign &o) {
81    value = o.value;
82    ++copy_assign;
83    return *this;
84  }
85  CopyAssign &operator=(CopyAssign &&o) noexcept {
86    value = o.value;
87    o.value = -1;
88    ++move_assign;
89    return *this;
90  }
91  ~CopyAssign() { --alive; }
92  int value;
93};
94
95int CopyAssign::alive = 0;
96int CopyAssign::copy_construct = 0;
97int CopyAssign::copy_assign = 0;
98int CopyAssign::move_construct = 0;
99int CopyAssign::move_assign = 0;
100
101struct CopyMaybeThrows {
102  CopyMaybeThrows(const CopyMaybeThrows &);
103  CopyMaybeThrows &operator=(const CopyMaybeThrows &);
104};
105struct CopyDoesThrow {
106  CopyDoesThrow(const CopyDoesThrow &) noexcept(false);
107  CopyDoesThrow &operator=(const CopyDoesThrow &) noexcept(false);
108};
109
110
111struct NTCopyAssign {
112  constexpr NTCopyAssign(int v) : value(v) {}
113  NTCopyAssign(const NTCopyAssign &) = default;
114  NTCopyAssign(NTCopyAssign &&) = default;
115  NTCopyAssign &operator=(const NTCopyAssign &that) {
116    value = that.value;
117    return *this;
118  };
119  NTCopyAssign &operator=(NTCopyAssign &&) = delete;
120  int value;
121};
122
123static_assert(!std::is_trivially_copy_assignable<NTCopyAssign>::value, "");
124static_assert(std::is_copy_assignable<NTCopyAssign>::value, "");
125
126struct TCopyAssign {
127  constexpr TCopyAssign(int v) : value(v) {}
128  TCopyAssign(const TCopyAssign &) = default;
129  TCopyAssign(TCopyAssign &&) = default;
130  TCopyAssign &operator=(const TCopyAssign &) = default;
131  TCopyAssign &operator=(TCopyAssign &&) = delete;
132  int value;
133};
134
135static_assert(std::is_trivially_copy_assignable<TCopyAssign>::value, "");
136
137struct TCopyAssignNTMoveAssign {
138  constexpr TCopyAssignNTMoveAssign(int v) : value(v) {}
139  TCopyAssignNTMoveAssign(const TCopyAssignNTMoveAssign &) = default;
140  TCopyAssignNTMoveAssign(TCopyAssignNTMoveAssign &&) = default;
141  TCopyAssignNTMoveAssign &operator=(const TCopyAssignNTMoveAssign &) = default;
142  TCopyAssignNTMoveAssign &operator=(TCopyAssignNTMoveAssign &&that) {
143    value = that.value;
144    that.value = -1;
145    return *this;
146  }
147  int value;
148};
149
150static_assert(std::is_trivially_copy_assignable_v<TCopyAssignNTMoveAssign>, "");
151
152#ifndef TEST_HAS_NO_EXCEPTIONS
153struct CopyThrows {
154  CopyThrows() = default;
155  CopyThrows(const CopyThrows &) { throw 42; }
156  CopyThrows &operator=(const CopyThrows &) { throw 42; }
157};
158
159struct CopyCannotThrow {
160  static int alive;
161  CopyCannotThrow() { ++alive; }
162  CopyCannotThrow(const CopyCannotThrow &) noexcept { ++alive; }
163  CopyCannotThrow(CopyCannotThrow &&) noexcept { assert(false); }
164  CopyCannotThrow &operator=(const CopyCannotThrow &) noexcept = default;
165  CopyCannotThrow &operator=(CopyCannotThrow &&) noexcept { assert(false); return *this; }
166};
167
168int CopyCannotThrow::alive = 0;
169
170struct MoveThrows {
171  static int alive;
172  MoveThrows() { ++alive; }
173  MoveThrows(const MoveThrows &) { ++alive; }
174  MoveThrows(MoveThrows &&) { throw 42; }
175  MoveThrows &operator=(const MoveThrows &) { return *this; }
176  MoveThrows &operator=(MoveThrows &&) { throw 42; }
177  ~MoveThrows() { --alive; }
178};
179
180int MoveThrows::alive = 0;
181
182struct MakeEmptyT {
183  static int alive;
184  MakeEmptyT() { ++alive; }
185  MakeEmptyT(const MakeEmptyT &) {
186    ++alive;
187    // Don't throw from the copy constructor since variant's assignment
188    // operator performs a copy before committing to the assignment.
189  }
190  MakeEmptyT(MakeEmptyT &&) { throw 42; }
191  MakeEmptyT &operator=(const MakeEmptyT &) { throw 42; }
192  MakeEmptyT &operator=(MakeEmptyT &&) { throw 42; }
193  ~MakeEmptyT() { --alive; }
194};
195
196int MakeEmptyT::alive = 0;
197
198template <class Variant> void makeEmpty(Variant &v) {
199  Variant v2(std::in_place_type<MakeEmptyT>);
200  try {
201    v = std::move(v2);
202    assert(false);
203  } catch (...) {
204    assert(v.valueless_by_exception());
205  }
206}
207#endif // TEST_HAS_NO_EXCEPTIONS
208
209void test_copy_assignment_not_noexcept() {
210  {
211    using V = std::variant<CopyMaybeThrows>;
212    static_assert(!std::is_nothrow_copy_assignable<V>::value, "");
213  }
214  {
215    using V = std::variant<int, CopyDoesThrow>;
216    static_assert(!std::is_nothrow_copy_assignable<V>::value, "");
217  }
218}
219
220void test_copy_assignment_sfinae() {
221  {
222    using V = std::variant<int, long>;
223    static_assert(std::is_copy_assignable<V>::value, "");
224  }
225  {
226    using V = std::variant<int, CopyOnly>;
227    static_assert(std::is_copy_assignable<V>::value, "");
228  }
229  {
230    using V = std::variant<int, NoCopy>;
231    static_assert(!std::is_copy_assignable<V>::value, "");
232  }
233  {
234    using V = std::variant<int, MoveOnly>;
235    static_assert(!std::is_copy_assignable<V>::value, "");
236  }
237  {
238    using V = std::variant<int, MoveOnlyNT>;
239    static_assert(!std::is_copy_assignable<V>::value, "");
240  }
241
242  // The following tests are for not-yet-standardized behavior (P0602):
243  {
244    using V = std::variant<int, long>;
245    static_assert(std::is_trivially_copy_assignable<V>::value, "");
246  }
247  {
248    using V = std::variant<int, NTCopyAssign>;
249    static_assert(!std::is_trivially_copy_assignable<V>::value, "");
250    static_assert(std::is_copy_assignable<V>::value, "");
251  }
252  {
253    using V = std::variant<int, TCopyAssign>;
254    static_assert(std::is_trivially_copy_assignable<V>::value, "");
255  }
256  {
257    using V = std::variant<int, TCopyAssignNTMoveAssign>;
258    static_assert(std::is_trivially_copy_assignable<V>::value, "");
259  }
260  {
261    using V = std::variant<int, CopyOnly>;
262    static_assert(std::is_trivially_copy_assignable<V>::value, "");
263  }
264}
265
266void test_copy_assignment_empty_empty() {
267#ifndef TEST_HAS_NO_EXCEPTIONS
268  using MET = MakeEmptyT;
269  {
270    using V = std::variant<int, long, MET>;
271    V v1(std::in_place_index<0>);
272    makeEmpty(v1);
273    V v2(std::in_place_index<0>);
274    makeEmpty(v2);
275    V &vref = (v1 = v2);
276    assert(&vref == &v1);
277    assert(v1.valueless_by_exception());
278    assert(v1.index() == std::variant_npos);
279  }
280#endif // TEST_HAS_NO_EXCEPTIONS
281}
282
283void test_copy_assignment_non_empty_empty() {
284#ifndef TEST_HAS_NO_EXCEPTIONS
285  using MET = MakeEmptyT;
286  {
287    using V = std::variant<int, MET>;
288    V v1(std::in_place_index<0>, 42);
289    V v2(std::in_place_index<0>);
290    makeEmpty(v2);
291    V &vref = (v1 = v2);
292    assert(&vref == &v1);
293    assert(v1.valueless_by_exception());
294    assert(v1.index() == std::variant_npos);
295  }
296  {
297    using V = std::variant<int, MET, std::string>;
298    V v1(std::in_place_index<2>, "hello");
299    V v2(std::in_place_index<0>);
300    makeEmpty(v2);
301    V &vref = (v1 = v2);
302    assert(&vref == &v1);
303    assert(v1.valueless_by_exception());
304    assert(v1.index() == std::variant_npos);
305  }
306#endif // TEST_HAS_NO_EXCEPTIONS
307}
308
309void test_copy_assignment_empty_non_empty() {
310#ifndef TEST_HAS_NO_EXCEPTIONS
311  using MET = MakeEmptyT;
312  {
313    using V = std::variant<int, MET>;
314    V v1(std::in_place_index<0>);
315    makeEmpty(v1);
316    V v2(std::in_place_index<0>, 42);
317    V &vref = (v1 = v2);
318    assert(&vref == &v1);
319    assert(v1.index() == 0);
320    assert(std::get<0>(v1) == 42);
321  }
322  {
323    using V = std::variant<int, MET, std::string>;
324    V v1(std::in_place_index<0>);
325    makeEmpty(v1);
326    V v2(std::in_place_type<std::string>, "hello");
327    V &vref = (v1 = v2);
328    assert(&vref == &v1);
329    assert(v1.index() == 2);
330    assert(std::get<2>(v1) == "hello");
331  }
332#endif // TEST_HAS_NO_EXCEPTIONS
333}
334
335template <typename T> struct Result { size_t index; T value; };
336
337void test_copy_assignment_same_index() {
338  {
339    using V = std::variant<int>;
340    V v1(43);
341    V v2(42);
342    V &vref = (v1 = v2);
343    assert(&vref == &v1);
344    assert(v1.index() == 0);
345    assert(std::get<0>(v1) == 42);
346  }
347  {
348    using V = std::variant<int, long, unsigned>;
349    V v1(43l);
350    V v2(42l);
351    V &vref = (v1 = v2);
352    assert(&vref == &v1);
353    assert(v1.index() == 1);
354    assert(std::get<1>(v1) == 42);
355  }
356  {
357    using V = std::variant<int, CopyAssign, unsigned>;
358    V v1(std::in_place_type<CopyAssign>, 43);
359    V v2(std::in_place_type<CopyAssign>, 42);
360    CopyAssign::reset();
361    V &vref = (v1 = v2);
362    assert(&vref == &v1);
363    assert(v1.index() == 1);
364    assert(std::get<1>(v1).value == 42);
365    assert(CopyAssign::copy_construct == 0);
366    assert(CopyAssign::move_construct == 0);
367    assert(CopyAssign::copy_assign == 1);
368  }
369#ifndef TEST_HAS_NO_EXCEPTIONS
370  using MET = MakeEmptyT;
371  {
372    using V = std::variant<int, MET, std::string>;
373    V v1(std::in_place_type<MET>);
374    MET &mref = std::get<1>(v1);
375    V v2(std::in_place_type<MET>);
376    try {
377      v1 = v2;
378      assert(false);
379    } catch (...) {
380    }
381    assert(v1.index() == 1);
382    assert(&std::get<1>(v1) == &mref);
383  }
384#endif // TEST_HAS_NO_EXCEPTIONS
385
386  // The following tests are for not-yet-standardized behavior (P0602):
387  {
388    struct {
389      constexpr Result<int> operator()() const {
390        using V = std::variant<int>;
391        V v(43);
392        V v2(42);
393        v = v2;
394        return {v.index(), std::get<0>(v)};
395      }
396    } test;
397    constexpr auto result = test();
398    static_assert(result.index == 0, "");
399    static_assert(result.value == 42, "");
400  }
401  {
402    struct {
403      constexpr Result<long> operator()() const {
404        using V = std::variant<int, long, unsigned>;
405        V v(43l);
406        V v2(42l);
407        v = v2;
408        return {v.index(), std::get<1>(v)};
409      }
410    } test;
411    constexpr auto result = test();
412    static_assert(result.index == 1, "");
413    static_assert(result.value == 42l, "");
414  }
415  {
416    struct {
417      constexpr Result<int> operator()() const {
418        using V = std::variant<int, TCopyAssign, unsigned>;
419        V v(std::in_place_type<TCopyAssign>, 43);
420        V v2(std::in_place_type<TCopyAssign>, 42);
421        v = v2;
422        return {v.index(), std::get<1>(v).value};
423      }
424    } test;
425    constexpr auto result = test();
426    static_assert(result.index == 1, "");
427    static_assert(result.value == 42, "");
428  }
429  {
430    struct {
431      constexpr Result<int> operator()() const {
432        using V = std::variant<int, TCopyAssignNTMoveAssign, unsigned>;
433        V v(std::in_place_type<TCopyAssignNTMoveAssign>, 43);
434        V v2(std::in_place_type<TCopyAssignNTMoveAssign>, 42);
435        v = v2;
436        return {v.index(), std::get<1>(v).value};
437      }
438    } test;
439    constexpr auto result = test();
440    static_assert(result.index == 1, "");
441    static_assert(result.value == 42, "");
442  }
443}
444
445void test_copy_assignment_different_index() {
446  {
447    using V = std::variant<int, long, unsigned>;
448    V v1(43);
449    V v2(42l);
450    V &vref = (v1 = v2);
451    assert(&vref == &v1);
452    assert(v1.index() == 1);
453    assert(std::get<1>(v1) == 42);
454  }
455  {
456    using V = std::variant<int, CopyAssign, unsigned>;
457    CopyAssign::reset();
458    V v1(std::in_place_type<unsigned>, 43);
459    V v2(std::in_place_type<CopyAssign>, 42);
460    assert(CopyAssign::copy_construct == 0);
461    assert(CopyAssign::move_construct == 0);
462    assert(CopyAssign::alive == 1);
463    V &vref = (v1 = v2);
464    assert(&vref == &v1);
465    assert(v1.index() == 1);
466    assert(std::get<1>(v1).value == 42);
467    assert(CopyAssign::alive == 2);
468    assert(CopyAssign::copy_construct == 1);
469    assert(CopyAssign::move_construct == 1);
470    assert(CopyAssign::copy_assign == 0);
471  }
472#ifndef TEST_HAS_NO_EXCEPTIONS
473  {
474    using V = std::variant<int, CopyThrows, std::string>;
475    V v1(std::in_place_type<std::string>, "hello");
476    V v2(std::in_place_type<CopyThrows>);
477    try {
478      v1 = v2;
479      assert(false);
480    } catch (...) { /* ... */
481    }
482    // Test that copy construction is used directly if move construction may throw,
483    // resulting in a valueless variant if copy throws.
484    assert(v1.valueless_by_exception());
485  }
486  {
487    using V = std::variant<int, MoveThrows, std::string>;
488    V v1(std::in_place_type<std::string>, "hello");
489    V v2(std::in_place_type<MoveThrows>);
490    assert(MoveThrows::alive == 1);
491    // Test that copy construction is used directly if move construction may throw.
492    v1 = v2;
493    assert(v1.index() == 1);
494    assert(v2.index() == 1);
495    assert(MoveThrows::alive == 2);
496  }
497  {
498    // Test that direct copy construction is preferred when it cannot throw.
499    using V = std::variant<int, CopyCannotThrow, std::string>;
500    V v1(std::in_place_type<std::string>, "hello");
501    V v2(std::in_place_type<CopyCannotThrow>);
502    assert(CopyCannotThrow::alive == 1);
503    v1 = v2;
504    assert(v1.index() == 1);
505    assert(v2.index() == 1);
506    assert(CopyCannotThrow::alive == 2);
507  }
508  {
509    using V = std::variant<int, CopyThrows, std::string>;
510    V v1(std::in_place_type<CopyThrows>);
511    V v2(std::in_place_type<std::string>, "hello");
512    V &vref = (v1 = v2);
513    assert(&vref == &v1);
514    assert(v1.index() == 2);
515    assert(std::get<2>(v1) == "hello");
516    assert(v2.index() == 2);
517    assert(std::get<2>(v2) == "hello");
518  }
519  {
520    using V = std::variant<int, MoveThrows, std::string>;
521    V v1(std::in_place_type<MoveThrows>);
522    V v2(std::in_place_type<std::string>, "hello");
523    V &vref = (v1 = v2);
524    assert(&vref == &v1);
525    assert(v1.index() == 2);
526    assert(std::get<2>(v1) == "hello");
527    assert(v2.index() == 2);
528    assert(std::get<2>(v2) == "hello");
529  }
530#endif // TEST_HAS_NO_EXCEPTIONS
531
532  // The following tests are for not-yet-standardized behavior (P0602):
533  {
534    struct {
535      constexpr Result<long> operator()() const {
536        using V = std::variant<int, long, unsigned>;
537        V v(43);
538        V v2(42l);
539        v = v2;
540        return {v.index(), std::get<1>(v)};
541      }
542    } test;
543    constexpr auto result = test();
544    static_assert(result.index == 1, "");
545    static_assert(result.value == 42l, "");
546  }
547  {
548    struct {
549      constexpr Result<int> operator()() const {
550        using V = std::variant<int, TCopyAssign, unsigned>;
551        V v(std::in_place_type<unsigned>, 43);
552        V v2(std::in_place_type<TCopyAssign>, 42);
553        v = v2;
554        return {v.index(), std::get<1>(v).value};
555      }
556    } test;
557    constexpr auto result = test();
558    static_assert(result.index == 1, "");
559    static_assert(result.value == 42, "");
560  }
561}
562
563template <size_t NewIdx, class ValueType>
564constexpr bool test_constexpr_assign_extension_imp(
565    std::variant<long, void*, int>&& v, ValueType&& new_value)
566{
567  const std::variant<long, void*, int> cp(
568      std::forward<ValueType>(new_value));
569  v = cp;
570  return v.index() == NewIdx &&
571        std::get<NewIdx>(v) == std::get<NewIdx>(cp);
572}
573
574void test_constexpr_copy_assignment_extension() {
575  // The following tests are for not-yet-standardized behavior (P0602):
576  using V = std::variant<long, void*, int>;
577  static_assert(std::is_trivially_copyable<V>::value, "");
578  static_assert(std::is_trivially_copy_assignable<V>::value, "");
579  static_assert(test_constexpr_assign_extension_imp<0>(V(42l), 101l), "");
580  static_assert(test_constexpr_assign_extension_imp<0>(V(nullptr), 101l), "");
581  static_assert(test_constexpr_assign_extension_imp<1>(V(42l), nullptr), "");
582  static_assert(test_constexpr_assign_extension_imp<2>(V(42l), 101), "");
583}
584
585int main() {
586  test_copy_assignment_empty_empty();
587  test_copy_assignment_non_empty_empty();
588  test_copy_assignment_empty_non_empty();
589  test_copy_assignment_same_index();
590  test_copy_assignment_different_index();
591  test_copy_assignment_sfinae();
592  test_copy_assignment_not_noexcept();
593  test_constexpr_copy_assignment_extension();
594}
595