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