diff options
Diffstat (limited to 'libcxx/test/std/utilities/variant/variant.variant')
23 files changed, 3245 insertions, 0 deletions
diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.assign/T.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.assign/T.pass.cpp new file mode 100644 index 00000000000..0a1d3c9e028 --- /dev/null +++ b/libcxx/test/std/utilities/variant/variant.variant/variant.assign/T.pass.cpp @@ -0,0 +1,232 @@ +// -*- 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; + +// template <class T> +// variant& operator=(T&&) noexcept(see below); + +#include <cassert> +#include <string> +#include <type_traits> +#include <variant> + +#include "test_macros.h" +#include "variant_test_helpers.hpp" + +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 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 + +void test_T_assignment_noexcept() { + using namespace MetaHelpers; + { + using V = std::variant<Dummy, NoThrowT>; + static_assert(std::is_nothrow_assignable<V, int>::value, ""); + } + { + using V = std::variant<Dummy, ThrowsCtorT>; + static_assert(!std::is_nothrow_assignable<V, int>::value, ""); + } + { + using V = std::variant<Dummy, ThrowsAssignT>; + static_assert(!std::is_nothrow_assignable<V, int>::value, ""); + } +} + +void test_T_assignment_sfinae() { + { + using V = std::variant<long, unsigned>; + static_assert(!std::is_assignable<V, int>::value, "ambiguous"); + } + { + using V = std::variant<std::string, std::string>; + static_assert(!std::is_assignable<V, const char *>::value, "ambiguous"); + } + { + using V = std::variant<std::string, void *>; + static_assert(!std::is_assignable<V, int>::value, "no matching operator="); + } +#if !defined(TEST_VARIANT_HAS_NO_REFERENCES) + { + using V = std::variant<int, int &&>; + static_assert(!std::is_assignable<V, int>::value, "ambiguous"); + } + { + using V = std::variant<int, int const &>; + static_assert(!std::is_assignable<V, int>::value, "ambiguous"); + } +#endif +} + +void test_T_assignment_basic() { + { + std::variant<int> v(43); + v = 42; + assert(v.index() == 0); + assert(std::get<0>(v) == 42); + } + { + std::variant<int, long> 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); + } +#if !defined(TEST_VARIANT_HAS_NO_REFERENCES) + { + using V = std::variant<int &, int &&, long>; + int x = 42; + V v(43l); + v = x; + assert(v.index() == 0); + assert(&std::get<0>(v) == &x); + v = std::move(x); + assert(v.index() == 1); + assert(&std::get<1>(v) == &x); + // 'long' is selected by FUN(int const&) since 'int const&' cannot bind + // to 'int&'. + int const &cx = x; + v = cx; + assert(v.index() == 2); + assert(std::get<2>(v) == 42); + } +#endif +} + +void test_T_assignment_performs_construction() { + using namespace RuntimeHelpers; +#ifndef TEST_HAS_NO_EXCEPTIONS + { + using V = std::variant<std::string, ThrowsCtorT>; + V v(std::in_place_type<std::string>, "hello"); + try { + v = 42; + } catch (...) { /* ... */ + } + assert(v.valueless_by_exception()); + } + { + 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 +} + +void test_T_assignment_performs_assignment() { + using namespace RuntimeHelpers; +#ifndef TEST_HAS_NO_EXCEPTIONS + { + using V = std::variant<ThrowsCtorT>; + V v; + v = 42; + assert(v.index() == 0); + assert(std::get<0>(v).value == 42); + } + { + using V = std::variant<ThrowsCtorT, std::string>; + V v; + v = 42; + assert(v.index() == 0); + assert(std::get<0>(v).value == 42); + } + { + using V = std::variant<ThrowsAssignT>; + V v(100); + try { + v = 42; + assert(false); + } catch (...) { /* ... */ + } + assert(v.index() == 0); + assert(std::get<0>(v).value == 100); + } + { + using V = std::variant<std::string, ThrowsAssignT>; + V v(100); + try { + v = 42; + assert(false); + } catch (...) { /* ... */ + } + assert(v.index() == 1); + assert(std::get<1>(v).value == 100); + } +#endif +} + +int main() { + test_T_assignment_basic(); + test_T_assignment_performs_construction(); + test_T_assignment_performs_assignment(); + test_T_assignment_noexcept(); + test_T_assignment_sfinae(); +} diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.assign/copy.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.assign/copy.pass.cpp new file mode 100644 index 00000000000..ba2b3ca0797 --- /dev/null +++ b/libcxx/test/std/utilities/variant/variant.variant/variant.assign/copy.pass.cpp @@ -0,0 +1,396 @@ +// -*- 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& operator=(variant const&); + +#include <cassert> +#include <string> +#include <type_traits> +#include <variant> + +#include "test_macros.h" + +struct NoCopy { + NoCopy(NoCopy const &) = delete; + NoCopy &operator=(NoCopy const &) = default; +}; + +struct NothrowCopy { + NothrowCopy(NothrowCopy const &) noexcept = default; + NothrowCopy &operator=(NothrowCopy const &) noexcept = default; +}; + +struct CopyOnly { + CopyOnly(CopyOnly const &) = default; + CopyOnly(CopyOnly &&) = delete; + CopyOnly &operator=(CopyOnly const &) = default; + CopyOnly &operator=(CopyOnly &&) = delete; +}; + +struct MoveOnly { + MoveOnly(MoveOnly const &) = delete; + MoveOnly(MoveOnly &&) = default; + MoveOnly &operator=(MoveOnly const &) = default; +}; + +struct MoveOnlyNT { + MoveOnlyNT(MoveOnlyNT const &) = delete; + MoveOnlyNT(MoveOnlyNT &&) {} + MoveOnlyNT &operator=(MoveOnlyNT const &) = default; +}; + +struct CopyAssign { + static int alive; + static int copy_construct; + static int copy_assign; + static int move_construct; + static int move_assign; + static void reset() { + copy_construct = copy_assign = move_construct = move_assign = alive = 0; + } + CopyAssign(int v) : value(v) { ++alive; } + CopyAssign(CopyAssign const &o) : value(o.value) { + ++alive; + ++copy_construct; + } + CopyAssign(CopyAssign &&o) : value(o.value) { + o.value = -1; + ++alive; + ++move_construct; + } + CopyAssign &operator=(CopyAssign const &o) { + value = o.value; + ++copy_assign; + return *this; + } + CopyAssign &operator=(CopyAssign &&o) { + value = o.value; + o.value = -1; + ++move_assign; + return *this; + } + ~CopyAssign() { --alive; } + int value; +}; + +int CopyAssign::alive = 0; +int CopyAssign::copy_construct = 0; +int CopyAssign::copy_assign = 0; +int CopyAssign::move_construct = 0; +int CopyAssign::move_assign = 0; + +struct CopyMaybeThrows { + CopyMaybeThrows(CopyMaybeThrows const &); + CopyMaybeThrows &operator=(CopyMaybeThrows const &); +}; +struct CopyDoesThrow { + CopyDoesThrow(CopyDoesThrow const &) noexcept(false); + CopyDoesThrow &operator=(CopyDoesThrow const &) noexcept(false); +}; + +#ifndef TEST_HAS_NO_EXCEPTIONS +struct CopyThrows { + CopyThrows() = default; + CopyThrows(CopyThrows const &) { throw 42; } + CopyThrows &operator=(CopyThrows const &) { throw 42; } +}; + +struct MoveThrows { + static int alive; + MoveThrows() { ++alive; } + MoveThrows(MoveThrows const &) { ++alive; } + MoveThrows(MoveThrows &&) { throw 42; } + MoveThrows &operator=(MoveThrows const &) { return *this; } + MoveThrows &operator=(MoveThrows &&) { throw 42; } + ~MoveThrows() { --alive; } +}; + +int MoveThrows::alive = 0; + +struct MakeEmptyT { + static int alive; + MakeEmptyT() { ++alive; } + MakeEmptyT(MakeEmptyT const &) { + ++alive; + // Don't throw from the copy constructor since variant's assignment + // operator performs a copy before committing to the assignment. + } + MakeEmptyT(MakeEmptyT &&) { throw 42; } + MakeEmptyT &operator=(MakeEmptyT const &) { throw 42; } + MakeEmptyT &operator=(MakeEmptyT &&) { throw 42; } + ~MakeEmptyT() { --alive; } +}; + +int MakeEmptyT::alive = 0; + +template <class Variant> void makeEmpty(Variant &v) { + Variant v2(std::in_place_type<MakeEmptyT>); + try { + v = v2; + assert(false); + } catch (...) { + assert(v.valueless_by_exception()); + } +} +#endif // TEST_HAS_NO_EXCEPTIONS + +void test_copy_assignment_not_noexcept() { + { + using V = std::variant<CopyMaybeThrows>; + static_assert(!std::is_nothrow_copy_assignable<V>::value, ""); + } + { + using V = std::variant<int, CopyDoesThrow>; + static_assert(!std::is_nothrow_copy_assignable<V>::value, ""); + } +} + +void test_copy_assignment_sfinae() { + { + using V = std::variant<int, long>; + static_assert(std::is_copy_assignable<V>::value, ""); + } + { + // variant only provides copy assignment when beth the copy and move + // constructors are well formed + using V = std::variant<int, CopyOnly>; + static_assert(!std::is_copy_assignable<V>::value, ""); + } + { + using V = std::variant<int, NoCopy>; + static_assert(!std::is_copy_assignable<V>::value, ""); + } + { + using V = std::variant<int, MoveOnly>; + static_assert(!std::is_copy_assignable<V>::value, ""); + } + { + using V = std::variant<int, MoveOnlyNT>; + static_assert(!std::is_copy_assignable<V>::value, ""); + } +} + +void test_copy_assignment_empty_empty() { +#ifndef TEST_HAS_NO_EXCEPTIONS + using MET = MakeEmptyT; + { + using V = std::variant<int, long, MET>; + V v1(std::in_place_index<0>); + makeEmpty(v1); + V v2(std::in_place_index<0>); + makeEmpty(v2); + V &vref = (v1 = v2); + assert(&vref == &v1); + assert(v1.valueless_by_exception()); + assert(v1.index() == std::variant_npos); + } +#endif +} + +void test_copy_assignment_non_empty_empty() { +#ifndef TEST_HAS_NO_EXCEPTIONS + using MET = MakeEmptyT; + { + using V = std::variant<int, MET>; + V v1(std::in_place_index<0>, 42); + V v2(std::in_place_index<0>); + makeEmpty(v2); + V &vref = (v1 = v2); + assert(&vref == &v1); + assert(v1.valueless_by_exception()); + assert(v1.index() == std::variant_npos); + } + { + using V = std::variant<int, MET, std::string>; + V v1(std::in_place_index<2>, "hello"); + V v2(std::in_place_index<0>); + makeEmpty(v2); + V &vref = (v1 = v2); + assert(&vref == &v1); + assert(v1.valueless_by_exception()); + assert(v1.index() == std::variant_npos); + } +#endif +} + +void test_copy_assignment_empty_non_empty() { +#ifndef TEST_HAS_NO_EXCEPTIONS + using MET = MakeEmptyT; + { + using V = std::variant<int, MET>; + V v1(std::in_place_index<0>); + makeEmpty(v1); + V v2(std::in_place_index<0>, 42); + V &vref = (v1 = v2); + assert(&vref == &v1); + assert(v1.index() == 0); + assert(std::get<0>(v1) == 42); + } + { + using V = std::variant<int, MET, std::string>; + V v1(std::in_place_index<0>); + makeEmpty(v1); + V v2(std::in_place_type<std::string>, "hello"); + V &vref = (v1 = v2); + assert(&vref == &v1); + assert(v1.index() == 2); + assert(std::get<2>(v1) == "hello"); + } +#endif +} + +void test_copy_assignment_same_index() { + { + using V = std::variant<int>; + V v1(43); + V v2(42); + V &vref = (v1 = v2); + assert(&vref == &v1); + assert(v1.index() == 0); + assert(std::get<0>(v1) == 42); + } + { + using V = std::variant<int, long, unsigned>; + V v1(43l); + V v2(42l); + V &vref = (v1 = v2); + assert(&vref == &v1); + assert(v1.index() == 1); + assert(std::get<1>(v1) == 42); + } + { + using V = std::variant<int, CopyAssign, unsigned>; + V v1(std::in_place_type<CopyAssign>, 43); + V v2(std::in_place_type<CopyAssign>, 42); + CopyAssign::reset(); + V &vref = (v1 = v2); + assert(&vref == &v1); + assert(v1.index() == 1); + assert(std::get<1>(v1).value == 42); + assert(CopyAssign::copy_construct == 0); + assert(CopyAssign::move_construct == 0); + assert(CopyAssign::copy_assign == 1); + } +#ifndef TEST_HAS_NO_EXCEPTIONS + using MET = MakeEmptyT; + { + using V = std::variant<int, MET, std::string>; + V v1(std::in_place_type<MET>); + MET &mref = std::get<1>(v1); + V v2(std::in_place_type<MET>); + try { + v1 = v2; + assert(false); + } catch (...) { + } + assert(v1.index() == 1); + assert(&std::get<1>(v1) == &mref); + } +#endif +} + +void test_copy_assignment_different_index() { + { + using V = std::variant<int, long, unsigned>; + V v1(43); + V v2(42l); + V &vref = (v1 = v2); + assert(&vref == &v1); + assert(v1.index() == 1); + assert(std::get<1>(v1) == 42); + } + { + using V = std::variant<int, CopyAssign, unsigned>; + CopyAssign::reset(); + V v1(std::in_place_type<unsigned>, 43); + V v2(std::in_place_type<CopyAssign>, 42); + assert(CopyAssign::copy_construct == 0); + assert(CopyAssign::move_construct == 0); + assert(CopyAssign::alive == 1); + V &vref = (v1 = v2); + assert(&vref == &v1); + assert(v1.index() == 1); + assert(std::get<1>(v1).value == 42); + assert(CopyAssign::alive == 2); + assert(CopyAssign::copy_construct == 1); + assert(CopyAssign::move_construct == 1); + assert(CopyAssign::copy_assign == 0); + } +#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 (...) { /* ... */ + } + assert(v1.index() == 2); + assert(std::get<2>(v1) == "hello"); + } + { + // 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); + try { + v1 = v2; + assert(false); + } catch (...) { /* ... */ + } + assert(v1.valueless_by_exception()); + assert(v2.index() == 1); + assert(MoveThrows::alive == 1); + } + { + using V = std::variant<int, CopyThrows, std::string>; + V v1(std::in_place_type<CopyThrows>); + V v2(std::in_place_type<std::string>, "hello"); + V &vref = (v1 = v2); + assert(&vref == &v1); + assert(v1.index() == 2); + assert(std::get<2>(v1) == "hello"); + assert(v2.index() == 2); + assert(std::get<2>(v2) == "hello"); + } + { + using V = std::variant<int, MoveThrows, std::string>; + V v1(std::in_place_type<MoveThrows>); + V v2(std::in_place_type<std::string>, "hello"); + V &vref = (v1 = v2); + assert(&vref == &v1); + assert(v1.index() == 2); + assert(std::get<2>(v1) == "hello"); + assert(v2.index() == 2); + assert(std::get<2>(v2) == "hello"); + } +#endif +} + +int main() { + test_copy_assignment_empty_empty(); + test_copy_assignment_non_empty_empty(); + test_copy_assignment_empty_non_empty(); + test_copy_assignment_same_index(); + test_copy_assignment_different_index(); + test_copy_assignment_sfinae(); + test_copy_assignment_not_noexcept(); +} diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.assign/move.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.assign/move.pass.cpp new file mode 100644 index 00000000000..659156b5179 --- /dev/null +++ b/libcxx/test/std/utilities/variant/variant.variant/variant.assign/move.pass.cpp @@ -0,0 +1,319 @@ +// -*- 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& operator=(variant&&) noexcept(see below); + +#include <cassert> +#include <string> +#include <type_traits> +#include <utility> +#include <variant> + +#include "test_macros.h" +#include "variant_test_helpers.hpp" + +struct NoCopy { + NoCopy(NoCopy const &) = delete; + NoCopy &operator=(NoCopy const &) = default; +}; + +struct CopyOnly { + CopyOnly(CopyOnly const &) = default; + CopyOnly(CopyOnly &&) = delete; + CopyOnly &operator=(CopyOnly const &) = default; + CopyOnly &operator=(CopyOnly &&) = delete; +}; + +struct MoveOnly { + MoveOnly(MoveOnly const &) = delete; + MoveOnly(MoveOnly &&) = default; + MoveOnly &operator=(MoveOnly const &) = delete; + MoveOnly &operator=(MoveOnly &&) = default; +}; + +struct MoveOnlyNT { + MoveOnlyNT(MoveOnlyNT const &) = delete; + MoveOnlyNT(MoveOnlyNT &&) {} + MoveOnlyNT &operator=(MoveOnlyNT const &) = delete; + MoveOnlyNT &operator=(MoveOnlyNT &&) = default; +}; + +struct MoveOnlyOddNothrow { + MoveOnlyOddNothrow(MoveOnlyOddNothrow &&) noexcept(false) {} + MoveOnlyOddNothrow(MoveOnlyOddNothrow const &) = delete; + MoveOnlyOddNothrow &operator=(MoveOnlyOddNothrow &&) noexcept = default; + MoveOnlyOddNothrow &operator=(MoveOnlyOddNothrow const &) = delete; +}; + +struct MoveAssignOnly { + MoveAssignOnly(MoveAssignOnly &&) = delete; + MoveAssignOnly &operator=(MoveAssignOnly &&) = default; +}; + +struct MoveAssign { + static int move_construct; + static int move_assign; + static void reset() { move_construct = move_assign = 0; } + MoveAssign(int v) : value(v) {} + MoveAssign(MoveAssign &&o) : value(o.value) { + ++move_construct; + o.value = -1; + } + MoveAssign &operator=(MoveAssign &&o) { + value = o.value; + ++move_assign; + o.value = -1; + return *this; + } + int value; +}; + +int MoveAssign::move_construct = 0; +int MoveAssign::move_assign = 0; + +void test_move_assignment_noexcept() { + { + using V = std::variant<int>; + static_assert(std::is_nothrow_move_assignable<V>::value, ""); + } + { + using V = std::variant<MoveOnly>; + static_assert(std::is_nothrow_move_assignable<V>::value, ""); + } + { + using V = std::variant<int, long>; + static_assert(std::is_nothrow_move_assignable<V>::value, ""); + } + { + using V = std::variant<int, MoveOnly>; + static_assert(std::is_nothrow_move_assignable<V>::value, ""); + } + { + using V = std::variant<MoveOnlyNT>; + static_assert(!std::is_nothrow_move_assignable<V>::value, ""); + } + { + using V = std::variant<MoveOnlyOddNothrow>; + static_assert(!std::is_nothrow_move_assignable<V>::value, ""); + } +} + +void test_move_assignment_sfinae() { + { + using V = std::variant<int, long>; + static_assert(std::is_move_assignable<V>::value, ""); + } + { + // 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, ""); + } + { + using V = std::variant<int, NoCopy>; + static_assert(!std::is_move_assignable<V>::value, ""); + } + { + using V = std::variant<int, MoveOnly>; + static_assert(std::is_move_assignable<V>::value, ""); + } + { + using V = std::variant<int, MoveOnlyNT>; + static_assert(std::is_move_assignable<V>::value, ""); + } + { + // variant only provides move assignment when the types also provide + // a move constructor. + using V = std::variant<int, MoveAssignOnly>; + static_assert(!std::is_move_assignable<V>::value, ""); + } +} + +void test_move_assignment_empty_empty() { +#ifndef TEST_HAS_NO_EXCEPTIONS + using MET = MakeEmptyT; + { + using V = std::variant<int, long, MET>; + V v1(std::in_place_index<0>); + makeEmpty(v1); + V v2(std::in_place_index<0>); + makeEmpty(v2); + V &vref = (v1 = std::move(v2)); + assert(&vref == &v1); + assert(v1.valueless_by_exception()); + assert(v1.index() == std::variant_npos); + } +#endif +} + +void test_move_assignment_non_empty_empty() { +#ifndef TEST_HAS_NO_EXCEPTIONS + using MET = MakeEmptyT; + { + using V = std::variant<int, MET>; + V v1(std::in_place_index<0>, 42); + V v2(std::in_place_index<0>); + makeEmpty(v2); + V &vref = (v1 = std::move(v2)); + assert(&vref == &v1); + assert(v1.valueless_by_exception()); + assert(v1.index() == std::variant_npos); + } + { + using V = std::variant<int, MET, std::string>; + V v1(std::in_place_index<2>, "hello"); + V v2(std::in_place_index<0>); + makeEmpty(v2); + V &vref = (v1 = std::move(v2)); + assert(&vref == &v1); + assert(v1.valueless_by_exception()); + assert(v1.index() == std::variant_npos); + } +#endif +} + +void test_move_assignment_empty_non_empty() { +#ifndef TEST_HAS_NO_EXCEPTIONS + using MET = MakeEmptyT; + { + using V = std::variant<int, MET>; + V v1(std::in_place_index<0>); + makeEmpty(v1); + V v2(std::in_place_index<0>, 42); + V &vref = (v1 = std::move(v2)); + assert(&vref == &v1); + assert(v1.index() == 0); + assert(std::get<0>(v1) == 42); + } + { + using V = std::variant<int, MET, std::string>; + V v1(std::in_place_index<0>); + makeEmpty(v1); + V v2(std::in_place_type<std::string>, "hello"); + V &vref = (v1 = std::move(v2)); + assert(&vref == &v1); + assert(v1.index() == 2); + assert(std::get<2>(v1) == "hello"); + } +#endif +} + +void test_move_assignment_same_index() { + { + using V = std::variant<int>; + V v1(43); + V v2(42); + V &vref = (v1 = std::move(v2)); + assert(&vref == &v1); + assert(v1.index() == 0); + assert(std::get<0>(v1) == 42); + } + { + using V = std::variant<int, long, unsigned>; + V v1(43l); + V v2(42l); + V &vref = (v1 = std::move(v2)); + assert(&vref == &v1); + assert(v1.index() == 1); + assert(std::get<1>(v1) == 42); + } + { + using V = std::variant<int, MoveAssign, unsigned>; + V v1(std::in_place_type<MoveAssign>, 43); + V v2(std::in_place_type<MoveAssign>, 42); + MoveAssign::reset(); + V &vref = (v1 = std::move(v2)); + assert(&vref == &v1); + assert(v1.index() == 1); + assert(std::get<1>(v1).value == 42); + assert(MoveAssign::move_construct == 0); + assert(MoveAssign::move_assign == 1); + } +#ifndef TEST_HAS_NO_EXCEPTIONS + using MET = MakeEmptyT; + { + using V = std::variant<int, MET, std::string>; + V v1(std::in_place_type<MET>); + MET &mref = std::get<1>(v1); + V v2(std::in_place_type<MET>); + try { + v1 = std::move(v2); + assert(false); + } catch (...) { + } + assert(v1.index() == 1); + assert(&std::get<1>(v1) == &mref); + } +#endif +} + +void test_move_assignment_different_index() { + { + using V = std::variant<int, long, unsigned>; + V v1(43); + V v2(42l); + V &vref = (v1 = std::move(v2)); + assert(&vref == &v1); + assert(v1.index() == 1); + assert(std::get<1>(v1) == 42); + } + { + using V = std::variant<int, MoveAssign, unsigned>; + V v1(std::in_place_type<unsigned>, 43); + V v2(std::in_place_type<MoveAssign>, 42); + MoveAssign::reset(); + V &vref = (v1 = std::move(v2)); + assert(&vref == &v1); + assert(v1.index() == 1); + assert(std::get<1>(v1).value == 42); + assert(MoveAssign::move_construct == 1); + assert(MoveAssign::move_assign == 0); + } +#ifndef TEST_HAS_NO_EXCEPTIONS + using MET = MakeEmptyT; + { + using V = std::variant<int, MET, std::string>; + V v1(std::in_place_type<int>); + V v2(std::in_place_type<MET>); + try { + v1 = std::move(v2); + assert(false); + } catch (...) { + } + assert(v1.valueless_by_exception()); + assert(v1.index() == std::variant_npos); + } + { + using V = std::variant<int, MET, std::string>; + V v1(std::in_place_type<MET>); + V v2(std::in_place_type<std::string>, "hello"); + V &vref = (v1 = std::move(v2)); + assert(&vref == &v1); + assert(v1.index() == 2); + assert(std::get<2>(v1) == "hello"); + } +#endif +} + +int main() { + test_move_assignment_empty_empty(); + test_move_assignment_non_empty_empty(); + test_move_assignment_empty_non_empty(); + test_move_assignment_same_index(); + test_move_assignment_different_index(); + test_move_assignment_sfinae(); + test_move_assignment_noexcept(); +} diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/T.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/T.pass.cpp new file mode 100644 index 00000000000..b9ea61046b4 --- /dev/null +++ b/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/T.pass.cpp @@ -0,0 +1,112 @@ +// -*- 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; + +// template <class T> constexpr variant(T&&) noexcept(see below); + +#include <cassert> +#include <string> +#include <type_traits> +#include <variant> + +#include "test_convertible.hpp" +#include "test_macros.h" +#include "variant_test_helpers.hpp" + +struct Dummy { + Dummy() = default; +}; + +struct ThrowsT { + ThrowsT(int) noexcept(false) {} +}; + +struct NoThrowT { + NoThrowT(int) noexcept(true) {} +}; + +void test_T_ctor_noexcept() { + { + using V = std::variant<Dummy, NoThrowT>; + static_assert(std::is_nothrow_constructible<V, int>::value, ""); + } + { + using V = std::variant<Dummy, ThrowsT>; + static_assert(!std::is_nothrow_constructible<V, int>::value, ""); + } +} + +void test_T_ctor_sfinae() { + { + using V = std::variant<long, unsigned>; + static_assert(!std::is_constructible<V, int>::value, "ambiguous"); + } + { + using V = std::variant<std::string, std::string>; + static_assert(!std::is_constructible<V, const char *>::value, "ambiguous"); + } + { + using V = std::variant<std::string, void *>; + static_assert(!std::is_constructible<V, int>::value, + "no matching constructor"); + } +#if !defined(TEST_VARIANT_HAS_NO_REFERENCES) + { + using V = std::variant<int, int &&>; + static_assert(!std::is_constructible<V, int>::value, "ambiguous"); + } + { + using V = std::variant<int, int const &>; + static_assert(!std::is_constructible<V, int>::value, "ambiguous"); + } +#endif +} + +void test_T_ctor_basic() { + { + constexpr std::variant<int> v(42); + static_assert(v.index() == 0, ""); + static_assert(std::get<0>(v) == 42, ""); + } + { + constexpr std::variant<int, long> v(42l); + static_assert(v.index() == 1, ""); + static_assert(std::get<1>(v) == 42, ""); + } +#if !defined(TEST_VARIANT_HAS_NO_REFERENCES) + { + using V = std::variant<int const &, int &&, long>; + static_assert(std::is_convertible<int &, V>::value, "must be implicit"); + int x = 42; + V v(x); + assert(v.index() == 0); + assert(&std::get<0>(v) == &x); + } + { + using V = std::variant<int const &, int &&, long>; + static_assert(std::is_convertible<int, V>::value, "must be implicit"); + int x = 42; + V v(std::move(x)); + assert(v.index() == 1); + assert(&std::get<1>(v) == &x); + } +#endif +} + +int main() { + test_T_ctor_basic(); + test_T_ctor_noexcept(); + test_T_ctor_sfinae(); +} diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/copy.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/copy.pass.cpp new file mode 100644 index 00000000000..90f4f2c5655 --- /dev/null +++ b/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/copy.pass.cpp @@ -0,0 +1,137 @@ +// -*- 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 <cassert> +#include <type_traits> +#include <variant> + +#include "test_macros.h" + +struct NonT { + NonT(int v) : value(v) {} + NonT(NonT const &o) : value(o.value) {} + int value; +}; +static_assert(!std::is_trivially_copy_constructible<NonT>::value, ""); + +struct NoCopy { + NoCopy(NoCopy const &) = delete; +}; + +struct MoveOnly { + MoveOnly(MoveOnly const &) = delete; + MoveOnly(MoveOnly &&) = default; +}; + +struct MoveOnlyNT { + MoveOnlyNT(MoveOnlyNT const &) = delete; + MoveOnlyNT(MoveOnlyNT &&) {} +}; + +#ifndef TEST_HAS_NO_EXCEPTIONS +struct MakeEmptyT { + static int alive; + MakeEmptyT() { ++alive; } + MakeEmptyT(MakeEmptyT const &) { + ++alive; + // Don't throw from the copy constructor since variant's assignment + // operator performs a copy before committing to the assignment. + } + MakeEmptyT(MakeEmptyT &&) { throw 42; } + MakeEmptyT &operator=(MakeEmptyT const &) { throw 42; } + MakeEmptyT &operator=(MakeEmptyT &&) { throw 42; } + ~MakeEmptyT() { --alive; } +}; + +int MakeEmptyT::alive = 0; + +template <class Variant> void makeEmpty(Variant &v) { + Variant v2(std::in_place_type<MakeEmptyT>); + try { + v = v2; + assert(false); + } catch (...) { + assert(v.valueless_by_exception()); + } +} +#endif // TEST_HAS_NO_EXCEPTIONS + +void test_copy_ctor_sfinae() { + { + using V = std::variant<int, long>; + static_assert(std::is_copy_constructible<V>::value, ""); + } + { + using V = std::variant<int, NoCopy>; + static_assert(!std::is_copy_constructible<V>::value, ""); + } + { + using V = std::variant<int, MoveOnly>; + static_assert(!std::is_copy_constructible<V>::value, ""); + } + { + using V = std::variant<int, MoveOnlyNT>; + static_assert(!std::is_copy_constructible<V>::value, ""); + } +} + +void test_copy_ctor_basic() { + { + std::variant<int> v(std::in_place_index<0>, 42); + std::variant<int> v2 = v; + assert(v2.index() == 0); + assert(std::get<0>(v2) == 42); + } + { + std::variant<int, long> v(std::in_place_index<1>, 42); + std::variant<int, long> v2 = v; + assert(v2.index() == 1); + assert(std::get<1>(v2) == 42); + } + { + std::variant<NonT> v(std::in_place_index<0>, 42); + assert(v.index() == 0); + std::variant<NonT> v2(v); + assert(v2.index() == 0); + assert(std::get<0>(v2).value == 42); + } + { + std::variant<int, NonT> v(std::in_place_index<1>, 42); + assert(v.index() == 1); + std::variant<int, NonT> v2(v); + assert(v2.index() == 1); + assert(std::get<1>(v2).value == 42); + } +} + +void test_copy_ctor_valueless_by_exception() { +#ifndef TEST_HAS_NO_EXCEPTIONS + using V = std::variant<int, MakeEmptyT>; + V v1; + makeEmpty(v1); + V const &cv1 = v1; + V v(cv1); + assert(v.valueless_by_exception()); +#endif +} + +int main() { + test_copy_ctor_basic(); + test_copy_ctor_valueless_by_exception(); + test_copy_ctor_sfinae(); +} diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/default.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/default.pass.cpp new file mode 100644 index 00000000000..124260bfcbf --- /dev/null +++ b/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/default.pass.cpp @@ -0,0 +1,112 @@ +// -*- 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; + +// constexpr variant() noexcept(see below); + +#include <cassert> +#include <type_traits> +#include <variant> + +#include "test_macros.h" +#include "variant_test_helpers.hpp" + +struct NonDefaultConstructible { + NonDefaultConstructible(int) {} +}; + +struct NotNoexcept { + NotNoexcept() noexcept(false) {} +}; + +#ifndef TEST_HAS_NO_EXCEPTIONS +struct DefaultCtorThrows { + DefaultCtorThrows() { throw 42; } +}; +#endif + +void test_default_ctor_sfinae() { + { + using V = std::variant<std::monostate, int>; + static_assert(std::is_default_constructible<V>::value, ""); + } + { + using V = std::variant<NonDefaultConstructible, int>; + static_assert(!std::is_default_constructible<V>::value, ""); + } +#if !defined(TEST_VARIANT_HAS_NO_REFERENCES) + { + using V = std::variant<int &, int>; + static_assert(!std::is_default_constructible<V>::value, ""); + } +#endif +} + +void test_default_ctor_noexcept() { + { + using V = std::variant<int>; + static_assert(std::is_nothrow_default_constructible<V>::value, ""); + } + { + using V = std::variant<NotNoexcept>; + static_assert(!std::is_nothrow_default_constructible<V>::value, ""); + } +} + +void test_default_ctor_throws() { +#ifndef TEST_HAS_NO_EXCEPTIONS + using V = std::variant<DefaultCtorThrows, int>; + try { + V v; + assert(false); + } catch (int const &ex) { + assert(ex == 42); + } catch (...) { + assert(false); + } +#endif +} + +void test_default_ctor_basic() { + { + std::variant<int> v; + assert(v.index() == 0); + assert(std::get<0>(v) == 0); + } + { + std::variant<int, long> v; + assert(v.index() == 0); + assert(std::get<0>(v) == 0); + } + { + using V = std::variant<int, long>; + constexpr V v; + static_assert(v.index() == 0, ""); + static_assert(std::get<0>(v) == 0, ""); + } + { + using V = std::variant<int, long>; + constexpr V v; + static_assert(v.index() == 0, ""); + static_assert(std::get<0>(v) == 0, ""); + } +} + +int main() { + test_default_ctor_basic(); + test_default_ctor_sfinae(); + test_default_ctor_noexcept(); + test_default_ctor_throws(); +} diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/in_place_index_args.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/in_place_index_args.pass.cpp new file mode 100644 index 00000000000..18115722f8d --- /dev/null +++ b/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/in_place_index_args.pass.cpp @@ -0,0 +1,103 @@ +// -*- 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; + +// template <size_t I, class ...Args> +// constexpr explicit variant(in_place_index_t<I>, Args&&...); + +#include <cassert> +#include <string> +#include <type_traits> +#include <variant> + +#include "test_convertible.hpp" +#include "test_macros.h" + +void test_ctor_sfinae() { + { + using V = std::variant<int>; + static_assert( + std::is_constructible<V, std::in_place_index_t<0>, int>::value, ""); + static_assert(!test_convertible<V, std::in_place_index_t<0>, int>(), ""); + } + { + using V = std::variant<int, long, long long>; + static_assert( + std::is_constructible<V, std::in_place_index_t<1>, int>::value, ""); + static_assert(!test_convertible<V, std::in_place_index_t<1>, int>(), ""); + } + { + using V = std::variant<int, long, int *>; + static_assert( + std::is_constructible<V, std::in_place_index_t<2>, int *>::value, ""); + static_assert(!test_convertible<V, std::in_place_index_t<2>, int *>(), ""); + } + { // args not convertible to type + using V = std::variant<int, long, int *>; + static_assert( + !std::is_constructible<V, std::in_place_index_t<0>, int *>::value, ""); + static_assert(!test_convertible<V, std::in_place_index_t<0>, int *>(), ""); + } + { // index not in variant + using V = std::variant<int, long, int *>; + static_assert( + !std::is_constructible<V, std::in_place_index_t<3>, int>::value, ""); + static_assert(!test_convertible<V, std::in_place_index_t<3>, int>(), ""); + } +} + +void test_ctor_basic() { + { + constexpr std::variant<int> v(std::in_place_index<0>, 42); + static_assert(v.index() == 0, ""); + static_assert(std::get<0>(v) == 42, ""); + } + { + constexpr std::variant<int, long, long> v(std::in_place_index<1>, 42); + static_assert(v.index() == 1, ""); + static_assert(std::get<1>(v) == 42, ""); + } + { + constexpr std::variant<int, const int, long> v(std::in_place_index<1>, 42); + static_assert(v.index() == 1, ""); + static_assert(std::get<1>(v) == 42, ""); + } + { + using V = std::variant<const int, volatile int, int>; + int x = 42; + V v(std::in_place_index<0>, x); + assert(v.index() == 0); + assert(std::get<0>(v) == x); + } + { + using V = std::variant<const int, volatile int, int>; + int x = 42; + V v(std::in_place_index<1>, x); + assert(v.index() == 1); + assert(std::get<1>(v) == x); + } + { + using V = std::variant<const int, volatile int, int>; + int x = 42; + V v(std::in_place_index<2>, x); + assert(v.index() == 2); + assert(std::get<2>(v) == x); + } +} + +int main() { + test_ctor_basic(); + test_ctor_sfinae(); +} diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/in_place_index_init_list_args.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/in_place_index_init_list_args.pass.cpp new file mode 100644 index 00000000000..608cdf9d6ef --- /dev/null +++ b/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/in_place_index_init_list_args.pass.cpp @@ -0,0 +1,103 @@ +// -*- 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; + +// template <size_t I, class Up, class ...Args> +// constexpr explicit +// variant(in_place_index_t<I>, initializer_list<Up>, Args&&...); + +#include <cassert> +#include <string> +#include <type_traits> +#include <variant> + +#include "test_convertible.hpp" +#include "test_macros.h" + +struct InitList { + std::size_t size; + constexpr InitList(std::initializer_list<int> il) : size(il.size()) {} +}; + +struct InitListArg { + std::size_t size; + int value; + constexpr InitListArg(std::initializer_list<int> il, int v) + : size(il.size()), value(v) {} +}; + +void test_ctor_sfinae() { + using IL = std::initializer_list<int>; + { // just init list + using V = std::variant<InitList, InitListArg, int>; + static_assert(std::is_constructible<V, std::in_place_index_t<0>, IL>::value, + ""); + static_assert(!test_convertible<V, std::in_place_index_t<0>, IL>(), ""); + } + { // too many arguments + using V = std::variant<InitList, InitListArg, int>; + static_assert( + !std::is_constructible<V, std::in_place_index_t<0>, IL, int>::value, + ""); + static_assert(!test_convertible<V, std::in_place_index_t<0>, IL, int>(), + ""); + } + { // too few arguments + using V = std::variant<InitList, InitListArg, int>; + static_assert( + !std::is_constructible<V, std::in_place_index_t<1>, IL>::value, ""); + static_assert(!test_convertible<V, std::in_place_index_t<1>, IL>(), ""); + } + { // init list and arguments + using V = std::variant<InitList, InitListArg, int>; + static_assert( + std::is_constructible<V, std::in_place_index_t<1>, IL, int>::value, ""); + static_assert(!test_convertible<V, std::in_place_index_t<1>, IL, int>(), + ""); + } + { // not constructible from arguments + using V = std::variant<InitList, InitListArg, int>; + static_assert( + !std::is_constructible<V, std::in_place_index_t<2>, IL>::value, ""); + static_assert(!test_convertible<V, std::in_place_index_t<2>, IL>(), ""); + } +} + +void test_ctor_basic() { + { + constexpr std::variant<InitList, InitListArg, InitList> v( + std::in_place_index<0>, {1, 2, 3}); + static_assert(v.index() == 0, ""); + static_assert(std::get<0>(v).size == 3, ""); + } + { + constexpr std::variant<InitList, InitListArg, InitList> v( + std::in_place_index<2>, {1, 2, 3}); + static_assert(v.index() == 2, ""); + static_assert(std::get<2>(v).size == 3, ""); + } + { + constexpr std::variant<InitList, InitListArg, InitListArg> v( + std::in_place_index<1>, {1, 2, 3, 4}, 42); + static_assert(v.index() == 1, ""); + static_assert(std::get<1>(v).size == 4, ""); + static_assert(std::get<1>(v).value == 42, ""); + } +} + +int main() { + test_ctor_basic(); + test_ctor_sfinae(); +} diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/in_place_type_args.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/in_place_type_args.pass.cpp new file mode 100644 index 00000000000..a023f02bad6 --- /dev/null +++ b/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/in_place_type_args.pass.cpp @@ -0,0 +1,113 @@ +// -*- 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; + +// template <class Tp, class ...Args> +// constexpr explicit variant(in_place_type_t<Tp>, Args&&...); + +#include <cassert> +#include <type_traits> +#include <variant> + +#include "test_convertible.hpp" +#include "test_macros.h" + +void test_ctor_sfinae() { + { + using V = std::variant<int>; + static_assert( + std::is_constructible<V, std::in_place_type_t<int>, int>::value, ""); + static_assert(!test_convertible<V, std::in_place_type_t<int>, int>(), ""); + } + { + using V = std::variant<int, long, long long>; + static_assert( + std::is_constructible<V, std::in_place_type_t<long>, int>::value, ""); + static_assert(!test_convertible<V, std::in_place_type_t<long>, int>(), ""); + } + { + using V = std::variant<int, long, int *>; + static_assert( + std::is_constructible<V, std::in_place_type_t<int *>, int *>::value, + ""); + static_assert(!test_convertible<V, std::in_place_type_t<int *>, int *>(), + ""); + } + { // duplicate type + using V = std::variant<int, long, int>; + static_assert( + !std::is_constructible<V, std::in_place_type_t<int>, int>::value, ""); + static_assert(!test_convertible<V, std::in_place_type_t<int>, int>(), ""); + } + { // args not convertible to type + using V = std::variant<int, long, int *>; + static_assert( + !std::is_constructible<V, std::in_place_type_t<int>, int *>::value, ""); + static_assert(!test_convertible<V, std::in_place_type_t<int>, int *>(), ""); + } + { // type not in variant + using V = std::variant<int, long, int *>; + static_assert( + !std::is_constructible<V, std::in_place_type_t<long long>, int>::value, + ""); + static_assert(!test_convertible<V, std::in_place_type_t<long long>, int>(), + ""); + } +} + +void test_ctor_basic() { + { + constexpr std::variant<int> v(std::in_place_type<int>, 42); + static_assert(v.index() == 0, ""); + static_assert(std::get<0>(v) == 42, ""); + } + { + constexpr std::variant<int, long> v(std::in_place_type<long>, 42); + static_assert(v.index() == 1, ""); + static_assert(std::get<1>(v) == 42, ""); + } + { + constexpr std::variant<int, const int, long> v( + std::in_place_type<const int>, 42); + static_assert(v.index() == 1, ""); + static_assert(std::get<1>(v) == 42, ""); + } + { + using V = std::variant<const int, volatile int, int>; + int x = 42; + V v(std::in_place_type<const int>, x); + assert(v.index() == 0); + assert(std::get<0>(v) == x); + } + { + using V = std::variant<const int, volatile int, int>; + int x = 42; + V v(std::in_place_type<volatile int>, x); + assert(v.index() == 1); + assert(std::get<1>(v) == x); + } + { + using V = std::variant<const int, volatile int, int>; + int x = 42; + V v(std::in_place_type<int>, x); + assert(v.index() == 2); + assert(std::get<2>(v) == x); + } +} + +int main() { + test_ctor_basic(); + test_ctor_sfinae(); +} diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/in_place_type_init_list_args.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/in_place_type_init_list_args.pass.cpp new file mode 100644 index 00000000000..e151572c466 --- /dev/null +++ b/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/in_place_type_init_list_args.pass.cpp @@ -0,0 +1,110 @@ +// -*- 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; + +// template <class Tp, class Up, class ...Args> +// constexpr explicit +// variant(in_place_type_t<Tp>, initializer_list<Up>, Args&&...); + +#include <cassert> +#include <string> +#include <type_traits> +#include <variant> + +#include "test_convertible.hpp" +#include "test_macros.h" + +struct InitList { + std::size_t size; + constexpr InitList(std::initializer_list<int> il) : size(il.size()) {} +}; + +struct InitListArg { + std::size_t size; + int value; + constexpr InitListArg(std::initializer_list<int> il, int v) + : size(il.size()), value(v) {} +}; + +void test_ctor_sfinae() { + using IL = std::initializer_list<int>; + { // just init list + using V = std::variant<InitList, InitListArg, int>; + static_assert( + std::is_constructible<V, std::in_place_type_t<InitList>, IL>::value, + ""); + static_assert(!test_convertible<V, std::in_place_type_t<InitList>, IL>(), + ""); + } + { // too many arguments + using V = std::variant<InitList, InitListArg, int>; + static_assert(!std::is_constructible<V, std::in_place_type_t<InitList>, IL, + int>::value, + ""); + static_assert( + !test_convertible<V, std::in_place_type_t<InitList>, IL, int>(), ""); + } + { // too few arguments + using V = std::variant<InitList, InitListArg, int>; + static_assert( + !std::is_constructible<V, std::in_place_type_t<InitListArg>, IL>::value, + ""); + static_assert(!test_convertible<V, std::in_place_type_t<InitListArg>, IL>(), + ""); + } + { // init list and arguments + using V = std::variant<InitList, InitListArg, int>; + static_assert(std::is_constructible<V, std::in_place_type_t<InitListArg>, + IL, int>::value, + ""); + static_assert( + !test_convertible<V, std::in_place_type_t<InitListArg>, IL, int>(), ""); + } + { // not constructible from arguments + using V = std::variant<InitList, InitListArg, int>; + static_assert( + !std::is_constructible<V, std::in_place_type_t<int>, IL>::value, ""); + static_assert(!test_convertible<V, std::in_place_type_t<int>, IL>(), ""); + } + { // duplicate types in variant + using V = std::variant<InitListArg, InitListArg, int>; + static_assert(!std::is_constructible<V, std::in_place_type_t<InitListArg>, + IL, int>::value, + ""); + static_assert( + !test_convertible<V, std::in_place_type_t<InitListArg>, IL, int>(), ""); + } +} + +void test_ctor_basic() { + { + constexpr std::variant<InitList, InitListArg> v( + std::in_place_type<InitList>, {1, 2, 3}); + static_assert(v.index() == 0, ""); + static_assert(std::get<0>(v).size == 3, ""); + } + { + constexpr std::variant<InitList, InitListArg> v( + std::in_place_type<InitListArg>, {1, 2, 3, 4}, 42); + static_assert(v.index() == 1, ""); + static_assert(std::get<1>(v).size == 4, ""); + static_assert(std::get<1>(v).value == 42, ""); + } +} + +int main() { + test_ctor_basic(); + test_ctor_sfinae(); +} diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/move.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/move.pass.cpp new file mode 100644 index 00000000000..dcc973f074f --- /dev/null +++ b/libcxx/test/std/utilities/variant/variant.variant/variant.ctor/move.pass.cpp @@ -0,0 +1,174 @@ +// -*- 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 <cassert> +#include <string> +#include <type_traits> +#include <variant> + +#include "test_macros.h" + +struct ThrowsMove { + ThrowsMove(ThrowsMove &&) noexcept(false) {} +}; + +struct NoCopy { + NoCopy(NoCopy const &) = delete; +}; + +struct MoveOnly { + int value; + MoveOnly(int v) : value(v) {} + MoveOnly(MoveOnly const &) = delete; + MoveOnly(MoveOnly &&) = default; +}; + +struct MoveOnlyNT { + int value; + MoveOnlyNT(int v) : value(v) {} + MoveOnlyNT(MoveOnlyNT const &) = delete; + MoveOnlyNT(MoveOnlyNT &&other) : value(other.value) { other.value = -1; } +}; + +#ifndef TEST_HAS_NO_EXCEPTIONS +struct MakeEmptyT { + static int alive; + MakeEmptyT() { ++alive; } + MakeEmptyT(MakeEmptyT const &) { + ++alive; + // Don't throw from the copy constructor since variant's assignment + // operator performs a copy before committing to the assignment. + } + MakeEmptyT(MakeEmptyT &&) { throw 42; } + MakeEmptyT &operator=(MakeEmptyT const &) { throw 42; } + MakeEmptyT &operator=(MakeEmptyT &&) { throw 42; } + ~MakeEmptyT() { --alive; } +}; + +int MakeEmptyT::alive = 0; + +template <class Variant> void makeEmpty(Variant &v) { + Variant v2(std::in_place_type<MakeEmptyT>); + try { + v = v2; + assert(false); + } catch (...) { + assert(v.valueless_by_exception()); + } +} +#endif // TEST_HAS_NO_EXCEPTIONS + +void test_move_noexcept() { + { + using V = std::variant<int, long>; + static_assert(std::is_nothrow_move_constructible<V>::value, ""); + } + { + using V = std::variant<int, MoveOnly>; + static_assert(std::is_nothrow_move_constructible<V>::value, ""); + } + { + using V = std::variant<int, MoveOnlyNT>; + static_assert(!std::is_nothrow_move_constructible<V>::value, ""); + } + { + using V = std::variant<int, ThrowsMove>; + static_assert(!std::is_nothrow_move_constructible<V>::value, ""); + } +} + +void test_move_ctor_sfinae() { + { + using V = std::variant<int, long>; + static_assert(std::is_move_constructible<V>::value, ""); + } + { + using V = std::variant<int, MoveOnly>; + static_assert(std::is_move_constructible<V>::value, ""); + } + { + using V = std::variant<int, MoveOnlyNT>; + static_assert(std::is_move_constructible<V>::value, ""); + } + { + using V = std::variant<int, NoCopy>; + static_assert(!std::is_move_constructible<V>::value, ""); + } +} + +void test_move_ctor_basic() { + { + std::variant<int> v(std::in_place_index<0>, 42); + std::variant<int> v2 = std::move(v); + assert(v2.index() == 0); + assert(std::get<0>(v2) == 42); + } + { + std::variant<int, long> v(std::in_place_index<1>, 42); + std::variant<int, long> v2 = std::move(v); + assert(v2.index() == 1); + assert(std::get<1>(v2) == 42); + } + { + std::variant<MoveOnly> v(std::in_place_index<0>, 42); + assert(v.index() == 0); + std::variant<MoveOnly> v2(std::move(v)); + assert(v2.index() == 0); + assert(std::get<0>(v2).value == 42); + } + { + std::variant<int, MoveOnly> v(std::in_place_index<1>, 42); + assert(v.index() == 1); + std::variant<int, MoveOnly> v2(std::move(v)); + assert(v2.index() == 1); + assert(std::get<1>(v2).value == 42); + } + { + std::variant<MoveOnlyNT> v(std::in_place_index<0>, 42); + assert(v.index() == 0); + std::variant<MoveOnlyNT> v2(std::move(v)); + assert(v2.index() == 0); + assert(std::get<0>(v).value == -1); + assert(std::get<0>(v2).value == 42); + } + { + std::variant<int, MoveOnlyNT> v(std::in_place_index<1>, 42); + assert(v.index() == 1); + std::variant<int, MoveOnlyNT> v2(std::move(v)); + assert(v2.index() == 1); + assert(std::get<1>(v).value == -1); + assert(std::get<1>(v2).value == 42); + } +} + +void test_move_ctor_valueless_by_exception() { +#ifndef TEST_HAS_NO_EXCEPTIONS + using V = std::variant<int, MakeEmptyT>; + V v1; + makeEmpty(v1); + V v(std::move(v1)); + assert(v.valueless_by_exception()); +#endif +} + +int main() { + test_move_ctor_basic(); + test_move_ctor_valueless_by_exception(); + test_move_noexcept(); + test_move_ctor_sfinae(); +} diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.dtor/dtor.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.dtor/dtor.pass.cpp new file mode 100644 index 00000000000..8e36a8aa135 --- /dev/null +++ b/libcxx/test/std/utilities/variant/variant.variant/variant.dtor/dtor.pass.cpp @@ -0,0 +1,75 @@ +// -*- 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(); + +#include <cassert> +#include <type_traits> +#include <variant> + +#include "test_macros.h" + +struct NonTDtor { + static int count; + NonTDtor() = default; + ~NonTDtor() { ++count; } +}; +int NonTDtor::count = 0; +static_assert(!std::is_trivially_destructible<NonTDtor>::value, ""); + +struct NonTDtor1 { + static int count; + NonTDtor1() = default; + ~NonTDtor1() { ++count; } +}; +int NonTDtor1::count = 0; +static_assert(!std::is_trivially_destructible<NonTDtor1>::value, ""); + +struct TDtor { + TDtor(TDtor const &) {} // non-trivial copy + ~TDtor() = default; +}; +static_assert(!std::is_trivially_copy_constructible<TDtor>::value, ""); +static_assert(std::is_trivially_destructible<TDtor>::value, ""); + +int main() { + { + using V = std::variant<int, long, TDtor>; + static_assert(std::is_trivially_destructible<V>::value, ""); + } + { + using V = std::variant<NonTDtor, int, NonTDtor1>; + static_assert(!std::is_trivially_destructible<V>::value, ""); + { + V v(std::in_place_index<0>); + assert(NonTDtor::count == 0); + assert(NonTDtor1::count == 0); + } + assert(NonTDtor::count == 1); + assert(NonTDtor1::count == 0); + NonTDtor::count = 0; + { V v(std::in_place_index<1>); } + assert(NonTDtor::count == 0); + assert(NonTDtor1::count == 0); + { + V v(std::in_place_index<2>); + assert(NonTDtor::count == 0); + assert(NonTDtor1::count == 0); + } + assert(NonTDtor::count == 0); + assert(NonTDtor1::count == 1); + } +} diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.mod/emplace_index_args.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.mod/emplace_index_args.pass.cpp new file mode 100644 index 00000000000..4dae324e665 --- /dev/null +++ b/libcxx/test/std/utilities/variant/variant.variant/variant.mod/emplace_index_args.pass.cpp @@ -0,0 +1,137 @@ +// -*- 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; + +// template <size_t I, class ...Args> void emplace(Args&&... args); + +#include <cassert> +#include <string> +#include <type_traits> +#include <variant> + +#include "archetypes.hpp" +#include "test_convertible.hpp" +#include "test_macros.h" +#include "variant_test_helpers.hpp" + +template <class Var, size_t I, class... Args> +constexpr auto test_emplace_exists_imp(int) -> decltype( + std::declval<Var>().template emplace<I>(std::declval<Args>()...), true) { + return true; +} + +template <class, size_t, class...> +constexpr auto test_emplace_exists_imp(long) -> bool { + return false; +} + +template <class Var, size_t I, class... Args> constexpr bool emplace_exists() { + return test_emplace_exists_imp<Var, I, Args...>(0); +} + +void test_emplace_sfinae() { + { + using V = std::variant<int, void *, const void *, TestTypes::NoCtors>; + static_assert(emplace_exists<V, 0>(), ""); + static_assert(emplace_exists<V, 0, int>(), ""); + static_assert(!emplace_exists<V, 0, decltype(nullptr)>(), + "cannot construct"); + static_assert(emplace_exists<V, 1, decltype(nullptr)>(), ""); + static_assert(emplace_exists<V, 1, int *>(), ""); + static_assert(!emplace_exists<V, 1, const int *>(), ""); + static_assert(!emplace_exists<V, 1, int>(), "cannot construct"); + static_assert(emplace_exists<V, 2, const int *>(), ""); + static_assert(emplace_exists<V, 2, int *>(), ""); + static_assert(!emplace_exists<V, 3>(), "cannot construct"); + } +#if !defined(TEST_VARIANT_HAS_NO_REFERENCES) + { + using V = std::variant<int, int &, int const &, int &&, TestTypes::NoCtors>; + static_assert(emplace_exists<V, 0>(), ""); + static_assert(emplace_exists<V, 0, int>(), ""); + static_assert(emplace_exists<V, 0, long long>(), ""); + static_assert(!emplace_exists<V, 0, int, int>(), "too many args"); + static_assert(emplace_exists<V, 1, int &>(), ""); + static_assert(!emplace_exists<V, 1>(), "cannot default construct ref"); + static_assert(!emplace_exists<V, 1, int const &>(), "cannot bind ref"); + static_assert(!emplace_exists<V, 1, int &&>(), "cannot bind ref"); + static_assert(emplace_exists<V, 2, int &>(), ""); + static_assert(emplace_exists<V, 2, const int &>(), ""); + static_assert(emplace_exists<V, 2, int &&>(), ""); + static_assert(!emplace_exists<V, 2, void *>(), + "not constructible from void*"); + static_assert(emplace_exists<V, 3, int>(), ""); + static_assert(!emplace_exists<V, 3, int &>(), "cannot bind ref"); + static_assert(!emplace_exists<V, 3, int const &>(), "cannot bind ref"); + static_assert(!emplace_exists<V, 3, int const &&>(), "cannot bind ref"); + static_assert(!emplace_exists<V, 4>(), "no ctors"); + } +#endif +} + +void test_basic() { + { + using V = std::variant<int>; + V v(42); + v.emplace<0>(); + assert(std::get<0>(v) == 0); + v.emplace<0>(42); + assert(std::get<0>(v) == 42); + } + { + using V = + std::variant<int, long, const void *, TestTypes::NoCtors, std::string>; + const int x = 100; + V v(std::in_place_index<0>, -1); + // default emplace a value + v.emplace<1>(); + assert(std::get<1>(v) == 0); + v.emplace<2>(&x); + assert(std::get<2>(v) == &x); + // emplace with multiple args + v.emplace<4>(3, 'a'); + assert(std::get<4>(v) == "aaa"); + } +#if !defined(TEST_VARIANT_HAS_NO_REFERENCES) + { + using V = std::variant<int, long, int const &, int &&, TestTypes::NoCtors, + std::string>; + const int x = 100; + int y = 42; + int z = 43; + V v(std::in_place_index<0>, -1); + // default emplace a value + v.emplace<1>(); + assert(std::get<1>(v) == 0); + // emplace a reference + v.emplace<2>(x); + assert(&std::get<2>(v) == &x); + // emplace an rvalue reference + v.emplace<3>(std::move(y)); + assert(&std::get<3>(v) == &y); + // re-emplace a new reference over the active member + v.emplace<3>(std::move(z)); + assert(&std::get<3>(v) == &z); + // emplace with multiple args + v.emplace<5>(3, 'a'); + assert(std::get<5>(v) == "aaa"); + } +#endif +} + +int main() { + test_basic(); + test_emplace_sfinae(); +} diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.mod/emplace_index_init_list_args.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.mod/emplace_index_init_list_args.pass.cpp new file mode 100644 index 00000000000..f466b160cb4 --- /dev/null +++ b/libcxx/test/std/utilities/variant/variant.variant/variant.mod/emplace_index_init_list_args.pass.cpp @@ -0,0 +1,85 @@ +// -*- 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; + +// template <size_t I, class U, class ...Args> +// void emplace(initializer_list<U> il,Args&&... args); + +#include <cassert> +#include <string> +#include <type_traits> +#include <variant> + +#include "archetypes.hpp" +#include "test_convertible.hpp" +#include "test_macros.h" + +struct InitList { + std::size_t size; + constexpr InitList(std::initializer_list<int> il) : size(il.size()) {} +}; + +struct InitListArg { + std::size_t size; + int value; + constexpr InitListArg(std::initializer_list<int> il, int v) + : size(il.size()), value(v) {} +}; + +template <class Var, size_t I, class... Args> +constexpr auto test_emplace_exists_imp(int) -> decltype( + std::declval<Var>().template emplace<I>(std::declval<Args>()...), true) { + return true; +} + +template <class, size_t, class...> +constexpr auto test_emplace_exists_imp(long) -> bool { + return false; +} + +template <class Var, size_t I, class... Args> constexpr bool emplace_exists() { + return test_emplace_exists_imp<Var, I, Args...>(0); +} + +void test_emplace_sfinae() { + using V = + std::variant<int, TestTypes::NoCtors, InitList, InitListArg, long, long>; + using IL = std::initializer_list<int>; + static_assert(!emplace_exists<V, 1, IL>(), "no such constructor"); + static_assert(emplace_exists<V, 2, IL>(), ""); + static_assert(!emplace_exists<V, 2, int>(), "args don't match"); + static_assert(!emplace_exists<V, 2, IL, int>(), "too many args"); + static_assert(emplace_exists<V, 3, IL, int>(), ""); + static_assert(!emplace_exists<V, 3, int>(), "args don't match"); + static_assert(!emplace_exists<V, 3, IL>(), "too few args"); + static_assert(!emplace_exists<V, 3, IL, int, int>(), "too many args"); +} + +void test_basic() { + using V = std::variant<int, InitList, InitListArg, TestTypes::NoCtors>; + V v; + v.emplace<1>({1, 2, 3}); + assert(std::get<1>(v).size == 3); + v.emplace<2>({1, 2, 3, 4}, 42); + assert(std::get<2>(v).size == 4); + assert(std::get<2>(v).value == 42); + v.emplace<1>({1}); + assert(std::get<1>(v).size == 1); +} + +int main() { + test_basic(); + test_emplace_sfinae(); +} diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.mod/emplace_type_args.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.mod/emplace_type_args.pass.cpp new file mode 100644 index 00000000000..53a030d082a --- /dev/null +++ b/libcxx/test/std/utilities/variant/variant.variant/variant.mod/emplace_type_args.pass.cpp @@ -0,0 +1,138 @@ +// -*- 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; + +// template <class T, class ...Args> void emplace(Args&&... args); + +#include <cassert> +#include <string> +#include <type_traits> +#include <variant> + +#include "archetypes.hpp" +#include "test_convertible.hpp" +#include "test_macros.h" +#include "variant_test_helpers.hpp" + +template <class Var, class T, class... Args> +constexpr auto test_emplace_exists_imp(int) -> decltype( + std::declval<Var>().template emplace<T>(std::declval<Args>()...), true) { + return true; +} + +template <class, class, class...> +constexpr auto test_emplace_exists_imp(long) -> bool { + return false; +} + +template <class... Args> constexpr bool emplace_exists() { + return test_emplace_exists_imp<Args...>(0); +} + +void test_emplace_sfinae() { + { + using V = std::variant<int, void *, const void *, TestTypes::NoCtors>; + static_assert(emplace_exists<V, int>(), ""); + static_assert(emplace_exists<V, int, int>(), ""); + static_assert(!emplace_exists<V, int, decltype(nullptr)>(), + "cannot construct"); + static_assert(emplace_exists<V, void *, decltype(nullptr)>(), ""); + static_assert(!emplace_exists<V, void *, int>(), "cannot construct"); + static_assert(emplace_exists<V, void *, int *>(), ""); + static_assert(!emplace_exists<V, void *, const int *>(), ""); + static_assert(emplace_exists<V, void const *, const int *>(), ""); + static_assert(emplace_exists<V, void const *, int *>(), ""); + static_assert(!emplace_exists<V, TestTypes::NoCtors>(), "cannot construct"); + } +#if !defined(TEST_VARIANT_HAS_NO_REFERENCES) + using V = std::variant<int, int &, int const &, int &&, long, long, + TestTypes::NoCtors>; + static_assert(emplace_exists<V, int>(), ""); + static_assert(emplace_exists<V, int, int>(), ""); + static_assert(emplace_exists<V, int, long long>(), ""); + static_assert(!emplace_exists<V, int, int, int>(), "too many args"); + static_assert(emplace_exists<V, int &, int &>(), ""); + static_assert(!emplace_exists<V, int &>(), "cannot default construct ref"); + static_assert(!emplace_exists<V, int &, int const &>(), "cannot bind ref"); + static_assert(!emplace_exists<V, int &, int &&>(), "cannot bind ref"); + static_assert(emplace_exists<V, int const &, int &>(), ""); + static_assert(emplace_exists<V, int const &, const int &>(), ""); + static_assert(emplace_exists<V, int const &, int &&>(), ""); + static_assert(!emplace_exists<V, int const &, void *>(), + "not constructible from void*"); + static_assert(emplace_exists<V, int &&, int>(), ""); + static_assert(!emplace_exists<V, int &&, int &>(), "cannot bind ref"); + static_assert(!emplace_exists<V, int &&, int const &>(), "cannot bind ref"); + static_assert(!emplace_exists<V, int &&, int const &&>(), "cannot bind ref"); + static_assert(!emplace_exists<V, long, long>(), "ambiguous"); + static_assert(!emplace_exists<V, TestTypes::NoCtors>(), + "cannot construct void"); +#endif +} + +void test_basic() { + { + using V = std::variant<int>; + V v(42); + v.emplace<int>(); + assert(std::get<0>(v) == 0); + v.emplace<int>(42); + assert(std::get<0>(v) == 42); + } + { + using V = + std::variant<int, long, const void *, TestTypes::NoCtors, std::string>; + const int x = 100; + V v(std::in_place_type<int>, -1); + // default emplace a value + v.emplace<long>(); + assert(std::get<1>(v) == 0); + v.emplace<const void *>(&x); + assert(std::get<2>(v) == &x); + // emplace with multiple args + v.emplace<std::string>(3, 'a'); + assert(std::get<4>(v) == "aaa"); + } +#if !defined(TEST_VARIANT_HAS_NO_REFERENCES) + { + using V = std::variant<int, long, int const &, int &&, TestTypes::NoCtors, + std::string>; + const int x = 100; + int y = 42; + int z = 43; + V v(std::in_place_index<0>, -1); + // default emplace a value + v.emplace<long>(); + assert(std::get<long>(v) == 0); + // emplace a reference + v.emplace<int const &>(x); + assert(&std::get<int const &>(v) == &x); + // emplace an rvalue reference + v.emplace<int &&>(std::move(y)); + assert(&std::get<int &&>(v) == &y); + // re-emplace a new reference over the active member + v.emplace<int &&>(std::move(z)); + assert(&std::get<int &&>(v) == &z); + // emplace with multiple args + v.emplace<std::string>(3, 'a'); + assert(std::get<std::string>(v) == "aaa"); + } +#endif +} + +int main() { + test_basic(); + test_emplace_sfinae(); +} diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.mod/emplace_type_init_list_args.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.mod/emplace_type_init_list_args.pass.cpp new file mode 100644 index 00000000000..b2be8ac5b3f --- /dev/null +++ b/libcxx/test/std/utilities/variant/variant.variant/variant.mod/emplace_type_init_list_args.pass.cpp @@ -0,0 +1,85 @@ +// -*- 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; + +// template <class T, class U, class ...Args> +// void emplace(initializer_list<U> il,Args&&... args); + +#include <cassert> +#include <string> +#include <type_traits> +#include <variant> + +#include "archetypes.hpp" +#include "test_convertible.hpp" +#include "test_macros.h" + +struct InitList { + std::size_t size; + constexpr InitList(std::initializer_list<int> il) : size(il.size()) {} +}; + +struct InitListArg { + std::size_t size; + int value; + constexpr InitListArg(std::initializer_list<int> il, int v) + : size(il.size()), value(v) {} +}; + +template <class Var, class T, class... Args> +constexpr auto test_emplace_exists_imp(int) -> decltype( + std::declval<Var>().template emplace<T>(std::declval<Args>()...), true) { + return true; +} + +template <class, class, class...> +constexpr auto test_emplace_exists_imp(long) -> bool { + return false; +} + +template <class... Args> constexpr bool emplace_exists() { + return test_emplace_exists_imp<Args...>(0); +} + +void test_emplace_sfinae() { + using V = + std::variant<int, TestTypes::NoCtors, InitList, InitListArg, long, long>; + using IL = std::initializer_list<int>; + static_assert(emplace_exists<V, InitList, IL>(), ""); + static_assert(!emplace_exists<V, InitList, int>(), "args don't match"); + static_assert(!emplace_exists<V, InitList, IL, int>(), "too many args"); + static_assert(emplace_exists<V, InitListArg, IL, int>(), ""); + static_assert(!emplace_exists<V, InitListArg, int>(), "args don't match"); + static_assert(!emplace_exists<V, InitListArg, IL>(), "too few args"); + static_assert(!emplace_exists<V, InitListArg, IL, int, int>(), + "too many args"); +} + +void test_basic() { + using V = std::variant<int, InitList, InitListArg, TestTypes::NoCtors>; + V v; + v.emplace<InitList>({1, 2, 3}); + assert(std::get<InitList>(v).size == 3); + v.emplace<InitListArg>({1, 2, 3, 4}, 42); + assert(std::get<InitListArg>(v).size == 4); + assert(std::get<InitListArg>(v).value == 42); + v.emplace<InitList>({1}); + assert(std::get<InitList>(v).size == 1); +} + +int main() { + test_basic(); + test_emplace_sfinae(); +} diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.status/index.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.status/index.pass.cpp new file mode 100644 index 00000000000..fab1850056e --- /dev/null +++ b/libcxx/test/std/utilities/variant/variant.variant/variant.status/index.pass.cpp @@ -0,0 +1,55 @@ +// -*- 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; + +// constexpr size_t index() const noexcept; + +#include <cassert> +#include <string> +#include <type_traits> +#include <variant> + +#include "archetypes.hpp" +#include "test_macros.h" +#include "variant_test_helpers.hpp" + +int main() { + { + using V = std::variant<int, ConstexprTestTypes::NoCtors>; + constexpr V v; + static_assert(v.index() == 0, ""); + } + { + using V = std::variant<int, long>; + constexpr V v(std::in_place_index<1>); + static_assert(v.index() == 1, ""); + } + { + using V = std::variant<int, std::string>; + V v("abc"); + assert(v.index() == 1); + v = 42; + assert(v.index() == 0); + } +#ifndef TEST_HAS_NO_EXCEPTIONS + { + using V = std::variant<int, MakeEmptyT>; + V v; + assert(v.index() == 0); + makeEmpty(v); + assert(v.index() == std::variant_npos); + } +#endif +} diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.status/valueless_by_exception.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.status/valueless_by_exception.pass.cpp new file mode 100644 index 00000000000..c0e7030fb11 --- /dev/null +++ b/libcxx/test/std/utilities/variant/variant.variant/variant.status/valueless_by_exception.pass.cpp @@ -0,0 +1,48 @@ +// -*- 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; + +// constexpr bool valueless_by_exception() const noexcept; + +#include <cassert> +#include <string> +#include <type_traits> +#include <variant> + +#include "archetypes.hpp" +#include "test_macros.h" +#include "variant_test_helpers.hpp" + +int main() { + { + using V = std::variant<int, ConstexprTestTypes::NoCtors>; + constexpr V v; + static_assert(!v.valueless_by_exception(), ""); + } + { + using V = std::variant<int, long, std::string>; + const V v("abc"); + assert(!v.valueless_by_exception()); + } +#ifndef TEST_HAS_NO_EXCEPTIONS + { + using V = std::variant<int, MakeEmptyT>; + V v; + assert(!v.valueless_by_exception()); + makeEmpty(v); + assert(v.valueless_by_exception()); + } +#endif +} diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant.swap/swap.pass.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant.swap/swap.pass.cpp new file mode 100644 index 00000000000..bcd5344cd72 --- /dev/null +++ b/libcxx/test/std/utilities/variant/variant.variant/variant.swap/swap.pass.cpp @@ -0,0 +1,591 @@ +// -*- 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; + +// void swap(variant& rhs) noexcept(see below) + +#include <cassert> +#include <string> +#include <type_traits> +#include <variant> + +#include "test_convertible.hpp" +#include "test_macros.h" +#include "variant_test_helpers.hpp" + +struct NotSwappable {}; +void swap(NotSwappable &, NotSwappable &) = delete; + +struct NotCopyable { + NotCopyable() = default; + NotCopyable(NotCopyable const &) = delete; + NotCopyable &operator=(NotCopyable const &) = delete; +}; + +struct NotCopyableWithSwap { + NotCopyableWithSwap() = default; + NotCopyableWithSwap(NotCopyableWithSwap const &) = delete; + NotCopyableWithSwap &operator=(NotCopyableWithSwap const &) = delete; +}; +void swap(NotCopyableWithSwap &, NotCopyableWithSwap) {} + +struct NotMoveAssignable { + NotMoveAssignable() = default; + NotMoveAssignable(NotMoveAssignable &&) = default; + NotMoveAssignable &operator=(NotMoveAssignable &&) = delete; +}; + +struct NotMoveAssignableWithSwap { + NotMoveAssignableWithSwap() = default; + NotMoveAssignableWithSwap(NotMoveAssignableWithSwap &&) = default; + NotMoveAssignableWithSwap &operator=(NotMoveAssignableWithSwap &&) = delete; +}; +void swap(NotMoveAssignableWithSwap &, NotMoveAssignableWithSwap &) noexcept {} + +template <bool Throws> void do_throw() {} + +template <> void do_throw<true>() { +#ifndef TEST_HAS_NO_EXCEPTIONS + throw 42; +#else + std::abort(); +#endif +} + +template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign, + bool NT_Swap, bool EnableSwap = true> +struct NothrowTypeImp { + static int move_called; + static int move_assign_called; + static int swap_called; + static void reset() { move_called = move_assign_called = swap_called = 0; } + NothrowTypeImp() = default; + explicit NothrowTypeImp(int v) : value(v) {} + NothrowTypeImp(NothrowTypeImp const &o) noexcept(NT_Copy) : value(o.value) { + assert(false); + } // never called by test + NothrowTypeImp(NothrowTypeImp &&o) noexcept(NT_Move) : value(o.value) { + ++move_called; + do_throw<!NT_Move>(); + o.value = -1; + } + NothrowTypeImp &operator=(NothrowTypeImp const &) noexcept(NT_CopyAssign) { + assert(false); + return *this; + } // never called by the tests + NothrowTypeImp &operator=(NothrowTypeImp &&o) noexcept(NT_MoveAssign) { + ++move_assign_called; + do_throw<!NT_MoveAssign>(); + value = o.value; + o.value = -1; + return *this; + } + int value; +}; +template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign, + bool NT_Swap, bool EnableSwap> +int NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, NT_Swap, + EnableSwap>::move_called = 0; +template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign, + bool NT_Swap, bool EnableSwap> +int NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, NT_Swap, + EnableSwap>::move_assign_called = 0; +template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign, + bool NT_Swap, bool EnableSwap> +int NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, NT_Swap, + EnableSwap>::swap_called = 0; + +template <bool NT_Copy, bool NT_Move, bool NT_CopyAssign, bool NT_MoveAssign, + bool NT_Swap> +void swap(NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, + NT_Swap, true> &lhs, + NothrowTypeImp<NT_Copy, NT_Move, NT_CopyAssign, NT_MoveAssign, + NT_Swap, true> &rhs) noexcept(NT_Swap) { + lhs.swap_called++; + do_throw<!NT_Swap>(); + int tmp = lhs.value; + lhs.value = rhs.value; + rhs.value = tmp; +} + +// throwing copy, nothrow move ctor/assign, no swap provided +using NothrowMoveable = NothrowTypeImp<false, true, false, true, false, false>; +// throwing copy and move assign, nothrow move ctor, no swap provided +using NothrowMoveCtor = NothrowTypeImp<false, true, false, false, false, false>; +// nothrow move ctor, throwing move assignment, swap provided +using NothrowMoveCtorWithThrowingSwap = + NothrowTypeImp<false, true, false, false, false, true>; +// throwing move ctor, nothrow move assignment, no swap provided +using ThrowingMoveCtor = + NothrowTypeImp<false, false, false, true, false, false>; +// throwing special members, nothrowing swap +using ThrowingTypeWithNothrowSwap = + NothrowTypeImp<false, false, false, false, true, true>; +using NothrowTypeWithThrowingSwap = + NothrowTypeImp<true, true, true, true, false, true>; +// throwing move assign with nothrow move and nothrow swap +using ThrowingMoveAssignNothrowMoveCtorWithSwap = + NothrowTypeImp<false, true, false, false, true, true>; +// throwing move assign with nothrow move but no swap. +using ThrowingMoveAssignNothrowMoveCtor = + NothrowTypeImp<false, true, false, false, false, false>; + +struct NonThrowingNonNoexceptType { + static int move_called; + static void reset() { move_called = 0; } + NonThrowingNonNoexceptType() = default; + NonThrowingNonNoexceptType(int v) : value(v) {} + NonThrowingNonNoexceptType(NonThrowingNonNoexceptType &&o) noexcept(false) + : value(o.value) { + ++move_called; + o.value = -1; + } + NonThrowingNonNoexceptType & + operator=(NonThrowingNonNoexceptType &&) noexcept(false) { + assert(false); // never called by the tests. + return *this; + } + int value; +}; +int NonThrowingNonNoexceptType::move_called = 0; + +struct ThrowsOnSecondMove { + int value; + int move_count; + ThrowsOnSecondMove(int v) : value(v), move_count(0) {} + ThrowsOnSecondMove(ThrowsOnSecondMove &&o) noexcept(false) + : value(o.value), move_count(o.move_count + 1) { + if (move_count == 2) + do_throw<true>(); + o.value = -1; + } + ThrowsOnSecondMove &operator=(ThrowsOnSecondMove &&) { + assert(false); // not called by test + return *this; + } +}; + +void test_swap_valueless_by_exception() { +#ifndef TEST_HAS_NO_EXCEPTIONS + using V = std::variant<int, MakeEmptyT>; + { // both empty + V v1; + makeEmpty(v1); + V v2; + makeEmpty(v2); + assert(MakeEmptyT::alive == 0); + { // member swap + v1.swap(v2); + assert(v1.valueless_by_exception()); + assert(v2.valueless_by_exception()); + assert(MakeEmptyT::alive == 0); + } + { // non-member swap + swap(v1, v2); + assert(v1.valueless_by_exception()); + assert(v2.valueless_by_exception()); + assert(MakeEmptyT::alive == 0); + } + } + { // only one empty + V v1(42); + V v2; + makeEmpty(v2); + { // member swap + v1.swap(v2); + assert(v1.valueless_by_exception()); + assert(std::get<0>(v2) == 42); + // swap again + v2.swap(v1); + assert(v2.valueless_by_exception()); + assert(std::get<0>(v1) == 42); + } + { // non-member swap + swap(v1, v2); + assert(v1.valueless_by_exception()); + assert(std::get<0>(v2) == 42); + // swap again + swap(v1, v2); + assert(v2.valueless_by_exception()); + assert(std::get<0>(v1) == 42); + } + } +#endif +} + +void test_swap_same_alternative() { + { + using T = ThrowingTypeWithNothrowSwap; + using V = std::variant<T, int>; + T::reset(); + V v1(std::in_place_index<0>, 42); + V v2(std::in_place_index<0>, 100); + v1.swap(v2); + assert(T::swap_called == 1); + assert(std::get<0>(v1).value == 100); + assert(std::get<0>(v2).value == 42); + swap(v1, v2); + assert(T::swap_called == 2); + assert(std::get<0>(v1).value == 42); + assert(std::get<0>(v2).value == 100); + } + { + using T = NothrowMoveable; + using V = std::variant<T, int>; + T::reset(); + V v1(std::in_place_index<0>, 42); + V v2(std::in_place_index<0>, 100); + v1.swap(v2); + assert(T::swap_called == 0); + assert(T::move_called == 1); + assert(T::move_assign_called == 2); + assert(std::get<0>(v1).value == 100); + assert(std::get<0>(v2).value == 42); + T::reset(); + swap(v1, v2); + assert(T::swap_called == 0); + assert(T::move_called == 1); + assert(T::move_assign_called == 2); + assert(std::get<0>(v1).value == 42); + assert(std::get<0>(v2).value == 100); + } +#ifndef TEST_HAS_NO_EXCEPTIONS + { + using T = NothrowTypeWithThrowingSwap; + using V = std::variant<T, int>; + T::reset(); + V v1(std::in_place_index<0>, 42); + V v2(std::in_place_index<0>, 100); + try { + v1.swap(v2); + assert(false); + } catch (int) { + } + assert(T::swap_called == 1); + assert(T::move_called == 0); + assert(T::move_assign_called == 0); + assert(std::get<0>(v1).value == 42); + assert(std::get<0>(v2).value == 100); + } + { + using T = ThrowingMoveCtor; + using V = std::variant<T, int>; + T::reset(); + V v1(std::in_place_index<0>, 42); + V v2(std::in_place_index<0>, 100); + try { + v1.swap(v2); + assert(false); + } catch (int) { + } + assert(T::move_called == 1); // call threw + assert(T::move_assign_called == 0); + assert(std::get<0>(v1).value == + 42); // throw happened before v1 was moved from + assert(std::get<0>(v2).value == 100); + } + { + using T = ThrowingMoveAssignNothrowMoveCtor; + using V = std::variant<T, int>; + T::reset(); + V v1(std::in_place_index<0>, 42); + V v2(std::in_place_index<0>, 100); + try { + v1.swap(v2); + assert(false); + } catch (int) { + } + assert(T::move_called == 1); + assert(T::move_assign_called == 1); // call threw and didn't complete + assert(std::get<0>(v1).value == -1); // v1 was moved from + assert(std::get<0>(v2).value == 100); + } +#endif +} + +void test_swap_different_alternatives() { + { + using T = NothrowMoveCtorWithThrowingSwap; + using V = std::variant<T, int>; + T::reset(); + V v1(std::in_place_index<0>, 42); + V v2(std::in_place_index<1>, 100); + v1.swap(v2); + assert(T::swap_called == 0); + // The libc++ implementation double copies the argument, and not + // the variant swap is called on. + LIBCPP_ASSERT(T::move_called == 1); + assert(T::move_called <= 2); + assert(T::move_assign_called == 0); + assert(std::get<1>(v1) == 100); + assert(std::get<0>(v2).value == 42); + T::reset(); + swap(v1, v2); + assert(T::swap_called == 0); + LIBCPP_ASSERT(T::move_called == 2); + assert(T::move_called <= 2); + assert(T::move_assign_called == 0); + assert(std::get<0>(v1).value == 42); + assert(std::get<1>(v2) == 100); + } +#ifndef TEST_HAS_NO_EXCEPTIONS + { + using T1 = ThrowingTypeWithNothrowSwap; + using T2 = NonThrowingNonNoexceptType; + using V = std::variant<T1, T2>; + T1::reset(); + T2::reset(); + V v1(std::in_place_index<0>, 42); + V v2(std::in_place_index<1>, 100); + try { + v1.swap(v2); + assert(false); + } catch (int) { + } + assert(T1::swap_called == 0); + assert(T1::move_called == 1); // throws + assert(T1::move_assign_called == 0); + // FIXME: libc++ shouldn't move from T2 here. + LIBCPP_ASSERT(T2::move_called == 1); + assert(T2::move_called <= 1); + assert(std::get<0>(v1).value == 42); + if (T2::move_called != 0) + assert(v2.valueless_by_exception()); + else + assert(std::get<1>(v2).value == 100); + } + { + using T1 = NonThrowingNonNoexceptType; + using T2 = ThrowingTypeWithNothrowSwap; + using V = std::variant<T1, T2>; + T1::reset(); + T2::reset(); + V v1(std::in_place_index<0>, 42); + V v2(std::in_place_index<1>, 100); + try { + v1.swap(v2); + assert(false); + } catch (int) { + } + LIBCPP_ASSERT(T1::move_called == 0); + assert(T1::move_called <= 1); + assert(T2::swap_called == 0); + assert(T2::move_called == 1); // throws + assert(T2::move_assign_called == 0); + if (T1::move_called != 0) + assert(v1.valueless_by_exception()); + else + assert(std::get<0>(v1).value == 42); + assert(std::get<1>(v2).value == 100); + } +// FIXME: The tests below are just very libc++ specific +#ifdef _LIBCPP_VERSION + { + using T1 = ThrowsOnSecondMove; + using T2 = NonThrowingNonNoexceptType; + using V = std::variant<T1, T2>; + T2::reset(); + V v1(std::in_place_index<0>, 42); + V v2(std::in_place_index<1>, 100); + v1.swap(v2); + assert(T2::move_called == 2); + assert(std::get<1>(v1).value == 100); + assert(std::get<0>(v2).value == 42); + assert(std::get<0>(v2).move_count == 1); + } + { + using T1 = NonThrowingNonNoexceptType; + using T2 = ThrowsOnSecondMove; + using V = std::variant<T1, T2>; + T1::reset(); + V v1(std::in_place_index<0>, 42); + V v2(std::in_place_index<1>, 100); + try { + v1.swap(v2); + assert(false); + } catch (int) { + } + assert(T1::move_called == 1); + assert(v1.valueless_by_exception()); + assert(std::get<0>(v2).value == 42); + } +#endif +// testing libc++ extension. If either variant stores a nothrow move +// constructible type v1.swap(v2) provides the strong exception safety +// guarantee. +#ifdef _LIBCPP_VERSION + { + + using T1 = ThrowingTypeWithNothrowSwap; + using T2 = NothrowMoveable; + using V = std::variant<T1, T2>; + T1::reset(); + T2::reset(); + V v1(std::in_place_index<0>, 42); + V v2(std::in_place_index<1>, 100); + try { + v1.swap(v2); + assert(false); + } catch (int) { + } + assert(T1::swap_called == 0); + assert(T1::move_called == 1); + assert(T1::move_assign_called == 0); + assert(T2::swap_called == 0); + assert(T2::move_called == 2); + assert(T2::move_assign_called == 0); + assert(std::get<0>(v1).value == 42); + assert(std::get<1>(v2).value == 100); + // swap again, but call v2's swap. + T1::reset(); + T2::reset(); + try { + v2.swap(v1); + assert(false); + } catch (int) { + } + assert(T1::swap_called == 0); + assert(T1::move_called == 1); + assert(T1::move_assign_called == 0); + assert(T2::swap_called == 0); + assert(T2::move_called == 2); + assert(T2::move_assign_called == 0); + assert(std::get<0>(v1).value == 42); + assert(std::get<1>(v2).value == 100); + } +#endif // _LIBCPP_VERSION +#endif +} + +template <class Var> +constexpr auto has_swap_member_imp(int) + -> decltype(std::declval<Var &>().swap(std::declval<Var &>()), true) { + return true; +} + +template <class Var> constexpr auto has_swap_member_imp(long) -> bool { + return false; +} + +template <class Var> constexpr bool has_swap_member() { + return has_swap_member_imp<Var>(0); +} + +void test_swap_sfinae() { + { + // This variant type does not provide either a member or non-member swap + // but is still swappable via the generic swap algorithm, since the + // variant is move constructible and move assignable. + using V = std::variant<int, NotSwappable>; + LIBCPP_STATIC_ASSERT(!has_swap_member<V>()); + static_assert(std::is_swappable_v<V>, ""); + } + { + using V = std::variant<int, NotCopyable>; + LIBCPP_STATIC_ASSERT(!has_swap_member<V>(), ""); + static_assert(!std::is_swappable_v<V>, ""); + } + { + using V = std::variant<int, NotCopyableWithSwap>; + LIBCPP_STATIC_ASSERT(!has_swap_member<V>(), ""); + static_assert(!std::is_swappable_v<V>, ""); + } + { + using V = std::variant<int, NotMoveAssignable>; + LIBCPP_STATIC_ASSERT(!has_swap_member<V>(), ""); + static_assert(!std::is_swappable_v<V>, ""); + } +} + +void test_swap_noexcept() { + { + using V = std::variant<int, NothrowMoveable>; + static_assert(std::is_swappable_v<V> && has_swap_member<V>(), ""); + static_assert(std::is_nothrow_swappable_v<V>, ""); + // instantiate swap + V v1, v2; + v1.swap(v2); + swap(v1, v2); + } + { + using V = std::variant<int, NothrowMoveCtor>; + static_assert(std::is_swappable_v<V> && has_swap_member<V>(), ""); + static_assert(!std::is_nothrow_swappable_v<V>, ""); + // instantiate swap + V v1, v2; + v1.swap(v2); + swap(v1, v2); + } + { + using V = std::variant<int, ThrowingTypeWithNothrowSwap>; + static_assert(std::is_swappable_v<V> && has_swap_member<V>(), ""); + static_assert(!std::is_nothrow_swappable_v<V>, ""); + // instantiate swap + V v1, v2; + v1.swap(v2); + swap(v1, v2); + } + { + using V = std::variant<int, ThrowingMoveAssignNothrowMoveCtor>; + static_assert(std::is_swappable_v<V> && has_swap_member<V>(), ""); + static_assert(!std::is_nothrow_swappable_v<V>, ""); + // instantiate swap + V v1, v2; + v1.swap(v2); + swap(v1, v2); + } + { + using V = std::variant<int, ThrowingMoveAssignNothrowMoveCtorWithSwap>; + static_assert(std::is_swappable_v<V> && has_swap_member<V>(), ""); + static_assert(std::is_nothrow_swappable_v<V>, ""); + // instantiate swap + V v1, v2; + v1.swap(v2); + swap(v1, v2); + } + { + using V = std::variant<int, NotMoveAssignableWithSwap>; + static_assert(std::is_swappable_v<V> && has_swap_member<V>(), ""); + static_assert(std::is_nothrow_swappable_v<V>, ""); + // instantiate swap + V v1, v2; + v1.swap(v2); + swap(v1, v2); + } + { + // This variant type does not provide either a member or non-member swap + // but is still swappable via the generic swap algorithm, since the + // variant is move constructible and move assignable. + using V = std::variant<int, NotSwappable>; + LIBCPP_STATIC_ASSERT(!has_swap_member<V>()); + static_assert(std::is_swappable_v<V>, ""); + static_assert(std::is_nothrow_swappable_v<V>, ""); + V v1, v2; + swap(v1, v2); + } +} + + +// This is why variant should SFINAE member swap. :-) +LIBCPP_ONLY(template class std::variant<int, NotSwappable>;) + + +int main() { + test_swap_valueless_by_exception(); + test_swap_same_alternative(); + test_swap_different_alternatives(); + test_swap_sfinae(); + test_swap_noexcept(); +} diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant_array.fail.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant_array.fail.cpp new file mode 100644 index 00000000000..11ee332e216 --- /dev/null +++ b/libcxx/test/std/utilities/variant/variant.variant/variant_array.fail.cpp @@ -0,0 +1,33 @@ +// -*- 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; + + +#include <variant> +#include <type_traits> +#include <string> +#include <cassert> + +#include "test_macros.h" +#include "variant_test_helpers.hpp" +#include "test_convertible.hpp" + +int main() +{ + // expected-error@variant:* 3 {{static_assert failed}} + std::variant<int, int[]> v; // expected-note {{requested here}} + std::variant<int, int[42]> v2; // expected-note {{requested here}} + std::variant<int, int[][42]> v3; // expected-note {{requested here}} +} diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant_empty.fail.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant_empty.fail.cpp new file mode 100644 index 00000000000..2d8cc0b3da0 --- /dev/null +++ b/libcxx/test/std/utilities/variant/variant.variant/variant_empty.fail.cpp @@ -0,0 +1,26 @@ +// -*- 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; + +#include <variant> + +#include "test_macros.h" +#include "variant_test_helpers.hpp" + +int main() +{ + // expected-error@variant:* 1 {{static_assert failed}} + std::variant<> v; // expected-note {{requested here}} +} diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant_reference.fail.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant_reference.fail.cpp new file mode 100644 index 00000000000..1e5b9271280 --- /dev/null +++ b/libcxx/test/std/utilities/variant/variant.variant/variant_reference.fail.cpp @@ -0,0 +1,28 @@ +// -*- 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; + +#include <variant> + +#include "test_macros.h" +#include "variant_test_helpers.hpp" + +int main() +{ + // expected-error@variant:* 3 {{static_assert failed}} + std::variant<int, int&> v; // expected-note {{requested here}} + std::variant<int, int const&> v2; // expected-note {{requested here}} + std::variant<int, int&&> v3; // expected-note {{requested here}} +} diff --git a/libcxx/test/std/utilities/variant/variant.variant/variant_void.fail.cpp b/libcxx/test/std/utilities/variant/variant.variant/variant_void.fail.cpp new file mode 100644 index 00000000000..3d0da5620b5 --- /dev/null +++ b/libcxx/test/std/utilities/variant/variant.variant/variant_void.fail.cpp @@ -0,0 +1,33 @@ +// -*- 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; + + +#include <variant> +#include <type_traits> +#include <string> +#include <cassert> + +#include "test_macros.h" +#include "variant_test_helpers.hpp" +#include "test_convertible.hpp" + +int main() +{ + // expected-error@variant:* 3 {{static_assert failed}} + std::variant<int, void> v; // expected-note {{requested here}} + std::variant<int, const void> v2; // expected-note {{requested here}} + std::variant<const volatile void, int> v3; // expected-note {{requested here}} +} |