CaseyCarter updated this revision to Diff 101497. CaseyCarter retitled this revision from "[libcxx] [test] variant: test coverage for P0602 extension" to "[libcxx] [test] variant: test coverage for LWG2904 and P0602". CaseyCarter edited the summary of this revision. CaseyCarter added a comment.
Massive update: Move the libcxx tree tests into std, merge my LWG2904 differential. https://reviews.llvm.org/D32671 Files: test/libcxx/utilities/variant/variant.variant/variant.assign/copy.pass.cpp test/libcxx/utilities/variant/variant.variant/variant.assign/move.pass.cpp test/libcxx/utilities/variant/variant.variant/variant.ctor/copy.pass.cpp test/libcxx/utilities/variant/variant.variant/variant.ctor/move.pass.cpp test/std/utilities/variant/variant.variant/variant.assign/T.pass.cpp test/std/utilities/variant/variant.variant/variant.assign/copy.pass.cpp test/std/utilities/variant/variant.variant/variant.assign/move.pass.cpp test/std/utilities/variant/variant.variant/variant.ctor/copy.pass.cpp test/std/utilities/variant/variant.variant/variant.ctor/move.pass.cpp test/support/variant_test_helpers.hpp
Index: test/support/variant_test_helpers.hpp =================================================================== --- test/support/variant_test_helpers.hpp +++ test/support/variant_test_helpers.hpp @@ -69,9 +69,9 @@ void makeEmpty(Variant& v) { Variant v2(std::in_place_type<MakeEmptyT>); try { - v = v2; + v = std::move(v2); assert(false); - } catch (...) { + } catch (...) { assert(v.valueless_by_exception()); } } Index: test/std/utilities/variant/variant.variant/variant.ctor/move.pass.cpp =================================================================== --- test/std/utilities/variant/variant.variant/variant.ctor/move.pass.cpp +++ test/std/utilities/variant/variant.variant/variant.ctor/move.pass.cpp @@ -53,6 +53,34 @@ MoveOnlyNT(MoveOnlyNT &&other) : value(other.value) { other.value = -1; } }; +struct NTMove { + constexpr NTMove(int v) : value(v) {} + NTMove(const NTMove &) = delete; + NTMove(NTMove &&that) : value(that.value) { that.value = -1; } + int value; +}; + +static_assert(!std::is_trivially_move_constructible<NTMove>::value, ""); +static_assert(std::is_move_constructible<NTMove>::value, ""); + +struct TMove { + constexpr TMove(int v) : value(v) {} + TMove(const TMove &) = delete; + TMove(TMove &&) = default; + int value; +}; + +static_assert(std::is_trivially_move_constructible<TMove>::value, ""); + +struct TMoveNTCopy { + constexpr TMoveNTCopy(int v) : value(v) {} + TMoveNTCopy(const TMoveNTCopy& that) : value(that.value) {} + TMoveNTCopy(TMoveNTCopy&&) = default; + int value; +}; + +static_assert(std::is_trivially_move_constructible<TMoveNTCopy>::value, ""); + #ifndef TEST_HAS_NO_EXCEPTIONS struct MakeEmptyT { static int alive; @@ -73,7 +101,7 @@ template <class Variant> void makeEmpty(Variant &v) { Variant v2(std::in_place_type<MakeEmptyT>); try { - v = v2; + v = std::move(v2); assert(false); } catch (...) { assert(v.valueless_by_exception()); @@ -117,8 +145,30 @@ using V = std::variant<int, NoCopy>; static_assert(!std::is_move_constructible<V>::value, ""); } + + // The following tests are for not-yet-standardized behavior (P0602): + { + using V = std::variant<int, long>; + static_assert(std::is_trivially_move_constructible<V>::value, ""); + } + { + using V = std::variant<int, NTMove>; + static_assert(!std::is_trivially_move_constructible<V>::value, ""); + static_assert(std::is_move_constructible<V>::value, ""); + } + { + using V = std::variant<int, TMove>; + static_assert(std::is_trivially_move_constructible<V>::value, ""); + } + { + using V = std::variant<int, TMoveNTCopy>; + static_assert(std::is_trivially_move_constructible<V>::value, ""); + } } +template <typename T> +struct Result { size_t index; T value; }; + void test_move_ctor_basic() { { std::variant<int> v(std::in_place_index<0>, 42); @@ -162,6 +212,80 @@ assert(std::get<1>(v).value == -1); assert(std::get<1>(v2).value == 42); } + + // The following tests are for not-yet-standardized behavior (P0602): + { + struct { + constexpr Result<int> operator()() const { + std::variant<int> v(std::in_place_index<0>, 42); + std::variant<int> v2 = std::move(v); + return {v2.index(), std::get<0>(std::move(v2))}; + } + } test; + constexpr auto result = test(); + static_assert(result.index == 0, ""); + static_assert(result.value == 42, ""); + } + { + struct { + constexpr Result<long> operator()() const { + std::variant<int, long> v(std::in_place_index<1>, 42); + std::variant<int, long> v2 = std::move(v); + return {v2.index(), std::get<1>(std::move(v2))}; + } + } test; + constexpr auto result = test(); + static_assert(result.index == 1, ""); + static_assert(result.value == 42, ""); + } + { + struct { + constexpr Result<TMove> operator()() const { + std::variant<TMove> v(std::in_place_index<0>, 42); + std::variant<TMove> v2(std::move(v)); + return {v2.index(), std::get<0>(std::move(v2))}; + } + } test; + constexpr auto result = test(); + static_assert(result.index == 0, ""); + static_assert(result.value.value == 42, ""); + } + { + struct { + constexpr Result<TMove> operator()() const { + std::variant<int, TMove> v(std::in_place_index<1>, 42); + std::variant<int, TMove> v2(std::move(v)); + return {v2.index(), std::get<1>(std::move(v2))}; + } + } test; + constexpr auto result = test(); + static_assert(result.index == 1, ""); + static_assert(result.value.value == 42, ""); + } + { + struct { + constexpr Result<TMoveNTCopy> operator()() const { + std::variant<TMoveNTCopy> v(std::in_place_index<0>, 42); + std::variant<TMoveNTCopy> v2(std::move(v)); + return {v2.index(), std::get<0>(std::move(v2))}; + } + } test; + constexpr auto result = test(); + static_assert(result.index == 0, ""); + static_assert(result.value.value == 42, ""); + } + { + struct { + constexpr Result<TMoveNTCopy> operator()() const { + std::variant<int, TMoveNTCopy> v(std::in_place_index<1>, 42); + std::variant<int, TMoveNTCopy> v2(std::move(v)); + return {v2.index(), std::get<1>(std::move(v2))}; + } + } test; + constexpr auto result = test(); + static_assert(result.index == 1, ""); + static_assert(result.value.value == 42, ""); + } } void test_move_ctor_valueless_by_exception() { @@ -171,7 +295,7 @@ makeEmpty(v1); V v(std::move(v1)); assert(v.valueless_by_exception()); -#endif +#endif // TEST_HAS_NO_EXCEPTIONS } template <size_t Idx> @@ -186,17 +310,17 @@ } void test_constexpr_move_ctor_extension() { - // NOTE: This test is for not yet standardized behavior. + // NOTE: This test is for not yet standardized behavior. (P0602) using V = std::variant<long, void*, const int>; #ifdef TEST_WORKAROUND_C1XX_BROKEN_IS_TRIVIALLY_COPYABLE static_assert(std::is_trivially_destructible<V>::value, ""); static_assert(std::is_trivially_copy_constructible<V>::value, ""); static_assert(std::is_trivially_move_constructible<V>::value, ""); static_assert(!std::is_copy_assignable<V>::value, ""); static_assert(!std::is_move_assignable<V>::value, ""); -#else +#else // TEST_WORKAROUND_C1XX_BROKEN_IS_TRIVIALLY_COPYABLE static_assert(std::is_trivially_copyable<V>::value, ""); -#endif +#endif // TEST_WORKAROUND_C1XX_BROKEN_IS_TRIVIALLY_COPYABLE static_assert(std::is_trivially_move_constructible<V>::value, ""); static_assert(test_constexpr_ctor_extension_imp<0>(V(42l)), ""); static_assert(test_constexpr_ctor_extension_imp<1>(V(nullptr)), ""); Index: test/std/utilities/variant/variant.variant/variant.ctor/copy.pass.cpp =================================================================== --- test/std/utilities/variant/variant.variant/variant.ctor/copy.pass.cpp +++ test/std/utilities/variant/variant.variant/variant.ctor/copy.pass.cpp @@ -51,6 +51,34 @@ MoveOnlyNT(MoveOnlyNT &&) {} }; +struct NTCopy { + constexpr NTCopy(int v) : value(v) {} + NTCopy(const NTCopy &that) : value(that.value) {} + NTCopy(NTCopy &&) = delete; + int value; +}; + +static_assert(!std::is_trivially_copy_constructible<NTCopy>::value, ""); +static_assert(std::is_copy_constructible<NTCopy>::value, ""); + +struct TCopy { + constexpr TCopy(int v) : value(v) {} + TCopy(TCopy const &) = default; + TCopy(TCopy &&) = delete; + int value; +}; + +static_assert(std::is_trivially_copy_constructible<TCopy>::value, ""); + +struct TCopyNTMove { + constexpr TCopyNTMove(int v) : value(v) {} + TCopyNTMove(const TCopyNTMove&) = default; + TCopyNTMove(TCopyNTMove&& that) : value(that.value) { that.value = -1; } + int value; +}; + +static_assert(std::is_trivially_copy_constructible<TCopyNTMove>::value, ""); + #ifndef TEST_HAS_NO_EXCEPTIONS struct MakeEmptyT { static int alive; @@ -71,7 +99,7 @@ template <class Variant> void makeEmpty(Variant &v) { Variant v2(std::in_place_type<MakeEmptyT>); try { - v = v2; + v = std::move(v2); assert(false); } catch (...) { assert(v.valueless_by_exception()); @@ -96,6 +124,25 @@ using V = std::variant<int, MoveOnlyNT>; static_assert(!std::is_copy_constructible<V>::value, ""); } + + // The following tests are for not-yet-standardized behavior (P0602): + { + using V = std::variant<int, long>; + static_assert(std::is_trivially_copy_constructible<V>::value, ""); + } + { + using V = std::variant<int, NTCopy>; + static_assert(!std::is_trivially_copy_constructible<V>::value, ""); + static_assert(std::is_copy_constructible<V>::value, ""); + } + { + using V = std::variant<int, TCopy>; + static_assert(std::is_trivially_copy_constructible<V>::value, ""); + } + { + using V = std::variant<int, TCopyNTMove>; + static_assert(std::is_trivially_copy_constructible<V>::value, ""); + } } void test_copy_ctor_basic() { @@ -125,6 +172,50 @@ assert(v2.index() == 1); assert(std::get<1>(v2).value == 42); } + + // The following tests are for not-yet-standardized behavior (P0602): + { + constexpr std::variant<int> v(std::in_place_index<0>, 42); + static_assert(v.index() == 0, ""); + constexpr std::variant<int> v2 = v; + static_assert(v2.index() == 0, ""); + static_assert(std::get<0>(v2) == 42, ""); + } + { + constexpr std::variant<int, long> v(std::in_place_index<1>, 42); + static_assert(v.index() == 1, ""); + constexpr std::variant<int, long> v2 = v; + static_assert(v2.index() == 1, ""); + static_assert(std::get<1>(v2) == 42, ""); + } + { + constexpr std::variant<TCopy> v(std::in_place_index<0>, 42); + static_assert(v.index() == 0, ""); + constexpr std::variant<TCopy> v2(v); + static_assert(v2.index() == 0, ""); + static_assert(std::get<0>(v2).value == 42, ""); + } + { + constexpr std::variant<int, TCopy> v(std::in_place_index<1>, 42); + static_assert(v.index() == 1, ""); + constexpr std::variant<int, TCopy> v2(v); + static_assert(v2.index() == 1, ""); + static_assert(std::get<1>(v2).value == 42, ""); + } + { + constexpr std::variant<TCopyNTMove> v(std::in_place_index<0>, 42); + static_assert(v.index() == 0, ""); + constexpr std::variant<TCopyNTMove> v2(v); + static_assert(v2.index() == 0, ""); + static_assert(std::get<0>(v2).value == 42, ""); + } + { + constexpr std::variant<int, TCopyNTMove> v(std::in_place_index<1>, 42); + static_assert(v.index() == 1, ""); + constexpr std::variant<int, TCopyNTMove> v2(v); + static_assert(v2.index() == 1, ""); + static_assert(std::get<1>(v2).value == 42, ""); + } } void test_copy_ctor_valueless_by_exception() { @@ -135,7 +226,7 @@ const V &cv1 = v1; V v(cv1); assert(v.valueless_by_exception()); -#endif +#endif // TEST_HAS_NO_EXCEPTIONS } template <size_t Idx> @@ -149,17 +240,17 @@ } void test_constexpr_copy_ctor_extension() { - // NOTE: This test is for not yet standardized behavior. + // NOTE: This test is for not yet standardized behavior. (P0602) using V = std::variant<long, void*, const int>; #ifdef TEST_WORKAROUND_C1XX_BROKEN_IS_TRIVIALLY_COPYABLE static_assert(std::is_trivially_destructible<V>::value, ""); static_assert(std::is_trivially_copy_constructible<V>::value, ""); static_assert(std::is_trivially_move_constructible<V>::value, ""); static_assert(!std::is_copy_assignable<V>::value, ""); static_assert(!std::is_move_assignable<V>::value, ""); -#else +#else // TEST_WORKAROUND_C1XX_BROKEN_IS_TRIVIALLY_COPYABLE static_assert(std::is_trivially_copyable<V>::value, ""); -#endif +#endif // TEST_WORKAROUND_C1XX_BROKEN_IS_TRIVIALLY_COPYABLE static_assert(test_constexpr_copy_ctor_extension_imp<0>(V(42l)), ""); static_assert(test_constexpr_copy_ctor_extension_imp<1>(V(nullptr)), ""); static_assert(test_constexpr_copy_ctor_extension_imp<2>(V(101)), ""); Index: test/std/utilities/variant/variant.variant/variant.assign/move.pass.cpp =================================================================== --- test/std/utilities/variant/variant.variant/variant.assign/move.pass.cpp +++ test/std/utilities/variant/variant.variant/variant.assign/move.pass.cpp @@ -10,6 +10,10 @@ // UNSUPPORTED: c++98, c++03, c++11, c++14 +// The following compilers don't generate constexpr special members correctly. +// XFAIL: clang-3.5, clang-3.6, clang-3.7, clang-3.8 +// XFAIL: apple-clang-6, apple-clang-7, apple-clang-8.0 + // XFAIL: with_system_cxx_lib=macosx10.12 // XFAIL: with_system_cxx_lib=macosx10.11 // XFAIL: with_system_cxx_lib=macosx10.10 @@ -91,6 +95,60 @@ int MoveAssign::move_construct = 0; int MoveAssign::move_assign = 0; +struct NTMoveAssign { + constexpr NTMoveAssign(int v) : value(v) {} + NTMoveAssign(const NTMoveAssign &) = default; + NTMoveAssign(NTMoveAssign &&) = default; + NTMoveAssign &operator=(const NTMoveAssign &that) = default; + NTMoveAssign &operator=(NTMoveAssign &&that) { + value = that.value; + that.value = -1; + return *this; + }; + int value; +}; + +static_assert(!std::is_trivially_move_assignable<NTMoveAssign>::value, ""); +static_assert(std::is_move_assignable<NTMoveAssign>::value, ""); + +struct TMoveAssign { + constexpr TMoveAssign(int v) : value(v) {} + TMoveAssign(const TMoveAssign &) = delete; + TMoveAssign(TMoveAssign &&) = default; + TMoveAssign &operator=(const TMoveAssign &) = delete; + TMoveAssign &operator=(TMoveAssign &&) = default; + int value; +}; + +static_assert(std::is_trivially_move_assignable<TMoveAssign>::value, ""); + +struct TMoveAssignNTCopyAssign { + constexpr TMoveAssignNTCopyAssign(int v) : value(v) {} + TMoveAssignNTCopyAssign(const TMoveAssignNTCopyAssign &) = default; + TMoveAssignNTCopyAssign(TMoveAssignNTCopyAssign &&) = default; + TMoveAssignNTCopyAssign &operator=(const TMoveAssignNTCopyAssign &that) { + value = that.value; + return *this; + } + TMoveAssignNTCopyAssign &operator=(TMoveAssignNTCopyAssign &&) = default; + int value; +}; + +static_assert(std::is_trivially_move_assignable_v<TMoveAssignNTCopyAssign>, ""); + +struct TrivialCopyNontrivialMove { + TrivialCopyNontrivialMove(TrivialCopyNontrivialMove const&) = default; + TrivialCopyNontrivialMove(TrivialCopyNontrivialMove&&) noexcept {} + TrivialCopyNontrivialMove& operator=(TrivialCopyNontrivialMove const&) = default; + TrivialCopyNontrivialMove& operator=(TrivialCopyNontrivialMove&&) noexcept { + return *this; + } +}; + +static_assert(std::is_trivially_copy_assignable_v<TrivialCopyNontrivialMove>, ""); +static_assert(!std::is_trivially_move_assignable_v<TrivialCopyNontrivialMove>, ""); + + void test_move_assignment_noexcept() { { using V = std::variant<int>; @@ -124,10 +182,14 @@ static_assert(std::is_move_assignable<V>::value, ""); } { + using V = std::variant<int, CopyOnly>; +#ifdef _LIBCPP_VERSION // LWG2904 // variant only provides move assignment when both the move constructor // and move assignment operator are well formed. - using V = std::variant<int, CopyOnly>; static_assert(!std::is_move_assignable<V>::value, ""); +#else // _LIBCPP_VERSION // LWG2904 + static_assert(std::is_move_assignable<V>::value, ""); +#endif // _LIBCPP_VERSION // LWG2904 } { using V = std::variant<int, NoCopy>; @@ -147,6 +209,35 @@ using V = std::variant<int, MoveAssignOnly>; static_assert(!std::is_move_assignable<V>::value, ""); } + + // The following tests are for not-yet-standardized behavior (P0602): + { + using V = std::variant<int, long>; + static_assert(std::is_trivially_move_assignable<V>::value, ""); + } + { + using V = std::variant<int, NTMoveAssign>; + static_assert(!std::is_trivially_move_assignable<V>::value, ""); + static_assert(std::is_move_assignable<V>::value, ""); + } + { + using V = std::variant<int, TMoveAssign>; + static_assert(std::is_trivially_move_assignable<V>::value, ""); + } + { + using V = std::variant<int, TMoveAssignNTCopyAssign>; + static_assert(std::is_trivially_move_assignable<V>::value, ""); + } + { + using V = std::variant<int, TrivialCopyNontrivialMove>; + static_assert(!std::is_trivially_move_assignable<V>::value, ""); + } +#ifndef _LIBCPP_VERSION // LWG2904 + { + using V = std::variant<int, CopyOnly>; + static_assert(std::is_trivially_move_assignable<V>::value, ""); + } +#endif // _LIBCPP_VERSION // LWG2904 } void test_move_assignment_empty_empty() { @@ -163,7 +254,7 @@ assert(v1.valueless_by_exception()); assert(v1.index() == std::variant_npos); } -#endif +#endif // TEST_HAS_NO_EXCEPTIONS } void test_move_assignment_non_empty_empty() { @@ -189,7 +280,7 @@ assert(v1.valueless_by_exception()); assert(v1.index() == std::variant_npos); } -#endif +#endif // TEST_HAS_NO_EXCEPTIONS } void test_move_assignment_empty_non_empty() { @@ -215,9 +306,11 @@ assert(v1.index() == 2); assert(std::get<2>(v1) == "hello"); } -#endif +#endif // TEST_HAS_NO_EXCEPTIONS } +template <typename T> struct Result { size_t index; T value; }; + void test_move_assignment_same_index() { { using V = std::variant<int>; @@ -264,7 +357,51 @@ assert(v1.index() == 1); assert(&std::get<1>(v1) == &mref); } -#endif +#endif // TEST_HAS_NO_EXCEPTIONS + + // The following tests are for not-yet-standardized behavior (P0602): + { + struct { + constexpr Result<int> operator()() const { + using V = std::variant<int>; + V v(43); + V v2(42); + v = std::move(v2); + return {v.index(), std::get<0>(v)}; + } + } test; + constexpr auto result = test(); + static_assert(result.index == 0, ""); + static_assert(result.value == 42, ""); + } + { + struct { + constexpr Result<long> operator()() const { + using V = std::variant<int, long, unsigned>; + V v(43l); + V v2(42l); + v = std::move(v2); + return {v.index(), std::get<1>(v)}; + } + } test; + constexpr auto result = test(); + static_assert(result.index == 1, ""); + static_assert(result.value == 42l, ""); + } + { + struct { + constexpr Result<int> operator()() const { + using V = std::variant<int, TMoveAssign, unsigned>; + V v(std::in_place_type<TMoveAssign>, 43); + V v2(std::in_place_type<TMoveAssign>, 42); + v = std::move(v2); + return {v.index(), std::get<1>(v).value}; + } + } test; + constexpr auto result = test(); + static_assert(result.index == 1, ""); + static_assert(result.value == 42, ""); + } } void test_move_assignment_different_index() { @@ -312,7 +449,60 @@ assert(v1.index() == 2); assert(std::get<2>(v1) == "hello"); } -#endif +#endif // TEST_HAS_NO_EXCEPTIONS + + // The following tests are for not-yet-standardized behavior (P0602): + { + struct { + constexpr Result<long> operator()() const { + using V = std::variant<int, long, unsigned>; + V v(43); + V v2(42l); + v = std::move(v2); + return {v.index(), std::get<1>(v)}; + } + } test; + constexpr auto result = test(); + static_assert(result.index == 1, ""); + static_assert(result.value == 42l, ""); + } + { + struct { + constexpr Result<long> operator()() const { + using V = std::variant<int, TMoveAssign, unsigned>; + V v(std::in_place_type<unsigned>, 43); + V v2(std::in_place_type<TMoveAssign>, 42); + v = std::move(v2); + return {v.index(), std::get<1>(v).value}; + } + } test; + constexpr auto result = test(); + static_assert(result.index == 1, ""); + static_assert(result.value == 42, ""); + } +} + +template <size_t NewIdx, class ValueType> +constexpr bool test_constexpr_assign_extension_imp( + std::variant<long, void*, int>&& v, ValueType&& new_value) +{ + std::variant<long, void*, int> v2( + std::forward<ValueType>(new_value)); + const auto cp = v2; + v = std::move(v2); + return v.index() == NewIdx && + std::get<NewIdx>(v) == std::get<NewIdx>(cp); +} + +void test_constexpr_move_assignment_extension() { + // The following tests are for not-yet-standardized behavior (P0602): + using V = std::variant<long, void*, int>; + static_assert(std::is_trivially_copyable<V>::value, ""); + static_assert(std::is_trivially_move_assignable<V>::value, ""); + static_assert(test_constexpr_assign_extension_imp<0>(V(42l), 101l), ""); + static_assert(test_constexpr_assign_extension_imp<0>(V(nullptr), 101l), ""); + static_assert(test_constexpr_assign_extension_imp<1>(V(42l), nullptr), ""); + static_assert(test_constexpr_assign_extension_imp<2>(V(42l), 101), ""); } int main() { @@ -323,4 +513,5 @@ test_move_assignment_different_index(); test_move_assignment_sfinae(); test_move_assignment_noexcept(); + test_constexpr_move_assignment_extension(); } Index: test/std/utilities/variant/variant.variant/variant.assign/copy.pass.cpp =================================================================== --- test/std/utilities/variant/variant.variant/variant.assign/copy.pass.cpp +++ test/std/utilities/variant/variant.variant/variant.assign/copy.pass.cpp @@ -10,6 +10,10 @@ // UNSUPPORTED: c++98, c++03, c++11, c++14 +// The following compilers don't generate constexpr special members correctly. +// XFAIL: clang-3.5, clang-3.6, clang-3.7, clang-3.8 +// XFAIL: apple-clang-6, apple-clang-7, apple-clang-8.0 + // XFAIL: with_system_cxx_lib=macosx10.12 // XFAIL: with_system_cxx_lib=macosx10.11 // XFAIL: with_system_cxx_lib=macosx10.10 @@ -35,11 +39,6 @@ NoCopy &operator=(const NoCopy &) = default; }; -struct NothrowCopy { - NothrowCopy(const NothrowCopy &) noexcept = default; - NothrowCopy &operator=(const NothrowCopy &) noexcept = default; -}; - struct CopyOnly { CopyOnly(const CopyOnly &) = default; CopyOnly(CopyOnly &&) = delete; @@ -73,7 +72,7 @@ ++alive; ++copy_construct; } - CopyAssign(CopyAssign &&o) : value(o.value) { + CopyAssign(CopyAssign &&o) noexcept : value(o.value) { o.value = -1; ++alive; ++move_construct; @@ -83,7 +82,7 @@ ++copy_assign; return *this; } - CopyAssign &operator=(CopyAssign &&o) { + CopyAssign &operator=(CopyAssign &&o) noexcept { value = o.value; o.value = -1; ++move_assign; @@ -115,6 +114,17 @@ CopyThrows &operator=(const CopyThrows &) { throw 42; } }; +struct CopyCannotThrow { + static int alive; + CopyCannotThrow() { ++alive; } + CopyCannotThrow(const CopyCannotThrow &) noexcept { ++alive; } + CopyCannotThrow(CopyCannotThrow &&) noexcept { assert(false); } + CopyCannotThrow &operator=(const CopyCannotThrow &) noexcept = default; + CopyCannotThrow &operator=(CopyCannotThrow &&) noexcept { assert(false); return *this; } +}; + +int CopyCannotThrow::alive = 0; + struct MoveThrows { static int alive; MoveThrows() { ++alive; } @@ -127,6 +137,47 @@ int MoveThrows::alive = 0; +struct NTCopyAssign { + constexpr NTCopyAssign(int v) : value(v) {} + NTCopyAssign(const NTCopyAssign &) = default; + NTCopyAssign(NTCopyAssign &&) = default; + NTCopyAssign &operator=(const NTCopyAssign &that) { + value = that.value; + return *this; + }; + NTCopyAssign &operator=(NTCopyAssign &&) = delete; + int value; +}; + +static_assert(!std::is_trivially_copy_assignable<NTCopyAssign>::value, ""); +static_assert(std::is_copy_assignable<NTCopyAssign>::value, ""); + +struct TCopyAssign { + constexpr TCopyAssign(int v) : value(v) {} + TCopyAssign(const TCopyAssign &) = default; + TCopyAssign(TCopyAssign &&) = default; + TCopyAssign &operator=(const TCopyAssign &) = default; + TCopyAssign &operator=(TCopyAssign &&) = delete; + int value; +}; + +static_assert(std::is_trivially_copy_assignable<TCopyAssign>::value, ""); + +struct TCopyAssignNTMoveAssign { + constexpr TCopyAssignNTMoveAssign(int v) : value(v) {} + TCopyAssignNTMoveAssign(const TCopyAssignNTMoveAssign &) = default; + TCopyAssignNTMoveAssign(TCopyAssignNTMoveAssign &&) = default; + TCopyAssignNTMoveAssign &operator=(const TCopyAssignNTMoveAssign &) = default; + TCopyAssignNTMoveAssign &operator=(TCopyAssignNTMoveAssign &&that) { + value = that.value; + that.value = -1; + return *this; + } + int value; +}; + +static_assert(std::is_trivially_copy_assignable_v<TCopyAssignNTMoveAssign>, ""); + struct MakeEmptyT { static int alive; MakeEmptyT() { ++alive; } @@ -146,7 +197,7 @@ template <class Variant> void makeEmpty(Variant &v) { Variant v2(std::in_place_type<MakeEmptyT>); try { - v = v2; + v = std::move(v2); assert(false); } catch (...) { assert(v.valueless_by_exception()); @@ -171,10 +222,14 @@ static_assert(std::is_copy_assignable<V>::value, ""); } { + using V = std::variant<int, CopyOnly>; +#ifdef _LIBCPP_VERSION // LWG2904 // variant only provides copy assignment when both the copy and move // constructors are well formed - using V = std::variant<int, CopyOnly>; static_assert(!std::is_copy_assignable<V>::value, ""); +#else // _LIBCPP_VERSION // LWG2904 + static_assert(std::is_copy_assignable<V>::value, ""); +#endif // _LIBCPP_VERSION // LWG2904 } { using V = std::variant<int, NoCopy>; @@ -188,6 +243,31 @@ using V = std::variant<int, MoveOnlyNT>; static_assert(!std::is_copy_assignable<V>::value, ""); } + + // The following tests are for not-yet-standardized behavior (P0602): + { + using V = std::variant<int, long>; + static_assert(std::is_trivially_copy_assignable<V>::value, ""); + } + { + using V = std::variant<int, NTCopyAssign>; + static_assert(!std::is_trivially_copy_assignable<V>::value, ""); + static_assert(std::is_copy_assignable<V>::value, ""); + } + { + using V = std::variant<int, TCopyAssign>; + static_assert(std::is_trivially_copy_assignable<V>::value, ""); + } + { + using V = std::variant<int, TCopyAssignNTMoveAssign>; + static_assert(std::is_trivially_copy_assignable<V>::value, ""); + } +#ifndef _LIBCPP_VERSION // LWG2904 + { + using V = std::variant<int, CopyOnly>; + static_assert(std::is_trivially_copy_assignable<V>::value, ""); + } +#endif // _LIBCPP_VERSION } void test_copy_assignment_empty_empty() { @@ -204,7 +284,7 @@ assert(v1.valueless_by_exception()); assert(v1.index() == std::variant_npos); } -#endif +#endif // TEST_HAS_NO_EXCEPTIONS } void test_copy_assignment_non_empty_empty() { @@ -230,7 +310,7 @@ assert(v1.valueless_by_exception()); assert(v1.index() == std::variant_npos); } -#endif +#endif // TEST_HAS_NO_EXCEPTIONS } void test_copy_assignment_empty_non_empty() { @@ -256,9 +336,11 @@ assert(v1.index() == 2); assert(std::get<2>(v1) == "hello"); } -#endif +#endif // TEST_HAS_NO_EXCEPTIONS } +template <typename T> struct Result { size_t index; T value; }; + void test_copy_assignment_same_index() { { using V = std::variant<int>; @@ -306,7 +388,65 @@ assert(v1.index() == 1); assert(&std::get<1>(v1) == &mref); } -#endif +#endif // TEST_HAS_NO_EXCEPTIONS + + // The following tests are for not-yet-standardized behavior (P0602): + { + struct { + constexpr Result<int> operator()() const { + using V = std::variant<int>; + V v(43); + V v2(42); + v = v2; + return {v.index(), std::get<0>(v)}; + } + } test; + constexpr auto result = test(); + static_assert(result.index == 0, ""); + static_assert(result.value == 42, ""); + } + { + struct { + constexpr Result<long> operator()() const { + using V = std::variant<int, long, unsigned>; + V v(43l); + V v2(42l); + v = v2; + return {v.index(), std::get<1>(v)}; + } + } test; + constexpr auto result = test(); + static_assert(result.index == 1, ""); + static_assert(result.value == 42l, ""); + } + { + struct { + constexpr Result<int> operator()() const { + using V = std::variant<int, TCopyAssign, unsigned>; + V v(std::in_place_type<TCopyAssign>, 43); + V v2(std::in_place_type<TCopyAssign>, 42); + v = v2; + return {v.index(), std::get<1>(v).value}; + } + } test; + constexpr auto result = test(); + static_assert(result.index == 1, ""); + static_assert(result.value == 42, ""); + } + { + struct { + constexpr Result<int> operator()() const { + using V = std::variant<int, TCopyAssignNTMoveAssign, unsigned>; + V v(std::in_place_type<TCopyAssignNTMoveAssign>, 43); + V v2(std::in_place_type<TCopyAssignNTMoveAssign>, 42); + v = v2; + return {v.index(), std::get<1>(v).value}; + } + } test; + constexpr auto result = test(); + static_assert(result.index == 1, ""); + static_assert(result.value == 42, ""); + } } void test_copy_assignment_different_index() { @@ -338,35 +478,61 @@ } #ifndef TEST_HAS_NO_EXCEPTIONS { - // Test that if copy construction throws then original value is - // unchanged. using V = std::variant<int, CopyThrows, std::string>; V v1(std::in_place_type<std::string>, "hello"); V v2(std::in_place_type<CopyThrows>); try { v1 = v2; assert(false); } catch (...) { /* ... */ } +#ifdef _LIBCPP_VERSION // LWG2904 + // Test that if copy construction throws then original value is unchanged. assert(v1.index() == 2); assert(std::get<2>(v1) == "hello"); +#else // _LIBCPP_VERSION // LWG2904 + // Test that copy construction is used directly if move construction may throw, + // resulting in a valueless variant if copy throws. + assert(v1.valueless_by_exception()); +#endif // _LIBCPP_VERSION // LWG2904 } { - // Test that if move construction throws then the variant is left - // valueless by exception. using V = std::variant<int, MoveThrows, std::string>; V v1(std::in_place_type<std::string>, "hello"); V v2(std::in_place_type<MoveThrows>); assert(MoveThrows::alive == 1); +#ifdef _LIBCPP_VERSION // LWG2904 + // Test that if move construction throws then the variant is left + // valueless by exception. try { v1 = v2; assert(false); } catch (...) { /* ... */ } assert(v1.valueless_by_exception()); assert(v2.index() == 1); assert(MoveThrows::alive == 1); +#else // _LIBCPP_VERSION // LWG2904 + // Test that copy construction is used directly if move construction may throw. + v1 = v2; + assert(v1.index() == 1); + assert(v2.index() == 1); + assert(MoveThrows::alive == 2); +#endif // _LIBCPP_VERSION // LWG2904 + } +#ifndef _LIBCPP_VERSION // LWG2904 + { + // Test that direct copy construction is preferred when it cannot throw. + using V = std::variant<int, CopyCannotThrow, std::string>; + V v1(std::in_place_type<std::string>, "hello"); + V v2(std::in_place_type<CopyCannotThrow>); + assert(CopyCannotThrow::alive == 1); + v1 = v2; + assert(v1.index() == 1); + assert(v2.index() == 1); + assert(CopyCannotThrow::alive == 2); } +#endif // _LIBCPP_VERSION // LWG2904 { using V = std::variant<int, CopyThrows, std::string>; V v1(std::in_place_type<CopyThrows>); @@ -389,9 +555,60 @@ assert(v2.index() == 2); assert(std::get<2>(v2) == "hello"); } -#endif +#endif // TEST_HAS_NO_EXCEPTIONS + + // The following tests are for not-yet-standardized behavior (P0602): + { + struct { + constexpr Result<long> operator()() const { + using V = std::variant<int, long, unsigned>; + V v(43); + V v2(42l); + v = v2; + return {v.index(), std::get<1>(v)}; + } + } test; + constexpr auto result = test(); + static_assert(result.index == 1, ""); + static_assert(result.value == 42l, ""); + } + { + struct { + constexpr Result<int> operator()() const { + using V = std::variant<int, TCopyAssign, unsigned>; + V v(std::in_place_type<unsigned>, 43); + V v2(std::in_place_type<TCopyAssign>, 42); + v = v2; + return {v.index(), std::get<1>(v).value}; + } + } test; + constexpr auto result = test(); + static_assert(result.index == 1, ""); + static_assert(result.value == 42, ""); + } +} + +template <size_t NewIdx, class ValueType> +constexpr bool test_constexpr_assign_extension_imp( + std::variant<long, void*, int>&& v, ValueType&& new_value) +{ + const std::variant<long, void*, int> cp( + std::forward<ValueType>(new_value)); + v = cp; + return v.index() == NewIdx && + std::get<NewIdx>(v) == std::get<NewIdx>(cp); } +void test_constexpr_copy_assignment_extension() { + // The following tests are for not-yet-standardized behavior (P0602): + using V = std::variant<long, void*, int>; + static_assert(std::is_trivially_copyable<V>::value, ""); + static_assert(std::is_trivially_copy_assignable<V>::value, ""); + static_assert(test_constexpr_assign_extension_imp<0>(V(42l), 101l), ""); + static_assert(test_constexpr_assign_extension_imp<0>(V(nullptr), 101l), ""); + static_assert(test_constexpr_assign_extension_imp<1>(V(42l), nullptr), ""); + static_assert(test_constexpr_assign_extension_imp<2>(V(42l), 101), ""); +} int main() { test_copy_assignment_empty_empty(); @@ -401,4 +618,5 @@ test_copy_assignment_different_index(); test_copy_assignment_sfinae(); test_copy_assignment_not_noexcept(); + test_constexpr_copy_assignment_extension(); } Index: test/std/utilities/variant/variant.variant/variant.assign/T.pass.cpp =================================================================== --- test/std/utilities/variant/variant.variant/variant.assign/T.pass.cpp +++ test/std/utilities/variant/variant.variant/variant.assign/T.pass.cpp @@ -68,6 +68,28 @@ } }; +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) {} @@ -126,7 +148,7 @@ using V = std::variant<int, const int &>; static_assert(!std::is_assignable<V, int>::value, "ambiguous"); } -#endif +#endif // TEST_VARIANT_HAS_NO_REFERENCES } void test_T_assignment_basic() { @@ -163,7 +185,7 @@ assert(v.index() == 2); assert(std::get<2>(v) == 42); } -#endif +#endif // TEST_VARIANT_HAS_NO_REFERENCES } void test_T_assignment_performs_construction() { @@ -174,18 +196,46 @@ V v(std::in_place_type<std::string>, "hello"); try { v = 42; + assert(false); } catch (...) { /* ... */ } +#ifdef _LIBCPP_VERSION // LWG2904 assert(v.valueless_by_exception()); +#else // _LIBCPP_VERSION + assert(v.index() == 0); + assert(std::get<0>(v) == "hello"); +#endif // _LIBCPP_VERSION } { using V = std::variant<ThrowsAssignT, std::string>; V v(std::in_place_type<std::string>, "hello"); v = 42; assert(v.index() == 0); assert(std::get<0>(v).value == 42); } -#endif +#ifdef _LIBCPP_VERSION // LWG2904 + { + // Test that nothrow direct construction is preferred to nothrow move. + using V = std::variant<MoveCrashes, std::string>; + V v(std::in_place_type<std::string>, "hello"); + v = 42; + assert(v.index() == 0); + assert(std::get<0>(v).value == 42); + } + { + // Test that throwing direct construction is preferred to copy-and-move when + // move can throw. + using V = std::variant<ThrowsCtorTandMove, std::string>; + V v(std::in_place_type<std::string>, "hello"); + try { + v = 42; + assert(false); + } catch(...) { /* ... */ + } + assert(v.valueless_by_exception()); + } +#endif // _LIBCPP_VERSION // LWG2904 +#endif // TEST_HAS_NO_EXCEPTIONS } void test_T_assignment_performs_assignment() { @@ -227,7 +277,7 @@ assert(v.index() == 1); assert(std::get<1>(v).value == 100); } -#endif +#endif // TEST_HAS_NO_EXCEPTIONS } int main() { Index: test/libcxx/utilities/variant/variant.variant/variant.ctor/move.pass.cpp =================================================================== --- test/libcxx/utilities/variant/variant.variant/variant.ctor/move.pass.cpp +++ /dev/null @@ -1,153 +0,0 @@ -// -*- C++ -*- -//===----------------------------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is dual licensed under the MIT and the University of Illinois Open -// Source Licenses. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++98, c++03, c++11, c++14 - -// <variant> - -// template <class ...Types> class variant; - -// variant(variant&&) noexcept(see below); - -#include <type_traits> -#include <variant> - -#include "test_macros.h" - -struct NTMove { - constexpr NTMove(int v) : value(v) {} - NTMove(const NTMove &) = delete; - NTMove(NTMove &&that) : value(that.value) { that.value = -1; } - int value; -}; - -static_assert(!std::is_trivially_move_constructible<NTMove>::value, ""); -static_assert(std::is_move_constructible<NTMove>::value, ""); - -struct TMove { - constexpr TMove(int v) : value(v) {} - TMove(const TMove &) = delete; - TMove(TMove &&) = default; - int value; -}; - -static_assert(std::is_trivially_move_constructible<TMove>::value, ""); - -struct TMoveNTCopy { - constexpr TMoveNTCopy(int v) : value(v) {} - TMoveNTCopy(const TMoveNTCopy& that) : value(that.value) {} - TMoveNTCopy(TMoveNTCopy&&) = default; - int value; -}; - -static_assert(std::is_trivially_move_constructible<TMoveNTCopy>::value, ""); - -void test_move_ctor_sfinae() { - { - using V = std::variant<int, long>; - static_assert(std::is_trivially_move_constructible<V>::value, ""); - } - { - using V = std::variant<int, NTMove>; - static_assert(!std::is_trivially_move_constructible<V>::value, ""); - static_assert(std::is_move_constructible<V>::value, ""); - } - { - using V = std::variant<int, TMove>; - static_assert(std::is_trivially_move_constructible<V>::value, ""); - } - { - using V = std::variant<int, TMoveNTCopy>; - static_assert(std::is_trivially_move_constructible<V>::value, ""); - } -} - -template <typename T> -struct Result { size_t index; T value; }; - -void test_move_ctor_basic() { - { - struct { - constexpr Result<int> operator()() const { - std::variant<int> v(std::in_place_index<0>, 42); - std::variant<int> v2 = std::move(v); - return {v2.index(), std::get<0>(std::move(v2))}; - } - } test; - constexpr auto result = test(); - static_assert(result.index == 0, ""); - static_assert(result.value == 42, ""); - } - { - struct { - constexpr Result<long> operator()() const { - std::variant<int, long> v(std::in_place_index<1>, 42); - std::variant<int, long> v2 = std::move(v); - return {v2.index(), std::get<1>(std::move(v2))}; - } - } test; - constexpr auto result = test(); - static_assert(result.index == 1, ""); - static_assert(result.value == 42, ""); - } - { - struct { - constexpr Result<TMove> operator()() const { - std::variant<TMove> v(std::in_place_index<0>, 42); - std::variant<TMove> v2(std::move(v)); - return {v2.index(), std::get<0>(std::move(v2))}; - } - } test; - constexpr auto result = test(); - static_assert(result.index == 0, ""); - static_assert(result.value.value == 42, ""); - } - { - struct { - constexpr Result<TMove> operator()() const { - std::variant<int, TMove> v(std::in_place_index<1>, 42); - std::variant<int, TMove> v2(std::move(v)); - return {v2.index(), std::get<1>(std::move(v2))}; - } - } test; - constexpr auto result = test(); - static_assert(result.index == 1, ""); - static_assert(result.value.value == 42, ""); - } - { - struct { - constexpr Result<TMoveNTCopy> operator()() const { - std::variant<TMoveNTCopy> v(std::in_place_index<0>, 42); - std::variant<TMoveNTCopy> v2(std::move(v)); - return {v2.index(), std::get<0>(std::move(v2))}; - } - } test; - constexpr auto result = test(); - static_assert(result.index == 0, ""); - static_assert(result.value.value == 42, ""); - } - { - struct { - constexpr Result<TMoveNTCopy> operator()() const { - std::variant<int, TMoveNTCopy> v(std::in_place_index<1>, 42); - std::variant<int, TMoveNTCopy> v2(std::move(v)); - return {v2.index(), std::get<1>(std::move(v2))}; - } - } test; - constexpr auto result = test(); - static_assert(result.index == 1, ""); - static_assert(result.value.value == 42, ""); - } -} - -int main() { - test_move_ctor_basic(); - test_move_ctor_sfinae(); -} Index: test/libcxx/utilities/variant/variant.variant/variant.ctor/copy.pass.cpp =================================================================== --- test/libcxx/utilities/variant/variant.variant/variant.ctor/copy.pass.cpp +++ /dev/null @@ -1,120 +0,0 @@ -// -*- C++ -*- -//===----------------------------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is dual licensed under the MIT and the University of Illinois Open -// Source Licenses. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++98, c++03, c++11, c++14 - -// <variant> - -// template <class ...Types> class variant; - -// variant(variant const&); - -#include <type_traits> -#include <variant> - -#include "test_macros.h" - -struct NTCopy { - constexpr NTCopy(int v) : value(v) {} - NTCopy(const NTCopy &that) : value(that.value) {} - NTCopy(NTCopy &&) = delete; - int value; -}; - -static_assert(!std::is_trivially_copy_constructible<NTCopy>::value, ""); -static_assert(std::is_copy_constructible<NTCopy>::value, ""); - -struct TCopy { - constexpr TCopy(int v) : value(v) {} - TCopy(TCopy const &) = default; - TCopy(TCopy &&) = delete; - int value; -}; - -static_assert(std::is_trivially_copy_constructible<TCopy>::value, ""); - -struct TCopyNTMove { - constexpr TCopyNTMove(int v) : value(v) {} - TCopyNTMove(const TCopyNTMove&) = default; - TCopyNTMove(TCopyNTMove&& that) : value(that.value) { that.value = -1; } - int value; -}; - -static_assert(std::is_trivially_copy_constructible<TCopyNTMove>::value, ""); - -void test_copy_ctor_sfinae() { - { - using V = std::variant<int, long>; - static_assert(std::is_trivially_copy_constructible<V>::value, ""); - } - { - using V = std::variant<int, NTCopy>; - static_assert(!std::is_trivially_copy_constructible<V>::value, ""); - static_assert(std::is_copy_constructible<V>::value, ""); - } - { - using V = std::variant<int, TCopy>; - static_assert(std::is_trivially_copy_constructible<V>::value, ""); - } - { - using V = std::variant<int, TCopyNTMove>; - static_assert(std::is_trivially_copy_constructible<V>::value, ""); - } -} - -void test_copy_ctor_basic() { - { - constexpr std::variant<int> v(std::in_place_index<0>, 42); - static_assert(v.index() == 0, ""); - constexpr std::variant<int> v2 = v; - static_assert(v2.index() == 0, ""); - static_assert(std::get<0>(v2) == 42, ""); - } - { - constexpr std::variant<int, long> v(std::in_place_index<1>, 42); - static_assert(v.index() == 1, ""); - constexpr std::variant<int, long> v2 = v; - static_assert(v2.index() == 1, ""); - static_assert(std::get<1>(v2) == 42, ""); - } - { - constexpr std::variant<TCopy> v(std::in_place_index<0>, 42); - static_assert(v.index() == 0, ""); - constexpr std::variant<TCopy> v2(v); - static_assert(v2.index() == 0, ""); - static_assert(std::get<0>(v2).value == 42, ""); - } - { - constexpr std::variant<int, TCopy> v(std::in_place_index<1>, 42); - static_assert(v.index() == 1, ""); - constexpr std::variant<int, TCopy> v2(v); - static_assert(v2.index() == 1, ""); - static_assert(std::get<1>(v2).value == 42, ""); - } - { - constexpr std::variant<TCopyNTMove> v(std::in_place_index<0>, 42); - static_assert(v.index() == 0, ""); - constexpr std::variant<TCopyNTMove> v2(v); - static_assert(v2.index() == 0, ""); - static_assert(std::get<0>(v2).value == 42, ""); - } - { - constexpr std::variant<int, TCopyNTMove> v(std::in_place_index<1>, 42); - static_assert(v.index() == 1, ""); - constexpr std::variant<int, TCopyNTMove> v2(v); - static_assert(v2.index() == 1, ""); - static_assert(std::get<1>(v2).value == 42, ""); - } -} - -int main() { - test_copy_ctor_basic(); - test_copy_ctor_sfinae(); -} Index: test/libcxx/utilities/variant/variant.variant/variant.assign/move.pass.cpp =================================================================== --- test/libcxx/utilities/variant/variant.variant/variant.assign/move.pass.cpp +++ /dev/null @@ -1,197 +0,0 @@ -// -*- C++ -*- -//===----------------------------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is dual licensed under the MIT and the University of Illinois Open -// Source Licenses. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++98, c++03, c++11, c++14 - -// The following compilers don't generate constexpr special members correctly. -// XFAIL: clang-3.5, clang-3.6, clang-3.7, clang-3.8 -// XFAIL: apple-clang-6, apple-clang-7, apple-clang-8.0 - -// <variant> - -// template <class ...Types> class variant; - -// variant& operator=(variant&&) noexcept(see below); - -#include <type_traits> -#include <variant> - -#include "test_macros.h" - -struct NTMoveAssign { - constexpr NTMoveAssign(int v) : value(v) {} - NTMoveAssign(const NTMoveAssign &) = default; - NTMoveAssign(NTMoveAssign &&) = default; - NTMoveAssign &operator=(const NTMoveAssign &that) = default; - NTMoveAssign &operator=(NTMoveAssign &&that) { - value = that.value; - that.value = -1; - return *this; - }; - int value; -}; - -static_assert(!std::is_trivially_move_assignable<NTMoveAssign>::value, ""); -static_assert(std::is_move_assignable<NTMoveAssign>::value, ""); - -struct TMoveAssign { - constexpr TMoveAssign(int v) : value(v) {} - TMoveAssign(const TMoveAssign &) = delete; - TMoveAssign(TMoveAssign &&) = default; - TMoveAssign &operator=(const TMoveAssign &) = delete; - TMoveAssign &operator=(TMoveAssign &&) = default; - int value; -}; - -static_assert(std::is_trivially_move_assignable<TMoveAssign>::value, ""); - -struct TMoveAssignNTCopyAssign { - constexpr TMoveAssignNTCopyAssign(int v) : value(v) {} - TMoveAssignNTCopyAssign(const TMoveAssignNTCopyAssign &) = default; - TMoveAssignNTCopyAssign(TMoveAssignNTCopyAssign &&) = default; - TMoveAssignNTCopyAssign &operator=(const TMoveAssignNTCopyAssign &that) { - value = that.value; - return *this; - } - TMoveAssignNTCopyAssign &operator=(TMoveAssignNTCopyAssign &&) = default; - int value; -}; - -static_assert(std::is_trivially_move_assignable_v<TMoveAssignNTCopyAssign>, ""); - -void test_move_assignment_sfinae() { - { - using V = std::variant<int, long>; - static_assert(std::is_trivially_move_assignable<V>::value, ""); - } - { - using V = std::variant<int, NTMoveAssign>; - static_assert(!std::is_trivially_move_assignable<V>::value, ""); - static_assert(std::is_move_assignable<V>::value, ""); - } - { - using V = std::variant<int, TMoveAssign>; - static_assert(std::is_trivially_move_assignable<V>::value, ""); - } - { - using V = std::variant<int, TMoveAssignNTCopyAssign>; - static_assert(std::is_trivially_move_assignable<V>::value, ""); - } -} - -template <typename T> struct Result { size_t index; T value; }; - -void test_move_assignment_same_index() { - { - struct { - constexpr Result<int> operator()() const { - using V = std::variant<int>; - V v(43); - V v2(42); - v = std::move(v2); - return {v.index(), std::get<0>(v)}; - } - } test; - constexpr auto result = test(); - static_assert(result.index == 0, ""); - static_assert(result.value == 42, ""); - } - { - struct { - constexpr Result<long> operator()() const { - using V = std::variant<int, long, unsigned>; - V v(43l); - V v2(42l); - v = std::move(v2); - return {v.index(), std::get<1>(v)}; - } - } test; - constexpr auto result = test(); - static_assert(result.index == 1, ""); - static_assert(result.value == 42l, ""); - } - { - struct { - constexpr Result<int> operator()() const { - using V = std::variant<int, TMoveAssign, unsigned>; - V v(std::in_place_type<TMoveAssign>, 43); - V v2(std::in_place_type<TMoveAssign>, 42); - v = std::move(v2); - return {v.index(), std::get<1>(v).value}; - } - } test; - constexpr auto result = test(); - static_assert(result.index == 1, ""); - static_assert(result.value == 42, ""); - } -} - -void test_move_assignment_different_index() { - { - struct { - constexpr Result<long> operator()() const { - using V = std::variant<int, long, unsigned>; - V v(43); - V v2(42l); - v = std::move(v2); - return {v.index(), std::get<1>(v)}; - } - } test; - constexpr auto result = test(); - static_assert(result.index == 1, ""); - static_assert(result.value == 42l, ""); - } - { - struct { - constexpr Result<long> operator()() const { - using V = std::variant<int, TMoveAssign, unsigned>; - V v(std::in_place_type<unsigned>, 43); - V v2(std::in_place_type<TMoveAssign>, 42); - v = std::move(v2); - return {v.index(), std::get<1>(v).value}; - } - } test; - constexpr auto result = test(); - static_assert(result.index == 1, ""); - static_assert(result.value == 42, ""); - } -} - - -template <size_t NewIdx, class ValueType> -constexpr bool test_constexpr_assign_extension_imp( - std::variant<long, void*, int>&& v, ValueType&& new_value) -{ - std::variant<long, void*, int> v2( - std::forward<ValueType>(new_value)); - const auto cp = v2; - v = std::move(v2); - return v.index() == NewIdx && - std::get<NewIdx>(v) == std::get<NewIdx>(cp); -} - -void test_constexpr_move_assignment_extension() { -#ifdef _LIBCPP_VERSION - using V = std::variant<long, void*, int>; - static_assert(std::is_trivially_copyable<V>::value, ""); - static_assert(std::is_trivially_move_assignable<V>::value, ""); - static_assert(test_constexpr_assign_extension_imp<0>(V(42l), 101l), ""); - static_assert(test_constexpr_assign_extension_imp<0>(V(nullptr), 101l), ""); - static_assert(test_constexpr_assign_extension_imp<1>(V(42l), nullptr), ""); - static_assert(test_constexpr_assign_extension_imp<2>(V(42l), 101), ""); -#endif -} - -int main() { - test_move_assignment_same_index(); - test_move_assignment_different_index(); - test_move_assignment_sfinae(); - test_constexpr_move_assignment_extension(); -} Index: test/libcxx/utilities/variant/variant.variant/variant.assign/copy.pass.cpp =================================================================== --- test/libcxx/utilities/variant/variant.variant/variant.assign/copy.pass.cpp +++ /dev/null @@ -1,209 +0,0 @@ -// -*- C++ -*- -//===----------------------------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is dual licensed under the MIT and the University of Illinois Open -// Source Licenses. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++98, c++03, c++11, c++14 - -// The following compilers don't generate constexpr special members correctly. -// XFAIL: clang-3.5, clang-3.6, clang-3.7, clang-3.8 -// XFAIL: apple-clang-6, apple-clang-7, apple-clang-8.0 - -// <variant> - -// template <class ...Types> class variant; - -// variant& operator=(variant const&); - -#include <type_traits> -#include <variant> - -#include "test_macros.h" - -struct NTCopyAssign { - constexpr NTCopyAssign(int v) : value(v) {} - NTCopyAssign(const NTCopyAssign &) = default; - NTCopyAssign(NTCopyAssign &&) = default; - NTCopyAssign &operator=(const NTCopyAssign &that) { - value = that.value; - return *this; - }; - NTCopyAssign &operator=(NTCopyAssign &&) = delete; - int value; -}; - -static_assert(!std::is_trivially_copy_assignable<NTCopyAssign>::value, ""); -static_assert(std::is_copy_assignable<NTCopyAssign>::value, ""); - -struct TCopyAssign { - constexpr TCopyAssign(int v) : value(v) {} - TCopyAssign(const TCopyAssign &) = default; - TCopyAssign(TCopyAssign &&) = default; - TCopyAssign &operator=(const TCopyAssign &) = default; - TCopyAssign &operator=(TCopyAssign &&) = delete; - int value; -}; - -static_assert(std::is_trivially_copy_assignable<TCopyAssign>::value, ""); - -struct TCopyAssignNTMoveAssign { - constexpr TCopyAssignNTMoveAssign(int v) : value(v) {} - TCopyAssignNTMoveAssign(const TCopyAssignNTMoveAssign &) = default; - TCopyAssignNTMoveAssign(TCopyAssignNTMoveAssign &&) = default; - TCopyAssignNTMoveAssign &operator=(const TCopyAssignNTMoveAssign &) = default; - TCopyAssignNTMoveAssign &operator=(TCopyAssignNTMoveAssign &&that) { - value = that.value; - that.value = -1; - return *this; - } - int value; -}; - -static_assert(std::is_trivially_copy_assignable_v<TCopyAssignNTMoveAssign>, ""); - -void test_copy_assignment_sfinae() { - { - using V = std::variant<int, long>; - static_assert(std::is_trivially_copy_assignable<V>::value, ""); - } - { - using V = std::variant<int, NTCopyAssign>; - static_assert(!std::is_trivially_copy_assignable<V>::value, ""); - static_assert(std::is_copy_assignable<V>::value, ""); - } - { - using V = std::variant<int, TCopyAssign>; - static_assert(std::is_trivially_copy_assignable<V>::value, ""); - } - { - using V = std::variant<int, TCopyAssignNTMoveAssign>; - static_assert(std::is_trivially_copy_assignable<V>::value, ""); - } -} - -template <typename T> struct Result { size_t index; T value; }; - -void test_copy_assignment_same_index() { - { - struct { - constexpr Result<int> operator()() const { - using V = std::variant<int>; - V v(43); - V v2(42); - v = v2; - return {v.index(), std::get<0>(v)}; - } - } test; - constexpr auto result = test(); - static_assert(result.index == 0, ""); - static_assert(result.value == 42, ""); - } - { - struct { - constexpr Result<long> operator()() const { - using V = std::variant<int, long, unsigned>; - V v(43l); - V v2(42l); - v = v2; - return {v.index(), std::get<1>(v)}; - } - } test; - constexpr auto result = test(); - static_assert(result.index == 1, ""); - static_assert(result.value == 42l, ""); - } - { - struct { - constexpr Result<int> operator()() const { - using V = std::variant<int, TCopyAssign, unsigned>; - V v(std::in_place_type<TCopyAssign>, 43); - V v2(std::in_place_type<TCopyAssign>, 42); - v = v2; - return {v.index(), std::get<1>(v).value}; - } - } test; - constexpr auto result = test(); - static_assert(result.index == 1, ""); - static_assert(result.value == 42, ""); - } - { - struct { - constexpr Result<int> operator()() const { - using V = std::variant<int, TCopyAssignNTMoveAssign, unsigned>; - V v(std::in_place_type<TCopyAssignNTMoveAssign>, 43); - V v2(std::in_place_type<TCopyAssignNTMoveAssign>, 42); - v = v2; - return {v.index(), std::get<1>(v).value}; - } - } test; - constexpr auto result = test(); - static_assert(result.index == 1, ""); - static_assert(result.value == 42, ""); - } -} - -void test_copy_assignment_different_index() { - { - struct { - constexpr Result<long> operator()() const { - using V = std::variant<int, long, unsigned>; - V v(43); - V v2(42l); - v = v2; - return {v.index(), std::get<1>(v)}; - } - } test; - constexpr auto result = test(); - static_assert(result.index == 1, ""); - static_assert(result.value == 42l, ""); - } - { - struct { - constexpr Result<int> operator()() const { - using V = std::variant<int, TCopyAssign, unsigned>; - V v(std::in_place_type<unsigned>, 43); - V v2(std::in_place_type<TCopyAssign>, 42); - v = v2; - return {v.index(), std::get<1>(v).value}; - } - } test; - constexpr auto result = test(); - static_assert(result.index == 1, ""); - static_assert(result.value == 42, ""); - } -} - -template <size_t NewIdx, class ValueType> -constexpr bool test_constexpr_assign_extension_imp( - std::variant<long, void*, int>&& v, ValueType&& new_value) -{ - const std::variant<long, void*, int> cp( - std::forward<ValueType>(new_value)); - v = cp; - return v.index() == NewIdx && - std::get<NewIdx>(v) == std::get<NewIdx>(cp); -} - -void test_constexpr_copy_assignment_extension() { -#ifdef _LIBCPP_VERSION - using V = std::variant<long, void*, int>; - static_assert(std::is_trivially_copyable<V>::value, ""); - static_assert(std::is_trivially_copy_assignable<V>::value, ""); - static_assert(test_constexpr_assign_extension_imp<0>(V(42l), 101l), ""); - static_assert(test_constexpr_assign_extension_imp<0>(V(nullptr), 101l), ""); - static_assert(test_constexpr_assign_extension_imp<1>(V(42l), nullptr), ""); - static_assert(test_constexpr_assign_extension_imp<2>(V(42l), 101), ""); -#endif -} - -int main() { - test_copy_assignment_same_index(); - test_copy_assignment_different_index(); - test_copy_assignment_sfinae(); - test_constexpr_copy_assignment_extension(); -}
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits