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