427 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9// UNSUPPORTED: c++03, c++11, c++1410 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 28namespace MetaHelpers {29 30struct Dummy {31 Dummy() = default;32};33 34struct ThrowsCtorT {35 ThrowsCtorT(int) noexcept(false) {}36 ThrowsCtorT& operator=(int) noexcept { return *this; }37};38 39struct ThrowsAssignT {40 ThrowsAssignT(int) noexcept {}41 ThrowsAssignT& operator=(int) noexcept(false) { return *this; }42};43 44struct NoThrowT {45 NoThrowT(int) noexcept {}46 NoThrowT& operator=(int) noexcept { return *this; }47};48 49} // namespace MetaHelpers50 51namespace RuntimeHelpers {52#ifndef TEST_HAS_NO_EXCEPTIONS53 54struct 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 64struct MoveCrashes {65 int value;66 MoveCrashes(int v = 0) noexcept : value{v} {}67 MoveCrashes(MoveCrashes&&) noexcept { assert(false); }68 MoveCrashes& operator=(MoveCrashes&&) noexcept {69 assert(false);70 return *this;71 }72 MoveCrashes& operator=(int v) noexcept {73 value = v;74 return *this;75 }76};77 78struct ThrowsCtorTandMove {79 int value;80 ThrowsCtorTandMove() : value(0) {}81 ThrowsCtorTandMove(int) noexcept(false) { throw 42; }82 ThrowsCtorTandMove(ThrowsCtorTandMove&&) noexcept(false) { assert(false); }83 ThrowsCtorTandMove& operator=(int v) noexcept {84 value = v;85 return *this;86 }87};88 89struct ThrowsAssignT {90 int value;91 ThrowsAssignT() : value(0) {}92 ThrowsAssignT(int v) noexcept : value(v) {}93 ThrowsAssignT& operator=(int) noexcept(false) { throw 42; }94};95 96struct NoThrowT {97 int value;98 NoThrowT() : value(0) {}99 NoThrowT(int v) noexcept : value(v) {}100 NoThrowT& operator=(int v) noexcept {101 value = v;102 return *this;103 }104};105 106#endif // !defined(TEST_HAS_NO_EXCEPTIONS)107} // namespace RuntimeHelpers108 109constexpr void test_T_assignment_noexcept() {110 using namespace MetaHelpers;111 {112 using V = std::variant<Dummy, NoThrowT>;113 static_assert(std::is_nothrow_assignable<V, int>::value, "");114 }115 {116 using V = std::variant<Dummy, ThrowsCtorT>;117 static_assert(!std::is_nothrow_assignable<V, int>::value, "");118 }119 {120 using V = std::variant<Dummy, ThrowsAssignT>;121 static_assert(!std::is_nothrow_assignable<V, int>::value, "");122 }123}124 125constexpr void test_T_assignment_sfinae() {126 {127 using V = std::variant<long, long long>;128 static_assert(!std::is_assignable<V, int>::value, "ambiguous");129 }130 {131 using V = std::variant<std::string, std::string>;132 static_assert(!std::is_assignable<V, const char*>::value, "ambiguous");133 }134 {135 using V = std::variant<std::string, void*>;136 static_assert(!std::is_assignable<V, int>::value, "no matching operator=");137 }138 {139 using V = std::variant<std::string, float>;140 static_assert(!std::is_assignable<V, int>::value, "no matching operator=");141 }142 {143 using V = std::variant<std::unique_ptr<int>, bool>;144 static_assert(!std::is_assignable<V, std::unique_ptr<char>>::value, "no explicit bool in operator=");145 struct X {146 operator void*();147 };148 static_assert(!std::is_assignable<V, X>::value, "no boolean conversion in operator=");149 static_assert(std::is_assignable<V, std::false_type>::value, "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, "regression on user-defined conversions in operator=");158 }159}160 161TEST_CONSTEXPR_CXX20 void test_T_assignment_basic() {162 {163 std::variant<int> v(43);164 v = 42;165 assert(v.index() == 0);166 assert(std::get<0>(v) == 42);167 }168 {169 std::variant<int, long> v(43l);170 v = 42;171 assert(v.index() == 0);172 assert(std::get<0>(v) == 42);173 v = 43l;174 assert(v.index() == 1);175 assert(std::get<1>(v) == 43);176 }177 {178 std::variant<unsigned, long> v;179 v = 42;180 assert(v.index() == 1);181 assert(std::get<1>(v) == 42);182 v = 43u;183 assert(v.index() == 0);184 assert(std::get<0>(v) == 43);185 }186 {187 std::variant<std::string, bool> v = true;188 v = "bar";189 assert(v.index() == 0);190 assert(std::get<0>(v) == "bar");191 }192}193 194void test_T_assignment_basic_no_constexpr() {195 std::variant<bool, std::unique_ptr<int>> v;196 v = nullptr;197 assert(v.index() == 1);198 assert(std::get<1>(v) == nullptr);199}200 201struct TraceStat {202 int construct = 0;203 int copy_construct = 0;204 int copy_assign = 0;205 int move_construct = 0;206 int move_assign = 0;207 int T_copy_assign = 0;208 int T_move_assign = 0;209 int destroy = 0;210};211 212template <bool CtorNoexcept, bool MoveCtorNoexcept>213struct Trace {214 struct T {};215 216 constexpr Trace(TraceStat* s) noexcept(CtorNoexcept) : stat(s) { ++s->construct; }217 constexpr Trace(T) noexcept(CtorNoexcept) : stat(nullptr) {}218 constexpr Trace(const Trace& o) : stat(o.stat) { ++stat->copy_construct; }219 constexpr Trace(Trace&& o) noexcept(MoveCtorNoexcept) : stat(o.stat) { ++stat->move_construct; }220 constexpr Trace& operator=(const Trace&) {221 ++stat->copy_assign;222 return *this;223 }224 constexpr Trace& operator=(Trace&&) noexcept {225 ++stat->move_assign;226 return *this;227 }228 229 constexpr Trace& operator=(const T&) {230 ++stat->T_copy_assign;231 return *this;232 }233 constexpr Trace& operator=(T&&) noexcept {234 ++stat->T_move_assign;235 return *this;236 }237 TEST_CONSTEXPR_CXX20 ~Trace() { ++stat->destroy; }238 239 TraceStat* stat;240};241 242TEST_CONSTEXPR_CXX20 void test_T_assignment_performs_construction() {243 {244 using V = std::variant<int, Trace<false, false>>;245 TraceStat stat;246 V v{1};247 v = &stat;248 assert(stat.construct == 1);249 assert(stat.copy_construct == 0);250 assert(stat.move_construct == 0);251 assert(stat.copy_assign == 0);252 assert(stat.move_assign == 0);253 assert(stat.destroy == 0);254 }255 {256 using V = std::variant<int, Trace<false, true>>;257 TraceStat stat;258 V v{1};259 v = &stat;260 assert(stat.construct == 1);261 assert(stat.copy_construct == 0);262 assert(stat.move_construct == 1);263 assert(stat.copy_assign == 0);264 assert(stat.move_assign == 0);265 assert(stat.destroy == 1);266 }267 268 {269 using V = std::variant<int, Trace<true, false>>;270 TraceStat stat;271 V v{1};272 v = &stat;273 assert(stat.construct == 1);274 assert(stat.copy_construct == 0);275 assert(stat.move_construct == 0);276 assert(stat.copy_assign == 0);277 assert(stat.move_assign == 0);278 assert(stat.destroy == 0);279 }280 281 {282 using V = std::variant<int, Trace<true, true>>;283 TraceStat stat;284 V v{1};285 v = &stat;286 assert(stat.construct == 1);287 assert(stat.copy_construct == 0);288 assert(stat.move_construct == 0);289 assert(stat.copy_assign == 0);290 assert(stat.move_assign == 0);291 assert(stat.destroy == 0);292 }293}294 295TEST_CONSTEXPR_CXX20 void test_T_assignment_performs_assignment() {296 {297 using V = std::variant<int, Trace<false, false>>;298 TraceStat stat;299 V v{&stat};300 v = Trace<false, false>::T{};301 assert(stat.construct == 1);302 assert(stat.copy_construct == 0);303 assert(stat.move_construct == 0);304 assert(stat.copy_assign == 0);305 assert(stat.move_assign == 0);306 assert(stat.T_copy_assign == 0);307 assert(stat.T_move_assign == 1);308 assert(stat.destroy == 0);309 }310 {311 using V = std::variant<int, Trace<false, false>>;312 TraceStat stat;313 V v{&stat};314 Trace<false, false>::T t;315 v = t;316 assert(stat.construct == 1);317 assert(stat.copy_construct == 0);318 assert(stat.move_construct == 0);319 assert(stat.copy_assign == 0);320 assert(stat.move_assign == 0);321 assert(stat.T_copy_assign == 1);322 assert(stat.T_move_assign == 0);323 assert(stat.destroy == 0);324 }325}326 327void test_T_assignment_performs_construction_throw() {328 using namespace RuntimeHelpers;329#ifndef TEST_HAS_NO_EXCEPTIONS330 {331 using V = std::variant<std::string, ThrowsCtorT>;332 V v(std::in_place_type<std::string>, "hello");333 try {334 v = 42;335 assert(false);336 } catch (...) { /* ... */337 }338 assert(v.index() == 0);339 assert(std::get<0>(v) == "hello");340 }341 {342 using V = std::variant<ThrowsAssignT, std::string>;343 V v(std::in_place_type<std::string>, "hello");344 v = 42;345 assert(v.index() == 0);346 assert(std::get<0>(v).value == 42);347 }348#endif // TEST_HAS_NO_EXCEPTIONS349}350 351void test_T_assignment_performs_assignment_throw() {352 using namespace RuntimeHelpers;353#ifndef TEST_HAS_NO_EXCEPTIONS354 {355 using V = std::variant<ThrowsCtorT>;356 V v;357 v = 42;358 assert(v.index() == 0);359 assert(std::get<0>(v).value == 42);360 }361 {362 using V = std::variant<ThrowsCtorT, std::string>;363 V v;364 v = 42;365 assert(v.index() == 0);366 assert(std::get<0>(v).value == 42);367 }368 {369 using V = std::variant<ThrowsAssignT>;370 V v(100);371 try {372 v = 42;373 assert(false);374 } catch (...) { /* ... */375 }376 assert(v.index() == 0);377 assert(std::get<0>(v).value == 100);378 }379 {380 using V = std::variant<std::string, ThrowsAssignT>;381 V v(100);382 try {383 v = 42;384 assert(false);385 } catch (...) { /* ... */386 }387 assert(v.index() == 1);388 assert(std::get<1>(v).value == 100);389 }390#endif // TEST_HAS_NO_EXCEPTIONS391}392 393TEST_CONSTEXPR_CXX20 void test_T_assignment_vector_bool() {394 std::vector<bool> vec = {true};395 std::variant<bool, int> v;396 v = vec[0];397 assert(v.index() == 0);398 assert(std::get<0>(v) == true);399}400 401void non_constexpr_test() {402 test_T_assignment_basic_no_constexpr();403 test_T_assignment_performs_construction_throw();404 test_T_assignment_performs_assignment_throw();405}406 407TEST_CONSTEXPR_CXX20 bool test() {408 test_T_assignment_basic();409 test_T_assignment_performs_construction();410 test_T_assignment_performs_assignment();411 test_T_assignment_noexcept();412 test_T_assignment_sfinae();413 test_T_assignment_vector_bool();414 415 return true;416}417 418int main(int, char**) {419 test();420 non_constexpr_test();421 422#if TEST_STD_VER >= 20423 static_assert(test());424#endif425 return 0;426}427