//===----------------------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // UNSUPPORTED: c++03, c++11, c++14 // // template class variant; // template // variant& operator=(T&&) noexcept(see below); #include #include #include #include #include #include #include "test_macros.h" #include "variant_test_helpers.h" namespace MetaHelpers { struct Dummy { Dummy() = default; }; struct ThrowsCtorT { ThrowsCtorT(int) noexcept(false) {} ThrowsCtorT& operator=(int) noexcept { return *this; } }; struct ThrowsAssignT { ThrowsAssignT(int) noexcept {} ThrowsAssignT& operator=(int) noexcept(false) { return *this; } }; struct NoThrowT { NoThrowT(int) noexcept {} NoThrowT& operator=(int) noexcept { return *this; } }; } // namespace MetaHelpers namespace RuntimeHelpers { #ifndef TEST_HAS_NO_EXCEPTIONS struct ThrowsCtorT { int value; ThrowsCtorT() : value(0) {} ThrowsCtorT(int) noexcept(false) { throw 42; } ThrowsCtorT& operator=(int v) noexcept { value = v; return *this; } }; struct MoveCrashes { int value; MoveCrashes(int v = 0) noexcept : value{v} {} MoveCrashes(MoveCrashes&&) noexcept { assert(false); } MoveCrashes& operator=(MoveCrashes&&) noexcept { assert(false); return *this; } MoveCrashes& operator=(int v) noexcept { value = v; return *this; } }; struct ThrowsCtorTandMove { int value; ThrowsCtorTandMove() : value(0) {} ThrowsCtorTandMove(int) noexcept(false) { throw 42; } ThrowsCtorTandMove(ThrowsCtorTandMove&&) noexcept(false) { assert(false); } ThrowsCtorTandMove& operator=(int v) noexcept { value = v; return *this; } }; struct ThrowsAssignT { int value; ThrowsAssignT() : value(0) {} ThrowsAssignT(int v) noexcept : value(v) {} ThrowsAssignT& operator=(int) noexcept(false) { throw 42; } }; struct NoThrowT { int value; NoThrowT() : value(0) {} NoThrowT(int v) noexcept : value(v) {} NoThrowT& operator=(int v) noexcept { value = v; return *this; } }; #endif // !defined(TEST_HAS_NO_EXCEPTIONS) } // namespace RuntimeHelpers constexpr void test_T_assignment_noexcept() { using namespace MetaHelpers; { using V = std::variant; static_assert(std::is_nothrow_assignable::value, ""); } { using V = std::variant; static_assert(!std::is_nothrow_assignable::value, ""); } { using V = std::variant; static_assert(!std::is_nothrow_assignable::value, ""); } } constexpr void test_T_assignment_sfinae() { { using V = std::variant; static_assert(!std::is_assignable::value, "ambiguous"); } { using V = std::variant; static_assert(!std::is_assignable::value, "ambiguous"); } { using V = std::variant; static_assert(!std::is_assignable::value, "no matching operator="); } { using V = std::variant; static_assert(!std::is_assignable::value, "no matching operator="); } { using V = std::variant, bool>; static_assert(!std::is_assignable>::value, "no explicit bool in operator="); struct X { operator void*(); }; static_assert(!std::is_assignable::value, "no boolean conversion in operator="); static_assert(std::is_assignable::value, "converted to bool in operator="); } { struct X {}; struct Y { operator X(); }; using V = std::variant; static_assert(std::is_assignable::value, "regression on user-defined conversions in operator="); } } TEST_CONSTEXPR_CXX20 void test_T_assignment_basic() { { std::variant v(43); v = 42; assert(v.index() == 0); assert(std::get<0>(v) == 42); } { std::variant v(43l); v = 42; assert(v.index() == 0); assert(std::get<0>(v) == 42); v = 43l; assert(v.index() == 1); assert(std::get<1>(v) == 43); } { std::variant v; v = 42; assert(v.index() == 1); assert(std::get<1>(v) == 42); v = 43u; assert(v.index() == 0); assert(std::get<0>(v) == 43); } { std::variant v = true; v = "bar"; assert(v.index() == 0); assert(std::get<0>(v) == "bar"); } } void test_T_assignment_basic_no_constexpr() { std::variant> v; v = nullptr; assert(v.index() == 1); assert(std::get<1>(v) == nullptr); } struct TraceStat { int construct = 0; int copy_construct = 0; int copy_assign = 0; int move_construct = 0; int move_assign = 0; int T_copy_assign = 0; int T_move_assign = 0; int destroy = 0; }; template struct Trace { struct T {}; constexpr Trace(TraceStat* s) noexcept(CtorNoexcept) : stat(s) { ++s->construct; } constexpr Trace(T) noexcept(CtorNoexcept) : stat(nullptr) {} constexpr Trace(const Trace& o) : stat(o.stat) { ++stat->copy_construct; } constexpr Trace(Trace&& o) noexcept(MoveCtorNoexcept) : stat(o.stat) { ++stat->move_construct; } constexpr Trace& operator=(const Trace&) { ++stat->copy_assign; return *this; } constexpr Trace& operator=(Trace&&) noexcept { ++stat->move_assign; return *this; } constexpr Trace& operator=(const T&) { ++stat->T_copy_assign; return *this; } constexpr Trace& operator=(T&&) noexcept { ++stat->T_move_assign; return *this; } TEST_CONSTEXPR_CXX20 ~Trace() { ++stat->destroy; } TraceStat* stat; }; TEST_CONSTEXPR_CXX20 void test_T_assignment_performs_construction() { { using V = std::variant>; TraceStat stat; V v{1}; v = &stat; assert(stat.construct == 1); assert(stat.copy_construct == 0); assert(stat.move_construct == 0); assert(stat.copy_assign == 0); assert(stat.move_assign == 0); assert(stat.destroy == 0); } { using V = std::variant>; TraceStat stat; V v{1}; v = &stat; assert(stat.construct == 1); assert(stat.copy_construct == 0); assert(stat.move_construct == 1); assert(stat.copy_assign == 0); assert(stat.move_assign == 0); assert(stat.destroy == 1); } { using V = std::variant>; TraceStat stat; V v{1}; v = &stat; assert(stat.construct == 1); assert(stat.copy_construct == 0); assert(stat.move_construct == 0); assert(stat.copy_assign == 0); assert(stat.move_assign == 0); assert(stat.destroy == 0); } { using V = std::variant>; TraceStat stat; V v{1}; v = &stat; assert(stat.construct == 1); assert(stat.copy_construct == 0); assert(stat.move_construct == 0); assert(stat.copy_assign == 0); assert(stat.move_assign == 0); assert(stat.destroy == 0); } } TEST_CONSTEXPR_CXX20 void test_T_assignment_performs_assignment() { { using V = std::variant>; TraceStat stat; V v{&stat}; v = Trace::T{}; assert(stat.construct == 1); assert(stat.copy_construct == 0); assert(stat.move_construct == 0); assert(stat.copy_assign == 0); assert(stat.move_assign == 0); assert(stat.T_copy_assign == 0); assert(stat.T_move_assign == 1); assert(stat.destroy == 0); } { using V = std::variant>; TraceStat stat; V v{&stat}; Trace::T t; v = t; assert(stat.construct == 1); assert(stat.copy_construct == 0); assert(stat.move_construct == 0); assert(stat.copy_assign == 0); assert(stat.move_assign == 0); assert(stat.T_copy_assign == 1); assert(stat.T_move_assign == 0); assert(stat.destroy == 0); } } void test_T_assignment_performs_construction_throw() { using namespace RuntimeHelpers; #ifndef TEST_HAS_NO_EXCEPTIONS { using V = std::variant; V v(std::in_place_type, "hello"); try { v = 42; assert(false); } catch (...) { /* ... */ } assert(v.index() == 0); assert(std::get<0>(v) == "hello"); } { using V = std::variant; V v(std::in_place_type, "hello"); v = 42; assert(v.index() == 0); assert(std::get<0>(v).value == 42); } #endif // TEST_HAS_NO_EXCEPTIONS } void test_T_assignment_performs_assignment_throw() { using namespace RuntimeHelpers; #ifndef TEST_HAS_NO_EXCEPTIONS { using V = std::variant; V v; v = 42; assert(v.index() == 0); assert(std::get<0>(v).value == 42); } { using V = std::variant; V v; v = 42; assert(v.index() == 0); assert(std::get<0>(v).value == 42); } { using V = std::variant; V v(100); try { v = 42; assert(false); } catch (...) { /* ... */ } assert(v.index() == 0); assert(std::get<0>(v).value == 100); } { using V = std::variant; V v(100); try { v = 42; assert(false); } catch (...) { /* ... */ } assert(v.index() == 1); assert(std::get<1>(v).value == 100); } #endif // TEST_HAS_NO_EXCEPTIONS } TEST_CONSTEXPR_CXX20 void test_T_assignment_vector_bool() { std::vector vec = {true}; std::variant v; v = vec[0]; assert(v.index() == 0); assert(std::get<0>(v) == true); } void non_constexpr_test() { test_T_assignment_basic_no_constexpr(); test_T_assignment_performs_construction_throw(); test_T_assignment_performs_assignment_throw(); } TEST_CONSTEXPR_CXX20 bool test() { test_T_assignment_basic(); test_T_assignment_performs_construction(); test_T_assignment_performs_assignment(); test_T_assignment_noexcept(); test_T_assignment_sfinae(); test_T_assignment_vector_bool(); return true; } int main(int, char**) { test(); non_constexpr_test(); #if TEST_STD_VER >= 20 static_assert(test()); #endif return 0; }