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// XFAIL: with_system_cxx_lib=macosx10.12
14// XFAIL: with_system_cxx_lib=macosx10.11
15// XFAIL: with_system_cxx_lib=macosx10.10
16// XFAIL: with_system_cxx_lib=macosx10.9
17// XFAIL: with_system_cxx_lib=macosx10.7
18// XFAIL: with_system_cxx_lib=macosx10.8
19
20// <variant>
21
22// template <class ...Types> class variant;
23
24// variant(variant&&) noexcept(see below);
25
26#include <cassert>
27#include <string>
28#include <type_traits>
29#include <variant>
30
31#include "test_macros.h"
32#include "test_workarounds.h"
33
34struct ThrowsMove {
35  ThrowsMove(ThrowsMove &&) noexcept(false) {}
36};
37
38struct NoCopy {
39  NoCopy(const NoCopy &) = delete;
40};
41
42struct MoveOnly {
43  int value;
44  MoveOnly(int v) : value(v) {}
45  MoveOnly(const MoveOnly &) = delete;
46  MoveOnly(MoveOnly &&) = default;
47};
48
49struct MoveOnlyNT {
50  int value;
51  MoveOnlyNT(int v) : value(v) {}
52  MoveOnlyNT(const MoveOnlyNT &) = delete;
53  MoveOnlyNT(MoveOnlyNT &&other) : value(other.value) { other.value = -1; }
54};
55
56struct NTMove {
57  constexpr NTMove(int v) : value(v) {}
58  NTMove(const NTMove &) = delete;
59  NTMove(NTMove &&that) : value(that.value) { that.value = -1; }
60  int value;
61};
62
63static_assert(!std::is_trivially_move_constructible<NTMove>::value, "");
64static_assert(std::is_move_constructible<NTMove>::value, "");
65
66struct TMove {
67  constexpr TMove(int v) : value(v) {}
68  TMove(const TMove &) = delete;
69  TMove(TMove &&) = default;
70  int value;
71};
72
73static_assert(std::is_trivially_move_constructible<TMove>::value, "");
74
75struct TMoveNTCopy {
76  constexpr TMoveNTCopy(int v) : value(v) {}
77  TMoveNTCopy(const TMoveNTCopy& that) : value(that.value) {}
78  TMoveNTCopy(TMoveNTCopy&&) = default;
79  int value;
80};
81
82static_assert(std::is_trivially_move_constructible<TMoveNTCopy>::value, "");
83
84#ifndef TEST_HAS_NO_EXCEPTIONS
85struct MakeEmptyT {
86  static int alive;
87  MakeEmptyT() { ++alive; }
88  MakeEmptyT(const MakeEmptyT &) {
89    ++alive;
90    // Don't throw from the copy constructor since variant's assignment
91    // operator performs a copy before committing to the assignment.
92  }
93  MakeEmptyT(MakeEmptyT &&) { throw 42; }
94  MakeEmptyT &operator=(const MakeEmptyT &) { throw 42; }
95  MakeEmptyT &operator=(MakeEmptyT &&) { throw 42; }
96  ~MakeEmptyT() { --alive; }
97};
98
99int MakeEmptyT::alive = 0;
100
101template <class Variant> void makeEmpty(Variant &v) {
102  Variant v2(std::in_place_type<MakeEmptyT>);
103  try {
104    v = std::move(v2);
105    assert(false);
106  } catch (...) {
107    assert(v.valueless_by_exception());
108  }
109}
110#endif // TEST_HAS_NO_EXCEPTIONS
111
112void test_move_noexcept() {
113  {
114    using V = std::variant<int, long>;
115    static_assert(std::is_nothrow_move_constructible<V>::value, "");
116  }
117  {
118    using V = std::variant<int, MoveOnly>;
119    static_assert(std::is_nothrow_move_constructible<V>::value, "");
120  }
121  {
122    using V = std::variant<int, MoveOnlyNT>;
123    static_assert(!std::is_nothrow_move_constructible<V>::value, "");
124  }
125  {
126    using V = std::variant<int, ThrowsMove>;
127    static_assert(!std::is_nothrow_move_constructible<V>::value, "");
128  }
129}
130
131void test_move_ctor_sfinae() {
132  {
133    using V = std::variant<int, long>;
134    static_assert(std::is_move_constructible<V>::value, "");
135  }
136  {
137    using V = std::variant<int, MoveOnly>;
138    static_assert(std::is_move_constructible<V>::value, "");
139  }
140  {
141    using V = std::variant<int, MoveOnlyNT>;
142    static_assert(std::is_move_constructible<V>::value, "");
143  }
144  {
145    using V = std::variant<int, NoCopy>;
146    static_assert(!std::is_move_constructible<V>::value, "");
147  }
148
149  // The following tests are for not-yet-standardized behavior (P0602):
150  {
151    using V = std::variant<int, long>;
152    static_assert(std::is_trivially_move_constructible<V>::value, "");
153  }
154  {
155    using V = std::variant<int, NTMove>;
156    static_assert(!std::is_trivially_move_constructible<V>::value, "");
157    static_assert(std::is_move_constructible<V>::value, "");
158  }
159  {
160    using V = std::variant<int, TMove>;
161    static_assert(std::is_trivially_move_constructible<V>::value, "");
162  }
163  {
164    using V = std::variant<int, TMoveNTCopy>;
165    static_assert(std::is_trivially_move_constructible<V>::value, "");
166  }
167}
168
169template <typename T>
170struct Result { size_t index; T value; };
171
172void test_move_ctor_basic() {
173  {
174    std::variant<int> v(std::in_place_index<0>, 42);
175    std::variant<int> v2 = std::move(v);
176    assert(v2.index() == 0);
177    assert(std::get<0>(v2) == 42);
178  }
179  {
180    std::variant<int, long> v(std::in_place_index<1>, 42);
181    std::variant<int, long> v2 = std::move(v);
182    assert(v2.index() == 1);
183    assert(std::get<1>(v2) == 42);
184  }
185  {
186    std::variant<MoveOnly> v(std::in_place_index<0>, 42);
187    assert(v.index() == 0);
188    std::variant<MoveOnly> v2(std::move(v));
189    assert(v2.index() == 0);
190    assert(std::get<0>(v2).value == 42);
191  }
192  {
193    std::variant<int, MoveOnly> v(std::in_place_index<1>, 42);
194    assert(v.index() == 1);
195    std::variant<int, MoveOnly> v2(std::move(v));
196    assert(v2.index() == 1);
197    assert(std::get<1>(v2).value == 42);
198  }
199  {
200    std::variant<MoveOnlyNT> v(std::in_place_index<0>, 42);
201    assert(v.index() == 0);
202    std::variant<MoveOnlyNT> v2(std::move(v));
203    assert(v2.index() == 0);
204    assert(std::get<0>(v).value == -1);
205    assert(std::get<0>(v2).value == 42);
206  }
207  {
208    std::variant<int, MoveOnlyNT> v(std::in_place_index<1>, 42);
209    assert(v.index() == 1);
210    std::variant<int, MoveOnlyNT> v2(std::move(v));
211    assert(v2.index() == 1);
212    assert(std::get<1>(v).value == -1);
213    assert(std::get<1>(v2).value == 42);
214  }
215
216  // The following tests are for not-yet-standardized behavior (P0602):
217  {
218    struct {
219      constexpr Result<int> operator()() const {
220        std::variant<int> v(std::in_place_index<0>, 42);
221        std::variant<int> v2 = std::move(v);
222        return {v2.index(), std::get<0>(std::move(v2))};
223      }
224    } test;
225    constexpr auto result = test();
226    static_assert(result.index == 0, "");
227    static_assert(result.value == 42, "");
228  }
229  {
230    struct {
231      constexpr Result<long> operator()() const {
232        std::variant<int, long> v(std::in_place_index<1>, 42);
233        std::variant<int, long> v2 = std::move(v);
234        return {v2.index(), std::get<1>(std::move(v2))};
235      }
236    } test;
237    constexpr auto result = test();
238    static_assert(result.index == 1, "");
239    static_assert(result.value == 42, "");
240  }
241  {
242    struct {
243      constexpr Result<TMove> operator()() const {
244        std::variant<TMove> v(std::in_place_index<0>, 42);
245        std::variant<TMove> v2(std::move(v));
246        return {v2.index(), std::get<0>(std::move(v2))};
247      }
248    } test;
249    constexpr auto result = test();
250    static_assert(result.index == 0, "");
251    static_assert(result.value.value == 42, "");
252  }
253  {
254    struct {
255      constexpr Result<TMove> operator()() const {
256        std::variant<int, TMove> v(std::in_place_index<1>, 42);
257        std::variant<int, TMove> v2(std::move(v));
258        return {v2.index(), std::get<1>(std::move(v2))};
259      }
260    } test;
261    constexpr auto result = test();
262    static_assert(result.index == 1, "");
263    static_assert(result.value.value == 42, "");
264  }
265  {
266    struct {
267      constexpr Result<TMoveNTCopy> operator()() const {
268        std::variant<TMoveNTCopy> v(std::in_place_index<0>, 42);
269        std::variant<TMoveNTCopy> v2(std::move(v));
270        return {v2.index(), std::get<0>(std::move(v2))};
271      }
272    } test;
273    constexpr auto result = test();
274    static_assert(result.index == 0, "");
275    static_assert(result.value.value == 42, "");
276  }
277  {
278    struct {
279      constexpr Result<TMoveNTCopy> operator()() const {
280        std::variant<int, TMoveNTCopy> v(std::in_place_index<1>, 42);
281        std::variant<int, TMoveNTCopy> v2(std::move(v));
282        return {v2.index(), std::get<1>(std::move(v2))};
283      }
284    } test;
285    constexpr auto result = test();
286    static_assert(result.index == 1, "");
287    static_assert(result.value.value == 42, "");
288  }
289}
290
291void test_move_ctor_valueless_by_exception() {
292#ifndef TEST_HAS_NO_EXCEPTIONS
293  using V = std::variant<int, MakeEmptyT>;
294  V v1;
295  makeEmpty(v1);
296  V v(std::move(v1));
297  assert(v.valueless_by_exception());
298#endif // TEST_HAS_NO_EXCEPTIONS
299}
300
301template <size_t Idx>
302constexpr bool test_constexpr_ctor_extension_imp(
303    std::variant<long, void*, const int> const& v)
304{
305  auto copy = v;
306  auto v2 = std::move(copy);
307  return v2.index() == v.index() &&
308         v2.index() == Idx &&
309        std::get<Idx>(v2) == std::get<Idx>(v);
310}
311
312void test_constexpr_move_ctor_extension() {
313  // NOTE: This test is for not yet standardized behavior. (P0602)
314  using V = std::variant<long, void*, const int>;
315#ifdef TEST_WORKAROUND_C1XX_BROKEN_IS_TRIVIALLY_COPYABLE
316  static_assert(std::is_trivially_destructible<V>::value, "");
317  static_assert(std::is_trivially_copy_constructible<V>::value, "");
318  static_assert(std::is_trivially_move_constructible<V>::value, "");
319  static_assert(!std::is_copy_assignable<V>::value, "");
320  static_assert(!std::is_move_assignable<V>::value, "");
321#else // TEST_WORKAROUND_C1XX_BROKEN_IS_TRIVIALLY_COPYABLE
322  static_assert(std::is_trivially_copyable<V>::value, "");
323#endif // TEST_WORKAROUND_C1XX_BROKEN_IS_TRIVIALLY_COPYABLE
324  static_assert(std::is_trivially_move_constructible<V>::value, "");
325  static_assert(test_constexpr_ctor_extension_imp<0>(V(42l)), "");
326  static_assert(test_constexpr_ctor_extension_imp<1>(V(nullptr)), "");
327  static_assert(test_constexpr_ctor_extension_imp<2>(V(101)), "");
328}
329
330int main() {
331  test_move_ctor_basic();
332  test_move_ctor_valueless_by_exception();
333  test_move_noexcept();
334  test_move_ctor_sfinae();
335  test_constexpr_move_ctor_extension();
336}
337