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