1 //===----------------------------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 // UNSUPPORTED: c++03, c++11, c++14 10 11 // <variant> 12 13 // template <class ...Types> class variant; 14 15 // template <class T> 16 // variant& operator=(T&&) noexcept(see below); 17 18 #include <cassert> 19 #include <string> 20 #include <type_traits> 21 #include <variant> 22 #include <vector> 23 #include <memory> 24 25 #include "test_macros.h" 26 #include "variant_test_helpers.h" 27 28 namespace MetaHelpers { 29 30 struct Dummy { 31 Dummy() = default; 32 }; 33 34 struct ThrowsCtorT { 35 ThrowsCtorT(int) noexcept(false) {} 36 ThrowsCtorT &operator=(int) noexcept { return *this; } 37 }; 38 39 struct ThrowsAssignT { 40 ThrowsAssignT(int) noexcept {} 41 ThrowsAssignT &operator=(int) noexcept(false) { return *this; } 42 }; 43 44 struct NoThrowT { 45 NoThrowT(int) noexcept {} 46 NoThrowT &operator=(int) noexcept { return *this; } 47 }; 48 49 } // namespace MetaHelpers 50 51 namespace RuntimeHelpers { 52 #ifndef TEST_HAS_NO_EXCEPTIONS 53 54 struct ThrowsCtorT { 55 int value; 56 ThrowsCtorT() : value(0) {} 57 ThrowsCtorT(int) noexcept(false) { throw 42; } 58 ThrowsCtorT &operator=(int v) noexcept { 59 value = v; 60 return *this; 61 } 62 }; 63 64 struct MoveCrashes { 65 int value; 66 MoveCrashes(int v = 0) noexcept : value{v} {} 67 MoveCrashes(MoveCrashes &&) noexcept { assert(false); } 68 MoveCrashes &operator=(MoveCrashes &&) noexcept { assert(false); return *this; } 69 MoveCrashes &operator=(int v) noexcept { 70 value = v; 71 return *this; 72 } 73 }; 74 75 struct ThrowsCtorTandMove { 76 int value; 77 ThrowsCtorTandMove() : value(0) {} 78 ThrowsCtorTandMove(int) noexcept(false) { throw 42; } 79 ThrowsCtorTandMove(ThrowsCtorTandMove &&) noexcept(false) { assert(false); } 80 ThrowsCtorTandMove &operator=(int v) noexcept { 81 value = v; 82 return *this; 83 } 84 }; 85 86 struct ThrowsAssignT { 87 int value; 88 ThrowsAssignT() : value(0) {} 89 ThrowsAssignT(int v) noexcept : value(v) {} 90 ThrowsAssignT &operator=(int) noexcept(false) { throw 42; } 91 }; 92 93 struct NoThrowT { 94 int value; 95 NoThrowT() : value(0) {} 96 NoThrowT(int v) noexcept : value(v) {} 97 NoThrowT &operator=(int v) noexcept { 98 value = v; 99 return *this; 100 } 101 }; 102 103 #endif // !defined(TEST_HAS_NO_EXCEPTIONS) 104 } // namespace RuntimeHelpers 105 106 void test_T_assignment_noexcept() { 107 using namespace MetaHelpers; 108 { 109 using V = std::variant<Dummy, NoThrowT>; 110 static_assert(std::is_nothrow_assignable<V, int>::value, ""); 111 } 112 { 113 using V = std::variant<Dummy, ThrowsCtorT>; 114 static_assert(!std::is_nothrow_assignable<V, int>::value, ""); 115 } 116 { 117 using V = std::variant<Dummy, ThrowsAssignT>; 118 static_assert(!std::is_nothrow_assignable<V, int>::value, ""); 119 } 120 } 121 122 void test_T_assignment_sfinae() { 123 { 124 using V = std::variant<long, long long>; 125 static_assert(!std::is_assignable<V, int>::value, "ambiguous"); 126 } 127 { 128 using V = std::variant<std::string, std::string>; 129 static_assert(!std::is_assignable<V, const char *>::value, "ambiguous"); 130 } 131 { 132 using V = std::variant<std::string, void *>; 133 static_assert(!std::is_assignable<V, int>::value, "no matching operator="); 134 } 135 { 136 using V = std::variant<std::string, float>; 137 static_assert(!std::is_assignable<V, int>::value, "no matching operator="); 138 } 139 { 140 using V = std::variant<std::unique_ptr<int>, bool>; 141 static_assert(!std::is_assignable<V, std::unique_ptr<char>>::value, 142 "no explicit bool in operator="); 143 struct X { 144 operator void*(); 145 }; 146 static_assert(!std::is_assignable<V, X>::value, "no boolean conversion in operator="); 147 static_assert(std::is_assignable<V, std::false_type>::value, "converted to bool in operator="); 148 } 149 { 150 struct X {}; 151 struct Y { 152 operator X(); 153 }; 154 using V = std::variant<X>; 155 static_assert(std::is_assignable<V, Y>::value, 156 "regression on user-defined conversions in operator="); 157 } 158 #if !defined(TEST_VARIANT_HAS_NO_REFERENCES) 159 { 160 using V = std::variant<int, int &&>; 161 static_assert(!std::is_assignable<V, int>::value, "ambiguous"); 162 } 163 { 164 using V = std::variant<int, const int &>; 165 static_assert(!std::is_assignable<V, int>::value, "ambiguous"); 166 } 167 #endif // TEST_VARIANT_HAS_NO_REFERENCES 168 } 169 170 void test_T_assignment_basic() { 171 { 172 std::variant<int> v(43); 173 v = 42; 174 assert(v.index() == 0); 175 assert(std::get<0>(v) == 42); 176 } 177 { 178 std::variant<int, long> v(43l); 179 v = 42; 180 assert(v.index() == 0); 181 assert(std::get<0>(v) == 42); 182 v = 43l; 183 assert(v.index() == 1); 184 assert(std::get<1>(v) == 43); 185 } 186 { 187 std::variant<unsigned, long> v; 188 v = 42; 189 assert(v.index() == 1); 190 assert(std::get<1>(v) == 42); 191 v = 43u; 192 assert(v.index() == 0); 193 assert(std::get<0>(v) == 43); 194 } 195 { 196 std::variant<std::string, bool> v = true; 197 v = "bar"; 198 assert(v.index() == 0); 199 assert(std::get<0>(v) == "bar"); 200 } 201 { 202 std::variant<bool, std::unique_ptr<int>> v; 203 v = nullptr; 204 assert(v.index() == 1); 205 assert(std::get<1>(v) == nullptr); 206 } 207 #if !defined(TEST_VARIANT_HAS_NO_REFERENCES) 208 { 209 using V = std::variant<int &, int &&, long>; 210 int x = 42; 211 V v(43l); 212 v = x; 213 assert(v.index() == 0); 214 assert(&std::get<0>(v) == &x); 215 v = std::move(x); 216 assert(v.index() == 1); 217 assert(&std::get<1>(v) == &x); 218 // 'long' is selected by FUN(const int &) since 'const int &' cannot bind 219 // to 'int&'. 220 const int &cx = x; 221 v = cx; 222 assert(v.index() == 2); 223 assert(std::get<2>(v) == 42); 224 } 225 #endif // TEST_VARIANT_HAS_NO_REFERENCES 226 } 227 228 void test_T_assignment_performs_construction() { 229 using namespace RuntimeHelpers; 230 #ifndef TEST_HAS_NO_EXCEPTIONS 231 { 232 using V = std::variant<std::string, ThrowsCtorT>; 233 V v(std::in_place_type<std::string>, "hello"); 234 try { 235 v = 42; 236 assert(false); 237 } catch (...) { /* ... */ 238 } 239 assert(v.index() == 0); 240 assert(std::get<0>(v) == "hello"); 241 } 242 { 243 using V = std::variant<ThrowsAssignT, std::string>; 244 V v(std::in_place_type<std::string>, "hello"); 245 v = 42; 246 assert(v.index() == 0); 247 assert(std::get<0>(v).value == 42); 248 } 249 #endif // TEST_HAS_NO_EXCEPTIONS 250 } 251 252 void test_T_assignment_performs_assignment() { 253 using namespace RuntimeHelpers; 254 #ifndef TEST_HAS_NO_EXCEPTIONS 255 { 256 using V = std::variant<ThrowsCtorT>; 257 V v; 258 v = 42; 259 assert(v.index() == 0); 260 assert(std::get<0>(v).value == 42); 261 } 262 { 263 using V = std::variant<ThrowsCtorT, std::string>; 264 V v; 265 v = 42; 266 assert(v.index() == 0); 267 assert(std::get<0>(v).value == 42); 268 } 269 { 270 using V = std::variant<ThrowsAssignT>; 271 V v(100); 272 try { 273 v = 42; 274 assert(false); 275 } catch (...) { /* ... */ 276 } 277 assert(v.index() == 0); 278 assert(std::get<0>(v).value == 100); 279 } 280 { 281 using V = std::variant<std::string, ThrowsAssignT>; 282 V v(100); 283 try { 284 v = 42; 285 assert(false); 286 } catch (...) { /* ... */ 287 } 288 assert(v.index() == 1); 289 assert(std::get<1>(v).value == 100); 290 } 291 #endif // TEST_HAS_NO_EXCEPTIONS 292 } 293 294 void test_T_assignment_vector_bool() { 295 std::vector<bool> vec = {true}; 296 std::variant<bool, int> v; 297 v = vec[0]; 298 assert(v.index() == 0); 299 assert(std::get<0>(v) == true); 300 } 301 302 int main(int, char**) { 303 test_T_assignment_basic(); 304 test_T_assignment_performs_construction(); 305 test_T_assignment_performs_assignment(); 306 test_T_assignment_noexcept(); 307 test_T_assignment_sfinae(); 308 test_T_assignment_vector_bool(); 309 310 return 0; 311 } 312