1 // -*- C++ -*-
2 //===----------------------------------------------------------------------===//
3 //
4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5 // See https://llvm.org/LICENSE.txt for license information.
6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //
8 //===----------------------------------------------------------------------===//
9 
10 // UNSUPPORTED: c++03, c++11, c++14
11 
12 // Throwing bad_variant_access is supported starting in macosx10.13
13 // XFAIL: use_system_cxx_lib && x86_64-apple-macosx10.12 && !no-exceptions
14 // XFAIL: use_system_cxx_lib && x86_64-apple-macosx10.11 && !no-exceptions
15 // XFAIL: use_system_cxx_lib && x86_64-apple-macosx10.10 && !no-exceptions
16 // XFAIL: use_system_cxx_lib && x86_64-apple-macosx10.9 && !no-exceptions
17 
18 // <variant>
19 
20 // template <class ...Types> class variant;
21 
22 // variant(variant const&); // constexpr in C++20
23 
24 #include <cassert>
25 #include <type_traits>
26 #include <variant>
27 
28 #include "test_macros.h"
29 #include "test_workarounds.h"
30 
31 struct NonT {
32   NonT(int v) : value(v) {}
33   NonT(const NonT &o) : value(o.value) {}
34   int value;
35 };
36 static_assert(!std::is_trivially_copy_constructible<NonT>::value, "");
37 
38 struct NoCopy {
39   NoCopy(const NoCopy &) = delete;
40 };
41 
42 struct MoveOnly {
43   MoveOnly(const MoveOnly &) = delete;
44   MoveOnly(MoveOnly &&) = default;
45 };
46 
47 struct MoveOnlyNT {
48   MoveOnlyNT(const MoveOnlyNT &) = delete;
49   MoveOnlyNT(MoveOnlyNT &&) {}
50 };
51 
52 struct NTCopy {
53   constexpr NTCopy(int v) : value(v) {}
54   NTCopy(const NTCopy &that) : value(that.value) {}
55   NTCopy(NTCopy &&) = delete;
56   int value;
57 };
58 
59 static_assert(!std::is_trivially_copy_constructible<NTCopy>::value, "");
60 static_assert(std::is_copy_constructible<NTCopy>::value, "");
61 
62 struct TCopy {
63   constexpr TCopy(int v) : value(v) {}
64   TCopy(TCopy const &) = default;
65   TCopy(TCopy &&) = delete;
66   int value;
67 };
68 
69 static_assert(std::is_trivially_copy_constructible<TCopy>::value, "");
70 
71 struct TCopyNTMove {
72   constexpr TCopyNTMove(int v) : value(v) {}
73   TCopyNTMove(const TCopyNTMove&) = default;
74   TCopyNTMove(TCopyNTMove&& that) : value(that.value) { that.value = -1; }
75   int value;
76 };
77 
78 static_assert(std::is_trivially_copy_constructible<TCopyNTMove>::value, "");
79 
80 #ifndef TEST_HAS_NO_EXCEPTIONS
81 struct MakeEmptyT {
82   static int alive;
83   MakeEmptyT() { ++alive; }
84   MakeEmptyT(const MakeEmptyT &) {
85     ++alive;
86     // Don't throw from the copy constructor since variant's assignment
87     // operator performs a copy before committing to the assignment.
88   }
89   MakeEmptyT(MakeEmptyT &&) { throw 42; }
90   MakeEmptyT &operator=(const MakeEmptyT &) { throw 42; }
91   MakeEmptyT &operator=(MakeEmptyT &&) { throw 42; }
92   ~MakeEmptyT() { --alive; }
93 };
94 
95 int MakeEmptyT::alive = 0;
96 
97 template <class Variant> void makeEmpty(Variant &v) {
98   Variant v2(std::in_place_type<MakeEmptyT>);
99   try {
100     v = std::move(v2);
101     assert(false);
102   } catch (...) {
103     assert(v.valueless_by_exception());
104   }
105 }
106 #endif // TEST_HAS_NO_EXCEPTIONS
107 
108 void test_copy_ctor_sfinae() {
109   {
110     using V = std::variant<int, long>;
111     static_assert(std::is_copy_constructible<V>::value, "");
112   }
113   {
114     using V = std::variant<int, NoCopy>;
115     static_assert(!std::is_copy_constructible<V>::value, "");
116   }
117   {
118     using V = std::variant<int, MoveOnly>;
119     static_assert(!std::is_copy_constructible<V>::value, "");
120   }
121   {
122     using V = std::variant<int, MoveOnlyNT>;
123     static_assert(!std::is_copy_constructible<V>::value, "");
124   }
125 
126   // Make sure we properly propagate triviality (see P0602R4).
127 #if TEST_STD_VER > 17
128   {
129     using V = std::variant<int, long>;
130     static_assert(std::is_trivially_copy_constructible<V>::value, "");
131   }
132   {
133     using V = std::variant<int, NTCopy>;
134     static_assert(!std::is_trivially_copy_constructible<V>::value, "");
135     static_assert(std::is_copy_constructible<V>::value, "");
136   }
137   {
138     using V = std::variant<int, TCopy>;
139     static_assert(std::is_trivially_copy_constructible<V>::value, "");
140   }
141   {
142     using V = std::variant<int, TCopyNTMove>;
143     static_assert(std::is_trivially_copy_constructible<V>::value, "");
144   }
145 #endif // > C++17
146 }
147 
148 void test_copy_ctor_basic() {
149   {
150     std::variant<int> v(std::in_place_index<0>, 42);
151     std::variant<int> v2 = v;
152     assert(v2.index() == 0);
153     assert(std::get<0>(v2) == 42);
154   }
155   {
156     std::variant<int, long> v(std::in_place_index<1>, 42);
157     std::variant<int, long> v2 = v;
158     assert(v2.index() == 1);
159     assert(std::get<1>(v2) == 42);
160   }
161   {
162     std::variant<NonT> v(std::in_place_index<0>, 42);
163     assert(v.index() == 0);
164     std::variant<NonT> v2(v);
165     assert(v2.index() == 0);
166     assert(std::get<0>(v2).value == 42);
167   }
168   {
169     std::variant<int, NonT> v(std::in_place_index<1>, 42);
170     assert(v.index() == 1);
171     std::variant<int, NonT> v2(v);
172     assert(v2.index() == 1);
173     assert(std::get<1>(v2).value == 42);
174   }
175 
176   // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
177 #if TEST_STD_VER > 17
178   {
179     constexpr std::variant<int> v(std::in_place_index<0>, 42);
180     static_assert(v.index() == 0, "");
181     constexpr std::variant<int> v2 = v;
182     static_assert(v2.index() == 0, "");
183     static_assert(std::get<0>(v2) == 42, "");
184   }
185   {
186     constexpr std::variant<int, long> v(std::in_place_index<1>, 42);
187     static_assert(v.index() == 1, "");
188     constexpr std::variant<int, long> v2 = v;
189     static_assert(v2.index() == 1, "");
190     static_assert(std::get<1>(v2) == 42, "");
191   }
192   {
193     constexpr std::variant<TCopy> v(std::in_place_index<0>, 42);
194     static_assert(v.index() == 0, "");
195     constexpr std::variant<TCopy> v2(v);
196     static_assert(v2.index() == 0, "");
197     static_assert(std::get<0>(v2).value == 42, "");
198   }
199   {
200     constexpr std::variant<int, TCopy> v(std::in_place_index<1>, 42);
201     static_assert(v.index() == 1, "");
202     constexpr std::variant<int, TCopy> v2(v);
203     static_assert(v2.index() == 1, "");
204     static_assert(std::get<1>(v2).value == 42, "");
205   }
206   {
207     constexpr std::variant<TCopyNTMove> v(std::in_place_index<0>, 42);
208     static_assert(v.index() == 0, "");
209     constexpr std::variant<TCopyNTMove> v2(v);
210     static_assert(v2.index() == 0, "");
211     static_assert(std::get<0>(v2).value == 42, "");
212   }
213   {
214     constexpr std::variant<int, TCopyNTMove> v(std::in_place_index<1>, 42);
215     static_assert(v.index() == 1, "");
216     constexpr std::variant<int, TCopyNTMove> v2(v);
217     static_assert(v2.index() == 1, "");
218     static_assert(std::get<1>(v2).value == 42, "");
219   }
220 #endif // > C++17
221 }
222 
223 void test_copy_ctor_valueless_by_exception() {
224 #ifndef TEST_HAS_NO_EXCEPTIONS
225   using V = std::variant<int, MakeEmptyT>;
226   V v1;
227   makeEmpty(v1);
228   const V &cv1 = v1;
229   V v(cv1);
230   assert(v.valueless_by_exception());
231 #endif // TEST_HAS_NO_EXCEPTIONS
232 }
233 
234 template <size_t Idx>
235 constexpr bool test_constexpr_copy_ctor_imp(std::variant<long, void*, const int> const& v) {
236   auto v2 = v;
237   return v2.index() == v.index() &&
238          v2.index() == Idx &&
239          std::get<Idx>(v2) == std::get<Idx>(v);
240 }
241 
242 void test_constexpr_copy_ctor() {
243   // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4).
244 #if TEST_STD_VER > 17
245   using V = std::variant<long, void*, const int>;
246 #ifdef TEST_WORKAROUND_C1XX_BROKEN_IS_TRIVIALLY_COPYABLE
247   static_assert(std::is_trivially_destructible<V>::value, "");
248   static_assert(std::is_trivially_copy_constructible<V>::value, "");
249   static_assert(std::is_trivially_move_constructible<V>::value, "");
250   static_assert(!std::is_copy_assignable<V>::value, "");
251   static_assert(!std::is_move_assignable<V>::value, "");
252 #else // TEST_WORKAROUND_C1XX_BROKEN_IS_TRIVIALLY_COPYABLE
253   static_assert(std::is_trivially_copyable<V>::value, "");
254 #endif // TEST_WORKAROUND_C1XX_BROKEN_IS_TRIVIALLY_COPYABLE
255   static_assert(test_constexpr_copy_ctor_imp<0>(V(42l)), "");
256   static_assert(test_constexpr_copy_ctor_imp<1>(V(nullptr)), "");
257   static_assert(test_constexpr_copy_ctor_imp<2>(V(101)), "");
258 #endif // > C++17
259 }
260 
261 int main(int, char**) {
262   test_copy_ctor_basic();
263   test_copy_ctor_valueless_by_exception();
264   test_copy_ctor_sfinae();
265   test_constexpr_copy_ctor();
266 #if 0
267 // disable this for the moment; it fails on older compilers.
268 //  Need to figure out which compilers will support it.
269 { // This is the motivating example from P0739R0
270   std::variant<int, double> v1(3);
271   std::variant v2 = v1;
272   (void) v2;
273 }
274 #endif
275 
276   return 0;
277 }
278