xref: /llvm-project/libcxx/test/std/utilities/variant/variant.variant/variant.assign/T.pass.cpp (revision f5f2f77755072888e955d05bc0ff568b18260ca0)
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++98, c++03, c++11, c++14
11 
12 // XFAIL: dylib-has-no-bad_variant_access && !libcpp-no-exceptions
13 
14 // <variant>
15 
16 // template <class ...Types> class variant;
17 
18 // template <class T>
19 // variant& operator=(T&&) noexcept(see below);
20 
21 #include <cassert>
22 #include <string>
23 #include <type_traits>
24 #include <variant>
25 
26 #include "test_macros.h"
27 #include "variant_test_helpers.hpp"
28 
29 namespace MetaHelpers {
30 
31 struct Dummy {
32   Dummy() = default;
33 };
34 
35 struct ThrowsCtorT {
36   ThrowsCtorT(int) noexcept(false) {}
37   ThrowsCtorT &operator=(int) noexcept { return *this; }
38 };
39 
40 struct ThrowsAssignT {
41   ThrowsAssignT(int) noexcept {}
42   ThrowsAssignT &operator=(int) noexcept(false) { return *this; }
43 };
44 
45 struct NoThrowT {
46   NoThrowT(int) noexcept {}
47   NoThrowT &operator=(int) noexcept { return *this; }
48 };
49 
50 } // namespace MetaHelpers
51 
52 namespace RuntimeHelpers {
53 #ifndef TEST_HAS_NO_EXCEPTIONS
54 
55 struct ThrowsCtorT {
56   int value;
57   ThrowsCtorT() : value(0) {}
58   ThrowsCtorT(int) noexcept(false) { throw 42; }
59   ThrowsCtorT &operator=(int v) noexcept {
60     value = v;
61     return *this;
62   }
63 };
64 
65 struct MoveCrashes {
66   int value;
67   MoveCrashes(int v = 0) noexcept : value{v} {}
68   MoveCrashes(MoveCrashes &&) noexcept { assert(false); }
69   MoveCrashes &operator=(MoveCrashes &&) noexcept { assert(false); return *this; }
70   MoveCrashes &operator=(int v) noexcept {
71     value = v;
72     return *this;
73   }
74 };
75 
76 struct ThrowsCtorTandMove {
77   int value;
78   ThrowsCtorTandMove() : value(0) {}
79   ThrowsCtorTandMove(int) noexcept(false) { throw 42; }
80   ThrowsCtorTandMove(ThrowsCtorTandMove &&) noexcept(false) { assert(false); }
81   ThrowsCtorTandMove &operator=(int v) noexcept {
82     value = v;
83     return *this;
84   }
85 };
86 
87 struct ThrowsAssignT {
88   int value;
89   ThrowsAssignT() : value(0) {}
90   ThrowsAssignT(int v) noexcept : value(v) {}
91   ThrowsAssignT &operator=(int) noexcept(false) { throw 42; }
92 };
93 
94 struct NoThrowT {
95   int value;
96   NoThrowT() : value(0) {}
97   NoThrowT(int v) noexcept : value(v) {}
98   NoThrowT &operator=(int v) noexcept {
99     value = v;
100     return *this;
101   }
102 };
103 
104 #endif // !defined(TEST_HAS_NO_EXCEPTIONS)
105 } // namespace RuntimeHelpers
106 
107 void test_T_assignment_noexcept() {
108   using namespace MetaHelpers;
109   {
110     using V = std::variant<Dummy, NoThrowT>;
111     static_assert(std::is_nothrow_assignable<V, int>::value, "");
112   }
113   {
114     using V = std::variant<Dummy, ThrowsCtorT>;
115     static_assert(!std::is_nothrow_assignable<V, int>::value, "");
116   }
117   {
118     using V = std::variant<Dummy, ThrowsAssignT>;
119     static_assert(!std::is_nothrow_assignable<V, int>::value, "");
120   }
121 }
122 
123 void test_T_assignment_sfinae() {
124   {
125     using V = std::variant<long, unsigned>;
126     static_assert(!std::is_assignable<V, int>::value, "ambiguous");
127   }
128   {
129     using V = std::variant<std::string, std::string>;
130     static_assert(!std::is_assignable<V, const char *>::value, "ambiguous");
131   }
132   {
133     using V = std::variant<std::string, void *>;
134     static_assert(!std::is_assignable<V, int>::value, "no matching operator=");
135   }
136 #if !defined(TEST_VARIANT_HAS_NO_REFERENCES)
137   {
138     using V = std::variant<int, int &&>;
139     static_assert(!std::is_assignable<V, int>::value, "ambiguous");
140   }
141   {
142     using V = std::variant<int, const int &>;
143     static_assert(!std::is_assignable<V, int>::value, "ambiguous");
144   }
145 #endif // TEST_VARIANT_HAS_NO_REFERENCES
146 }
147 
148 void test_T_assignment_basic() {
149   {
150     std::variant<int> v(43);
151     v = 42;
152     assert(v.index() == 0);
153     assert(std::get<0>(v) == 42);
154   }
155   {
156     std::variant<int, long> v(43l);
157     v = 42;
158     assert(v.index() == 0);
159     assert(std::get<0>(v) == 42);
160     v = 43l;
161     assert(v.index() == 1);
162     assert(std::get<1>(v) == 43);
163   }
164 #if !defined(TEST_VARIANT_HAS_NO_REFERENCES)
165   {
166     using V = std::variant<int &, int &&, long>;
167     int x = 42;
168     V v(43l);
169     v = x;
170     assert(v.index() == 0);
171     assert(&std::get<0>(v) == &x);
172     v = std::move(x);
173     assert(v.index() == 1);
174     assert(&std::get<1>(v) == &x);
175     // 'long' is selected by FUN(const int &) since 'const int &' cannot bind
176     // to 'int&'.
177     const int &cx = x;
178     v = cx;
179     assert(v.index() == 2);
180     assert(std::get<2>(v) == 42);
181   }
182 #endif // TEST_VARIANT_HAS_NO_REFERENCES
183 }
184 
185 void test_T_assignment_performs_construction() {
186   using namespace RuntimeHelpers;
187 #ifndef TEST_HAS_NO_EXCEPTIONS
188   {
189     using V = std::variant<std::string, ThrowsCtorT>;
190     V v(std::in_place_type<std::string>, "hello");
191     try {
192       v = 42;
193       assert(false);
194     } catch (...) { /* ... */
195     }
196     assert(v.index() == 0);
197     assert(std::get<0>(v) == "hello");
198   }
199   {
200     using V = std::variant<ThrowsAssignT, std::string>;
201     V v(std::in_place_type<std::string>, "hello");
202     v = 42;
203     assert(v.index() == 0);
204     assert(std::get<0>(v).value == 42);
205   }
206 #endif // TEST_HAS_NO_EXCEPTIONS
207 }
208 
209 void test_T_assignment_performs_assignment() {
210   using namespace RuntimeHelpers;
211 #ifndef TEST_HAS_NO_EXCEPTIONS
212   {
213     using V = std::variant<ThrowsCtorT>;
214     V v;
215     v = 42;
216     assert(v.index() == 0);
217     assert(std::get<0>(v).value == 42);
218   }
219   {
220     using V = std::variant<ThrowsCtorT, std::string>;
221     V v;
222     v = 42;
223     assert(v.index() == 0);
224     assert(std::get<0>(v).value == 42);
225   }
226   {
227     using V = std::variant<ThrowsAssignT>;
228     V v(100);
229     try {
230       v = 42;
231       assert(false);
232     } catch (...) { /* ... */
233     }
234     assert(v.index() == 0);
235     assert(std::get<0>(v).value == 100);
236   }
237   {
238     using V = std::variant<std::string, ThrowsAssignT>;
239     V v(100);
240     try {
241       v = 42;
242       assert(false);
243     } catch (...) { /* ... */
244     }
245     assert(v.index() == 1);
246     assert(std::get<1>(v).value == 100);
247   }
248 #endif // TEST_HAS_NO_EXCEPTIONS
249 }
250 
251 int main(int, char**) {
252   test_T_assignment_basic();
253   test_T_assignment_performs_construction();
254   test_T_assignment_performs_assignment();
255   test_T_assignment_noexcept();
256   test_T_assignment_sfinae();
257 
258   return 0;
259 }
260