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