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 == VariantAllowsNarrowingConversions, 138 "no matching operator="); 139 } 140 { 141 using V = std::variant<std::unique_ptr<int>, bool>; 142 static_assert(!std::is_assignable<V, std::unique_ptr<char>>::value, 143 "no explicit bool in operator="); 144 struct X { 145 operator void*(); 146 }; 147 static_assert(!std::is_assignable<V, X>::value, 148 "no boolean conversion in operator="); 149 static_assert(std::is_assignable<V, std::false_type>::value, 150 "converted to bool in operator="); 151 } 152 { 153 struct X {}; 154 struct Y { 155 operator X(); 156 }; 157 using V = std::variant<X>; 158 static_assert(std::is_assignable<V, Y>::value, 159 "regression on user-defined conversions in operator="); 160 } 161 #if !defined(TEST_VARIANT_HAS_NO_REFERENCES) 162 { 163 using V = std::variant<int, int &&>; 164 static_assert(!std::is_assignable<V, int>::value, "ambiguous"); 165 } 166 { 167 using V = std::variant<int, const int &>; 168 static_assert(!std::is_assignable<V, int>::value, "ambiguous"); 169 } 170 #endif // TEST_VARIANT_HAS_NO_REFERENCES 171 } 172 173 void test_T_assignment_basic() { 174 { 175 std::variant<int> v(43); 176 v = 42; 177 assert(v.index() == 0); 178 assert(std::get<0>(v) == 42); 179 } 180 { 181 std::variant<int, long> v(43l); 182 v = 42; 183 assert(v.index() == 0); 184 assert(std::get<0>(v) == 42); 185 v = 43l; 186 assert(v.index() == 1); 187 assert(std::get<1>(v) == 43); 188 } 189 #ifndef TEST_VARIANT_ALLOWS_NARROWING_CONVERSIONS 190 { 191 std::variant<unsigned, long> v; 192 v = 42; 193 assert(v.index() == 1); 194 assert(std::get<1>(v) == 42); 195 v = 43u; 196 assert(v.index() == 0); 197 assert(std::get<0>(v) == 43); 198 } 199 #endif 200 { 201 std::variant<std::string, bool> v = true; 202 v = "bar"; 203 assert(v.index() == 0); 204 assert(std::get<0>(v) == "bar"); 205 } 206 { 207 std::variant<bool, std::unique_ptr<int>> v; 208 v = nullptr; 209 assert(v.index() == 1); 210 assert(std::get<1>(v) == nullptr); 211 } 212 #if !defined(TEST_VARIANT_HAS_NO_REFERENCES) 213 { 214 using V = std::variant<int &, int &&, long>; 215 int x = 42; 216 V v(43l); 217 v = x; 218 assert(v.index() == 0); 219 assert(&std::get<0>(v) == &x); 220 v = std::move(x); 221 assert(v.index() == 1); 222 assert(&std::get<1>(v) == &x); 223 // 'long' is selected by FUN(const int &) since 'const int &' cannot bind 224 // to 'int&'. 225 const int &cx = x; 226 v = cx; 227 assert(v.index() == 2); 228 assert(std::get<2>(v) == 42); 229 } 230 #endif // TEST_VARIANT_HAS_NO_REFERENCES 231 } 232 233 void test_T_assignment_performs_construction() { 234 using namespace RuntimeHelpers; 235 #ifndef TEST_HAS_NO_EXCEPTIONS 236 { 237 using V = std::variant<std::string, ThrowsCtorT>; 238 V v(std::in_place_type<std::string>, "hello"); 239 try { 240 v = 42; 241 assert(false); 242 } catch (...) { /* ... */ 243 } 244 assert(v.index() == 0); 245 assert(std::get<0>(v) == "hello"); 246 } 247 { 248 using V = std::variant<ThrowsAssignT, std::string>; 249 V v(std::in_place_type<std::string>, "hello"); 250 v = 42; 251 assert(v.index() == 0); 252 assert(std::get<0>(v).value == 42); 253 } 254 #endif // TEST_HAS_NO_EXCEPTIONS 255 } 256 257 void test_T_assignment_performs_assignment() { 258 using namespace RuntimeHelpers; 259 #ifndef TEST_HAS_NO_EXCEPTIONS 260 { 261 using V = std::variant<ThrowsCtorT>; 262 V v; 263 v = 42; 264 assert(v.index() == 0); 265 assert(std::get<0>(v).value == 42); 266 } 267 { 268 using V = std::variant<ThrowsCtorT, std::string>; 269 V v; 270 v = 42; 271 assert(v.index() == 0); 272 assert(std::get<0>(v).value == 42); 273 } 274 { 275 using V = std::variant<ThrowsAssignT>; 276 V v(100); 277 try { 278 v = 42; 279 assert(false); 280 } catch (...) { /* ... */ 281 } 282 assert(v.index() == 0); 283 assert(std::get<0>(v).value == 100); 284 } 285 { 286 using V = std::variant<std::string, ThrowsAssignT>; 287 V v(100); 288 try { 289 v = 42; 290 assert(false); 291 } catch (...) { /* ... */ 292 } 293 assert(v.index() == 1); 294 assert(std::get<1>(v).value == 100); 295 } 296 #endif // TEST_HAS_NO_EXCEPTIONS 297 } 298 299 void test_T_assignment_vector_bool() { 300 std::vector<bool> vec = {true}; 301 std::variant<bool, int> v; 302 v = vec[0]; 303 assert(v.index() == 0); 304 assert(std::get<0>(v) == true); 305 } 306 307 int main(int, char**) { 308 test_T_assignment_basic(); 309 test_T_assignment_performs_construction(); 310 test_T_assignment_performs_assignment(); 311 test_T_assignment_noexcept(); 312 test_T_assignment_sfinae(); 313 test_T_assignment_vector_bool(); 314 315 return 0; 316 } 317