From 18c09c48f5666e6b1ea2a3724c5f6f9917c4c6fb Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 17 Jun 2024 22:59:30 -0700 Subject: [PATCH] Enable `variant` P0608R3 in C++17, update LLVM, overhaul `variant`/`any`/`optional` tests (#4713) --- docs/cgmanifest.json | 2 +- llvm-project | 2 +- stl/inc/variant | 7 - stl/inc/yvals_core.h | 2 +- tests/std/test.lst | 1 + tests/std/tests/P0088R3_variant/env.lst | 54 +- tests/std/tests/P0088R3_variant/test.cpp | 4002 +++++++++-------- tests/std/tests/P0088R3_variant_msvc/env.lst | 4 + tests/std/tests/P0088R3_variant_msvc/test.cpp | 844 ++++ tests/std/tests/P0220R1_any/test.cpp | 93 +- tests/std/tests/P0220R1_optional/test.cpp | 590 ++- .../env.lst | 2 +- tests/std/tests/usual_17_matrix.lst | 2 - tools/scripts/transform_llvm.sh | 17 + 14 files changed, 3541 insertions(+), 2081 deletions(-) create mode 100644 tests/std/tests/P0088R3_variant_msvc/env.lst create mode 100644 tests/std/tests/P0088R3_variant_msvc/test.cpp create mode 100644 tools/scripts/transform_llvm.sh diff --git a/docs/cgmanifest.json b/docs/cgmanifest.json index e5350696d..9ad09bf1b 100644 --- a/docs/cgmanifest.json +++ b/docs/cgmanifest.json @@ -15,7 +15,7 @@ "type": "git", "git": { "repositoryUrl": "https://github.com/llvm/llvm-project.git", - "commitHash": "2e2b6b53f5f63179b52168ee156df7c76b90bc71" + "commitHash": "12fcca0afeb08fbe41d79c5387cfacb249992bb4" } } }, diff --git a/llvm-project b/llvm-project index ded04bf5d..12fcca0af 160000 --- a/llvm-project +++ b/llvm-project @@ -1 +1 @@ -Subproject commit ded04bf5d32a4fd5e0919053a598443f9d773549 +Subproject commit 12fcca0afeb08fbe41d79c5387cfacb249992bb4 diff --git a/stl/inc/variant b/stl/inc/variant index bada3c556..7ebf3b6cf 100644 --- a/stl/inc/variant +++ b/stl/inc/variant @@ -862,24 +862,17 @@ using _Variant_destroy_layer = conditional_t(t)}; template auto _Construct_array(_TargetType (&&)[1]) -> _Meta_list, _TargetType>; template using _Variant_type_resolver = decltype(_STD _Construct_array<_Idx, _TargetType>({_STD declval<_InitializerType>()})); -#endif // _HAS_CXX20 template struct _Variant_init_single_overload { -#if _HAS_CXX20 template auto operator()(_TargetType, _InitializerType&&) -> _Variant_type_resolver<_Idx, _TargetType, _InitializerType>; -#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv - template - auto operator()(_TargetType, _InitializerType&&) -> _Meta_list, _TargetType>; -#endif // ^^^ !_HAS_CXX20 ^^^ }; template diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 1d03a6f45..8a0e7d29d 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -121,6 +121,7 @@ // P0602R4 Propagating Copy/Move Triviality In variant/optional // P0604R0 invoke_result, is_invocable, is_nothrow_invocable // P0607R0 Inline Variables For The STL +// P0608R3 Improving variant's Converting Constructor/Assignment // P0682R1 Repairing Elementary String Conversions // P0739R0 Improving Class Template Argument Deduction For The STL // P0858R0 Constexpr Iterator Requirements @@ -185,7 +186,6 @@ // P0586R2 Integer Comparison Functions // P0591R4 Utility Functions For Uses-Allocator Construction // P0595R2 is_constant_evaluated() -// P0608R3 Improving variant's Converting Constructor/Assignment // P0616R0 Using move() In // P0631R8 Math Constants // P0645R10 Text Formatting diff --git a/tests/std/test.lst b/tests/std/test.lst index 3893043f2..3b22ab147 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -307,6 +307,7 @@ tests\P0053R7_cpp_synchronized_buffered_ostream tests\P0067R5_charconv tests\P0083R3_splicing_maps_and_sets tests\P0088R3_variant +tests\P0088R3_variant_msvc tests\P0092R1_polishing_chrono tests\P0122R7_span tests\P0122R7_span_death diff --git a/tests/std/tests/P0088R3_variant/env.lst b/tests/std/tests/P0088R3_variant/env.lst index 4c863cb63..2de7aab29 100644 --- a/tests/std/tests/P0088R3_variant/env.lst +++ b/tests/std/tests/P0088R3_variant/env.lst @@ -1,56 +1,4 @@ # Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -# This is `usual_17_matrix.lst`, with `/DCONSTEXPR_NOTHROW` added to C1XX permissive configurations since the test is -# sensitive to our permissive-mode treatment of calls to potentially-throwing functions as `noexcept` when they are -# constant expressions. - -RUNALL_INCLUDE ..\prefix.lst -RUNALL_CROSSLIST -* PM_CL="/w14640 /Zc:threadSafeInit-" -RUNALL_CROSSLIST -PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /Zc:noexceptTypes-" -ASAN PM_CL="/EHsc /MD /std:c++latest /permissive- /Zc:noexceptTypes- -fsanitize=address /Zi" PM_LINK="/debug" -PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=0 /std:c++17 /DCONSTEXPR_NOTHROW /DTEST_PERMISSIVE" -ASAN PM_CL="/EHsc /MD /std:c++17 /DCONSTEXPR_NOTHROW /DTEST_PERMISSIVE -fsanitize=address /Zi" PM_LINK="/debug" -PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=0 /std:c++20" -ASAN PM_CL="/EHsc /MD /std:c++20 -fsanitize=address /Zi" PM_LINK="/debug" -PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=1 /std:c++latest /permissive-" -ASAN PM_CL="/EHsc /MD /std:c++latest /permissive- -fsanitize=address /Zi" PM_LINK="/debug" -PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /Zc:char8_t- /Zc:preprocessor" -ASAN PM_CL="/EHsc /MD /std:c++latest /permissive- /Zc:char8_t- /Zc:preprocessor -fsanitize=address /Zi" PM_LINK="/debug" -PM_CL="/EHsc /MDd /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /Zc:wchar_t-" -ASAN PM_CL="/EHsc /MDd /std:c++latest /permissive- /Zc:wchar_t- -fsanitize=address /Zi" PM_LINK="/debug" -PM_CL="/EHsc /MDd /D_ITERATOR_DEBUG_LEVEL=1 /std:c++latest /permissive-" -ASAN PM_CL="/EHsc /MDd /std:c++latest /permissive- -fsanitize=address /Zi" PM_LINK="/debug" -PM_CL="/EHsc /MDd /D_ITERATOR_DEBUG_LEVEL=2 /std:c++latest /permissive- /fp:except /Zc:preprocessor" -ASAN PM_CL="/EHsc /MDd /std:c++latest /permissive- /fp:except /Zc:preprocessor -fsanitize=address /Zi" PM_LINK="/debug" -PM_CL="/EHsc /MDd /D_ITERATOR_DEBUG_LEVEL=2 /std:c++17 /permissive-" -ASAN PM_CL="/EHsc /MDd /std:c++17 /permissive- -fsanitize=address /Zi" PM_LINK="/debug" -PM_CL="/EHsc /MDd /D_ITERATOR_DEBUG_LEVEL=2 /std:c++20 /permissive-" -ASAN PM_CL="/EHsc /MDd /std:c++20 /permissive- -fsanitize=address /Zi" PM_LINK="/debug" -PM_CL="/EHsc /MT /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive-" -ASAN PM_CL="/EHsc /MT /std:c++latest /permissive- -fsanitize=address /Zi" PM_LINK="/debug" -PM_CL="/EHsc /MT /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /analyze:only /analyze:autolog-" -ASAN PM_CL="/EHsc /MT /std:c++latest /permissive- /analyze:only /analyze:autolog- -fsanitize=address /Zi" PM_LINK="/debug" -PM_CL="/EHsc /MT /D_ITERATOR_DEBUG_LEVEL=1 /std:c++latest /permissive-" -# No corresponding ASAN config, since the above differs from another config only in IDL -PM_CL="/EHsc /MTd /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /fp:strict" -ASAN PM_CL="/EHsc /MTd /std:c++latest /permissive- /fp:strict -fsanitize=address /Zi" PM_LINK="/debug" -PM_CL="/EHsc /MTd /D_ITERATOR_DEBUG_LEVEL=1 /std:c++latest /permissive-" -ASAN PM_CL="/EHsc /MTd /std:c++latest /permissive- -fsanitize=address /Zi" PM_LINK="/debug" -PM_CL="/EHsc /MTd /D_ITERATOR_DEBUG_LEVEL=2 /std:c++latest /permissive /DCONSTEXPR_NOTHROW /DTEST_PERMISSIVE" -ASAN PM_CL="/EHsc /MTd /std:c++latest /permissive /DCONSTEXPR_NOTHROW /DTEST_PERMISSIVE -fsanitize=address /Zi" PM_LINK="/debug" -PM_CL="/EHsc /MTd /D_ITERATOR_DEBUG_LEVEL=2 /std:c++latest /permissive- /analyze:only /analyze:autolog-" -ASAN PM_CL="/EHsc /MTd /std:c++latest /permissive- /analyze:only /analyze:autolog- -fsanitize=address /Zi" PM_LINK="/debug" -# With /clr /std:c++20, extreme compiler memory consumption causes test timeouts. -PM_CL="/clr /MD /std:c++17 /DCONSTEXPR_NOTHROW /DTEST_PERMISSIVE" -PM_CL="/clr /MDd /std:c++17 /DCONSTEXPR_NOTHROW /DTEST_PERMISSIVE" -PM_CL="/BE /c /EHsc /MD /std:c++latest /permissive-" -PM_CL="/BE /c /EHsc /MDd /std:c++17 /permissive-" -PM_CL="/BE /c /EHsc /MT /std:c++20 /permissive-" -PM_CL="/BE /c /EHsc /MTd /std:c++latest /permissive-" -PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MD /std:c++latest /permissive-" -PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MDd /std:c++17 /DTEST_PERMISSIVE" -PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MT /std:c++20 /permissive-" -PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing -Wno-unqualified-std-cast-call /EHsc /MTd /std:c++latest /permissive- /fp:strict" +RUNALL_INCLUDE ..\usual_17_matrix.lst diff --git a/tests/std/tests/P0088R3_variant/test.cpp b/tests/std/tests/P0088R3_variant/test.cpp index a15cad4e1..58a54cbda 100644 --- a/tests/std/tests/P0088R3_variant/test.cpp +++ b/tests/std/tests/P0088R3_variant/test.cpp @@ -15,35 +15,29 @@ // the MSVC-specific test cases. // // The LLVM sources are updated manually: -// 1. Navigate a bash prompt to `libcxx` in an LLVM monorepo. -// 2. Redirect the output of the bash loop: -// for f in $(find test/std/utilities/variant -name '*.pass.cpp' -a -not -name '*nothing_to_do*'); -// do echo "// -- BEGIN: $f"; -// sed -e 's/int main(int, char\*\*)/int run_test()/; s/FIXME/TODO/g' < $f; -// echo -e "// -- END: $f\n"; -// done +// 1. Navigate a bash prompt to `llvm-project/libcxx`. +// 2. Redirect the output of: +// ../../tools/scripts/transform_llvm.sh test/std/utilities/variant // into a file. // 3. Replicate the namespace structure from here into that file, use its content to replace everything between the // "LLVM SOURCES BEGIN"/"END" delimiters, and ensure that `main` properly calls each of the `run_test` functions. -// 4. You'll need to fixup the specializations of std::hash in test/std/utilities/variant/variant.hash/hash.pass.cpp, -// and preserve the `// TRANSITION, P0608` conditionals if we still haven't implemented P0608. +// 4. Fix the specializations of std::hash and std::get by closing/reopening namespaces and qualifying types. +// 5. Restore the TRANSITION-commented workarounds. +// 6. Restore the _HAS_CXX20 guards for relops::three_way and visit::return_type. +// 7. Restore the guards for P2637R3 Member visit. +// 8. Restore the is_permissive workarounds. // // Yes, this is an awkward hand process; notably the required headers can change without notice. We should investigate // running the libc++ tests directly in all of our configurations so we needn't replicate this subset of files. #if !defined(_PREFAST_) || !defined(_M_IX86) // TRANSITION, VSO-1639191 -#ifdef CONSTEXPR_NOTHROW -#define TEST_WORKAROUND_CONSTEXPR_IMPLIES_NOEXCEPT -#endif // CONSTEXPR_NOTHROW -#define _HAS_DEPRECATED_RESULT_OF 1 -#define _SILENCE_CXX17_RESULT_OF_DEPRECATION_WARNING -#define _SILENCE_CXX20_CISO646_REMOVED_WARNING #define _SILENCE_CXX20_VOLATILE_DEPRECATION_WARNING -#define _SILENCE_CXX23_ALIGNED_UNION_DEPRECATION_WARNING #define _LIBCXX_IN_DEVCRT #include // Must precede any other libc++ headers +#include + // clang-format off // LLVM SOURCES BEGIN // -- BEGIN: test/std/utilities/variant/variant.bad_variant_access/bad_variant_access.pass.cpp @@ -56,8 +50,6 @@ //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_variant_access - // @@ -71,7 +63,6 @@ public: */ -#include #include #include #include @@ -103,6 +94,7 @@ int run_test() { //===----------------------------------------------------------------------===// + // // template @@ -118,7 +110,7 @@ int run_test() { #include #include -namespace get_if::index { +namespace get::get_if_index { void test_const_get_if() { { using V = std::variant; @@ -171,7 +163,7 @@ int run_test() { return 0; } -} // namespace get_if::index +} // namespace get::get_if_index // -- END: test/std/utilities/variant/variant.get/get_if_index.pass.cpp // -- BEGIN: test/std/utilities/variant/variant.get/get_if_type.pass.cpp @@ -184,6 +176,7 @@ int run_test() { //===----------------------------------------------------------------------===// + // // template @@ -197,7 +190,7 @@ int run_test() { #include #include -namespace get_if::type { +namespace get::get_if_type { void test_const_get_if() { { using V = std::variant; @@ -250,7 +243,7 @@ int run_test() { return 0; } -} // namespace get_if::type +} // namespace get::get_if_type // -- END: test/std/utilities/variant/variant.get/get_if_type.pass.cpp // -- BEGIN: test/std/utilities/variant/variant.get/get_index.pass.cpp @@ -263,7 +256,6 @@ int run_test() { //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_variant_access && !libcpp-no-exceptions // @@ -288,16 +280,12 @@ int run_test() { #include #include -namespace get::index { +namespace get::get_index { void test_const_lvalue_get() { { using V = std::variant; constexpr V v(42); -#ifdef TEST_WORKAROUND_CONSTEXPR_IMPLIES_NOEXCEPT - ASSERT_NOEXCEPT(std::get<0>(v)); -#else - ASSERT_NOT_NOEXCEPT(std::get<0>(v)); -#endif + static_assert(noexcept(std::get<0>(v)) == is_permissive); ASSERT_SAME_TYPE(decltype(std::get<0>(v)), const int &); static_assert(std::get<0>(v) == 42, ""); } @@ -311,11 +299,7 @@ void test_const_lvalue_get() { { using V = std::variant; constexpr V v(42l); -#ifdef TEST_WORKAROUND_CONSTEXPR_IMPLIES_NOEXCEPT - ASSERT_NOEXCEPT(std::get<1>(v)); -#else - ASSERT_NOT_NOEXCEPT(std::get<1>(v)); -#endif + static_assert(noexcept(std::get<1>(v)) == is_permissive); ASSERT_SAME_TYPE(decltype(std::get<1>(v)), const long &); static_assert(std::get<1>(v) == 42, ""); } @@ -376,7 +360,7 @@ void test_const_rvalue_get() { } } -template using Idx = std::integral_constant; +template using Idx = std::integral_constant; void test_throws_for_all_value_categories() { #ifndef TEST_HAS_NO_EXCEPTIONS @@ -387,8 +371,8 @@ void test_throws_for_all_value_categories() { V v1(42l); const V &cv1 = v1; assert(v1.index() == 1); - std::integral_constant zero; - std::integral_constant one; + std::integral_constant zero; + std::integral_constant one; auto test = [](auto idx, auto &&v) { using Idx = decltype(idx); try { @@ -427,7 +411,7 @@ int run_test() { return 0; } -} // namespace get::index +} // namespace get::get_index // -- END: test/std/utilities/variant/variant.get/get_index.pass.cpp // -- BEGIN: test/std/utilities/variant/variant.get/get_type.pass.cpp @@ -440,7 +424,6 @@ int run_test() { //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_variant_access && !libcpp-no-exceptions // @@ -459,16 +442,12 @@ int run_test() { #include #include -namespace get::type { +namespace get::get_type { void test_const_lvalue_get() { { using V = std::variant; constexpr V v(42); -#ifdef TEST_WORKAROUND_CONSTEXPR_IMPLIES_NOEXCEPT - ASSERT_NOEXCEPT(std::get(v)); -#else - ASSERT_NOT_NOEXCEPT(std::get(v)); -#endif + static_assert(noexcept(std::get(v)) == is_permissive); ASSERT_SAME_TYPE(decltype(std::get(v)), const int &); static_assert(std::get(v) == 42, ""); } @@ -482,11 +461,7 @@ void test_const_lvalue_get() { { using V = std::variant; constexpr V v(42l); -#ifdef TEST_WORKAROUND_CONSTEXPR_IMPLIES_NOEXCEPT - ASSERT_NOEXCEPT(std::get(v)); -#else - ASSERT_NOT_NOEXCEPT(std::get(v)); -#endif + static_assert(noexcept(std::get(v)) == is_permissive); ASSERT_SAME_TYPE(decltype(std::get(v)), const long &); static_assert(std::get(v) == 42, ""); } @@ -600,7 +575,7 @@ int run_test() { return 0; } -} // namespace get::type +} // namespace get::get_type // -- END: test/std/utilities/variant/variant.get/get_type.pass.cpp // -- BEGIN: test/std/utilities/variant/variant.get/holds_alternative.pass.cpp @@ -613,6 +588,7 @@ int run_test() { //===----------------------------------------------------------------------===// + // // template @@ -621,7 +597,7 @@ int run_test() { #include "test_macros.h" #include -namespace holds_alternative { +namespace get::holds_alternative { int run_test() { { using V = std::variant; @@ -642,7 +618,7 @@ int run_test() { return 0; } -} // namespace holds_alternative +} // namespace get::holds_alternative // -- END: test/std/utilities/variant/variant.get/holds_alternative.pass.cpp // -- BEGIN: test/std/utilities/variant/variant.hash/enabled_hash.pass.cpp @@ -655,6 +631,7 @@ int run_test() { //===----------------------------------------------------------------------===// + // // Test that provides all of the arithmetic, enum, and pointer @@ -666,13 +643,13 @@ int run_test() { #include "test_macros.h" -namespace enabled_hash { +namespace hash::enabled_hash { int run_test() { test_library_hash_specializations_available(); return 0; } -} // namespace enabled_hash +} // namespace hash::enabled_hash // -- END: test/std/utilities/variant/variant.hash/enabled_hash.pass.cpp // -- BEGIN: test/std/utilities/variant/variant.hash/hash.pass.cpp @@ -685,6 +662,7 @@ int run_test() { //===----------------------------------------------------------------------===// + // // template struct hash>; @@ -701,7 +679,7 @@ int run_test() { #ifndef TEST_HAS_NO_EXCEPTIONS namespace std { template <> struct hash<::MakeEmptyT> { - size_t operator()(const ::MakeEmptyT &) const { + std::size_t operator()(const ::MakeEmptyT &) const { assert(false); return 0; } @@ -806,7 +784,7 @@ namespace std { template <> struct hash<::hash::B> { - size_t operator()(::hash::B const&) const { + std::size_t operator()(::hash::B const&) const { return 0; } }; @@ -819,7 +797,7 @@ void test_hash_variant_enabled() { #ifndef __EDG__ // TRANSITION, DevCom-10107834 test_hash_enabled_for_type >(); test_hash_enabled_for_type >(); -#endif // TRANSITION, DevCom-10107834 +#endif // ^^^ no workaround ^^^ } { test_hash_disabled_for_type>(); @@ -829,7 +807,7 @@ void test_hash_variant_enabled() { #ifndef __EDG__ // TRANSITION, DevCom-10107834 test_hash_enabled_for_type>(); test_hash_enabled_for_type>(); -#endif // TRANSITION, DevCom-10107834 +#endif // ^^^ no workaround ^^^ } } @@ -854,6 +832,7 @@ int run_test() { //===----------------------------------------------------------------------===// + // // template struct variant_alternative; // undefined @@ -874,7 +853,7 @@ int run_test() { #include "variant_test_helpers.h" namespace helpers::variant_alternative { -template void test() { +template void test() { static_assert( std::is_same_v::type, E>, ""); static_assert( @@ -925,6 +904,7 @@ int run_test() { //===----------------------------------------------------------------------===// + // // template struct variant_size; // undefined @@ -941,7 +921,7 @@ int run_test() { #include "test_macros.h" namespace helpers::variant_size { -template void test() { +template void test() { static_assert(std::variant_size::value == E, ""); static_assert(std::variant_size::value == E, ""); static_assert(std::variant_size::value == E, ""); @@ -975,6 +955,7 @@ int run_test() { //===----------------------------------------------------------------------===// + // // struct monostate {}; @@ -1009,6 +990,7 @@ int run_test() { //===----------------------------------------------------------------------===// + // // constexpr bool operator<(monostate, monostate) noexcept { return false; } @@ -1017,41 +999,33 @@ int run_test() { // constexpr bool operator>=(monostate, monostate) noexcept { return true; } // constexpr bool operator==(monostate, monostate) noexcept { return true; } // constexpr bool operator!=(monostate, monostate) noexcept { return false; } +// constexpr strong_ordering operator<=>(monostate, monostate) noexcept { return strong_ordering::equal; } // C++20 -#include "test_macros.h" #include -#include #include +#include "test_comparisons.h" +#include "test_macros.h" + namespace monostate::relops { -int run_test() { +constexpr bool test() { using M = std::monostate; constexpr M m1{}; constexpr M m2{}; - { - static_assert((m1 < m2) == false, ""); - ASSERT_NOEXCEPT(m1 < m2); - } - { - static_assert((m1 > m2) == false, ""); - ASSERT_NOEXCEPT(m1 > m2); - } - { - static_assert((m1 <= m2) == true, ""); - ASSERT_NOEXCEPT(m1 <= m2); - } - { - static_assert((m1 >= m2) == true, ""); - ASSERT_NOEXCEPT(m1 >= m2); - } - { - static_assert((m1 == m2) == true, ""); - ASSERT_NOEXCEPT(m1 == m2); - } - { - static_assert((m1 != m2) == false, ""); - ASSERT_NOEXCEPT(m1 != m2); - } + assert(testComparisons(m1, m2, /*isEqual*/ true, /*isLess*/ false)); + AssertComparisonsAreNoexcept(); + +#if TEST_STD_VER > 17 + assert(testOrder(m1, m2, std::strong_ordering::equal)); + AssertOrderAreNoexcept(); +#endif // TEST_STD_VER > 17 + + return true; +} + +int run_test() { + test(); + static_assert(test()); return 0; } @@ -1068,6 +1042,7 @@ int run_test() { //===----------------------------------------------------------------------===// + // // template @@ -1101,7 +1076,7 @@ int run_test() { #include "test_macros.h" -namespace relops { +namespace relops::relops { #ifndef TEST_HAS_NO_EXCEPTIONS struct MakeEmptyT { MakeEmptyT() = default; @@ -1335,9 +1310,226 @@ int run_test() { return 0; } -} // namespace relops +} // namespace relops::relops // -- END: test/std/utilities/variant/variant.relops/relops.pass.cpp +// -- BEGIN: test/std/utilities/variant/variant.relops/three_way.pass.cpp +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + + + +// + +// template class variant; + +// template requires (three_way_comparable && ...) +// constexpr std::common_comparison_category_t< +// std::compare_three_way_result_t...> +// operator<=>(const variant& t, const variant& u); + +#include +#include +#include +#include +#include + +#include "test_macros.h" +#include "test_comparisons.h" + +namespace relops::three_way { +#if _HAS_CXX20 +#ifndef TEST_HAS_NO_EXCEPTIONS +// MakeEmptyT throws in operator=(&&), so we can move to it to create valueless-by-exception variants. +struct MakeEmptyT { + MakeEmptyT() = default; + MakeEmptyT(MakeEmptyT&&) { throw 42; } + MakeEmptyT& operator=(MakeEmptyT&&) { throw 42; } +}; +inline bool operator==(const MakeEmptyT&, const MakeEmptyT&) { + assert(false); + return false; +} +inline std::weak_ordering operator<=>(const MakeEmptyT&, const MakeEmptyT&) { + assert(false); + return std::weak_ordering::equivalent; +} + +template +void makeEmpty(Variant& v) { + Variant v2(std::in_place_type); + try { + v = std::move(v2); + assert(false); + } catch (...) { + assert(v.valueless_by_exception()); + } +} + +void test_empty() { + { + using V = std::variant; + V v1; + V v2; + makeEmpty(v2); + assert(testOrder(v1, v2, std::weak_ordering::greater)); + } + { + using V = std::variant; + V v1; + makeEmpty(v1); + V v2; + assert(testOrder(v1, v2, std::weak_ordering::less)); + } + { + using V = std::variant; + V v1; + makeEmpty(v1); + V v2; + makeEmpty(v2); + assert(testOrder(v1, v2, std::weak_ordering::equivalent)); + } +} +#endif // TEST_HAS_NO_EXCEPTIONS + +template +constexpr bool test_with_types() { + using V = std::variant; + AssertOrderReturn(); + { // same index, same value + constexpr V v1(std::in_place_index<0>, T1{1}); + constexpr V v2(std::in_place_index<0>, T1{1}); + assert(testOrder(v1, v2, Order::equivalent)); + } + { // same index, value < other_value + constexpr V v1(std::in_place_index<0>, T1{0}); + constexpr V v2(std::in_place_index<0>, T1{1}); + assert(testOrder(v1, v2, Order::less)); + } + { // same index, value > other_value + constexpr V v1(std::in_place_index<0>, T1{1}); + constexpr V v2(std::in_place_index<0>, T1{0}); + assert(testOrder(v1, v2, Order::greater)); + } + { // LHS.index() < RHS.index() + constexpr V v1(std::in_place_index<0>, T1{0}); + constexpr V v2(std::in_place_index<1>, T2{0}); + assert(testOrder(v1, v2, Order::less)); + } + { // LHS.index() > RHS.index() + constexpr V v1(std::in_place_index<1>, T2{0}); + constexpr V v2(std::in_place_index<0>, T1{0}); + assert(testOrder(v1, v2, Order::greater)); + } + + return true; +} + +constexpr bool test_three_way() { + assert((test_with_types())); + assert((test_with_types())); + + { + using V = std::variant; + constexpr double nan = std::numeric_limits::quiet_NaN(); + { + constexpr V v1(std::in_place_type, 1); + constexpr V v2(std::in_place_type, nan); + assert(testOrder(v1, v2, std::partial_ordering::less)); + } + { + constexpr V v1(std::in_place_type, nan); + constexpr V v2(std::in_place_type, 2); + assert(testOrder(v1, v2, std::partial_ordering::greater)); + } +#if !defined(__clang__) // TRANSITION, DevCom-1626139 (MSVC), EDG says "floating-point values cannot be compared" + if (!std::is_constant_evaluated()) +#endif // ^^^ workaround ^^^ + { + constexpr V v1(std::in_place_type, nan); + constexpr V v2(std::in_place_type, nan); + assert(testOrder(v1, v2, std::partial_ordering::unordered)); + } + } + + return true; +} + +// SFINAE tests +template +concept has_three_way_op = requires (T& t, U& u) { t <=> u; }; + +// std::three_way_comparable is a more stringent requirement that demands +// operator== and a few other things. +using std::three_way_comparable; + +struct HasSimpleOrdering { + constexpr bool operator==(const HasSimpleOrdering&) const; + constexpr bool operator<(const HasSimpleOrdering&) const; +}; + +struct HasOnlySpaceship { + constexpr bool operator==(const HasOnlySpaceship&) const = delete; + constexpr std::weak_ordering operator<=>(const HasOnlySpaceship&) const; +}; + +#ifdef __clang__ // TRANSITION, not yet investigated +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wundefined-inline" +#endif // ^^^ workaround ^^^ + +struct HasFullOrdering { + constexpr bool operator==(const HasFullOrdering&) const; + constexpr std::weak_ordering operator<=>(const HasFullOrdering&) const; +}; + +#ifdef __clang__ // TRANSITION, not yet investigated +#pragma clang diagnostic pop +#endif // ^^^ workaround ^^^ + +// operator<=> must resolve the return types of all its union types' +// operator<=>s to determine its own return type, so it is detectable by SFINAE +static_assert(!has_three_way_op); +static_assert(!has_three_way_op>); + +static_assert(!three_way_comparable); +static_assert(!three_way_comparable>); + +static_assert(has_three_way_op); +static_assert(!has_three_way_op>); + +static_assert(!three_way_comparable); +static_assert(!three_way_comparable>); + +static_assert( has_three_way_op); +static_assert( has_three_way_op>); + +static_assert( three_way_comparable); +static_assert( three_way_comparable>); + +int run_test() { + test_three_way(); + static_assert(test_three_way()); + +#ifndef TEST_HAS_NO_EXCEPTIONS + test_empty(); +#endif // TEST_HAS_NO_EXCEPTIONS + + return 0; +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv +int run_test() { + return 0; +} +#endif // ^^^ !_HAS_CXX20 ^^^ +} // namespace relops::three_way +// -- END: test/std/utilities/variant/variant.relops/three_way.pass.cpp + // -- BEGIN: test/std/utilities/variant/variant.synopsis/variant_npos.pass.cpp //===----------------------------------------------------------------------===// // @@ -1348,6 +1540,7 @@ int run_test() { //===----------------------------------------------------------------------===// + // // constexpr size_t variant_npos = -1; @@ -1365,6 +1558,52 @@ int run_test() { } // namespace npos // -- END: test/std/utilities/variant/variant.synopsis/variant_npos.pass.cpp +// -- BEGIN: test/std/utilities/variant/variant.variant/implicit_ctad.pass.cpp +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + + + +// + +// template class variant; + +// Make sure that the implicitly-generated CTAD works. + +// We make sure that it is not ill-formed, however we still produce a warning for +// this one because explicit construction from a variant using CTAD is ambiguous +// (in the sense that the programmer intent is not clear). +// ADDITIONAL_COMPILE_FLAGS(gcc-style-warnings): -Wno-ctad-maybe-unsupported + +#include + +#include "test_macros.h" + +namespace implicit_ctad { +int run_test() { + // This is the motivating example from P0739R0 + { + std::variant v1(3); + std::variant v2 = v1; + ASSERT_SAME_TYPE(decltype(v2), std::variant); + } + + { + std::variant v1(3); + std::variant v2 = std::variant(v1); // Technically valid, but intent is ambiguous! + ASSERT_SAME_TYPE(decltype(v2), std::variant); + } + + return 0; +} +} // namespace implicit_ctad +// -- END: test/std/utilities/variant/variant.variant/implicit_ctad.pass.cpp + // -- BEGIN: test/std/utilities/variant/variant.variant/variant.assign/conv.pass.cpp //===----------------------------------------------------------------------===// // @@ -1375,6 +1614,7 @@ int run_test() { //===----------------------------------------------------------------------===// + // // template class variant; @@ -1393,26 +1633,18 @@ int run_test() { static_assert(!std::is_assignable, int>::value, ""); static_assert(!std::is_assignable, int>::value, ""); -#if _HAS_CXX20 - static_assert(std::is_assignable, int>::value == false, ""); + static_assert(!std::is_assignable, int>::value, ""); - static_assert(std::is_assignable, int>::value - == false, ""); - static_assert(std::is_assignable, int>::value - == false, ""); + static_assert(!std::is_assignable, int>::value, ""); + static_assert(!std::is_assignable, int>::value, ""); static_assert(!std::is_assignable, int>::value, ""); static_assert(!std::is_assignable, decltype("meow")>::value, ""); static_assert(!std::is_assignable, decltype("meow")>::value, ""); - static_assert(!std::is_assignable, decltype("meow")>::value, ""); - // libc++ is missing P1957R2 static_assert(std::is_assignable, std::true_type>::value, ""); static_assert(!std::is_assignable, std::unique_ptr >::value, ""); -#ifndef TEST_PERMISSIVE - static_assert(!std::is_assignable, decltype(nullptr)>::value, ""); -#endif // !TEST_PERMISSIVE -#endif // _HAS_CXX20 + static_assert(std::is_assignable, decltype(nullptr)>::value == is_permissive, ""); return 0; } @@ -1429,17 +1661,12 @@ int run_test() //===----------------------------------------------------------------------===// -// The following compilers don't generate constexpr special members correctly. -// XFAIL: clang-3.5, clang-3.6, clang-3.7, clang-3.8 -// XFAIL: apple-clang-6, apple-clang-7, apple-clang-8.0 - -// XFAIL: dylib-has-no-bad_variant_access && !libcpp-no-exceptions // // template class variant; -// variant& operator=(variant const&); // constexpr in C++20 +// constexpr variant& operator=(variant const&); #include #include @@ -1450,88 +1677,108 @@ int run_test() namespace assign::copy { struct NoCopy { - NoCopy(const NoCopy &) = delete; - NoCopy &operator=(const NoCopy &) = default; + NoCopy(const NoCopy&) = delete; + NoCopy& operator=(const NoCopy&) = default; }; struct CopyOnly { - CopyOnly(const CopyOnly &) = default; - CopyOnly(CopyOnly &&) = delete; - CopyOnly &operator=(const CopyOnly &) = default; - CopyOnly &operator=(CopyOnly &&) = delete; + CopyOnly(const CopyOnly&) = default; + CopyOnly(CopyOnly&&) = delete; + CopyOnly& operator=(const CopyOnly&) = default; + CopyOnly& operator=(CopyOnly&&) = delete; }; struct MoveOnly { - MoveOnly(const MoveOnly &) = delete; - MoveOnly(MoveOnly &&) = default; - MoveOnly &operator=(const MoveOnly &) = default; + MoveOnly(const MoveOnly&) = delete; + MoveOnly(MoveOnly&&) = default; + MoveOnly& operator=(const MoveOnly&) = default; }; struct MoveOnlyNT { - MoveOnlyNT(const MoveOnlyNT &) = delete; - MoveOnlyNT(MoveOnlyNT &&) {} - MoveOnlyNT &operator=(const MoveOnlyNT &) = default; + MoveOnlyNT(const MoveOnlyNT&) = delete; + MoveOnlyNT(MoveOnlyNT&&) {} + MoveOnlyNT& operator=(const MoveOnlyNT&) = 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; + constexpr CopyAssign(int v, int* alv, int* cpy_ctr, int* cpy_assi, int* move_ctr, int* move_assi) + : value(v), + alive(alv), + copy_construct(cpy_ctr), + copy_assign(cpy_assi), + move_construct(move_ctr), + move_assign(move_assi) { + ++*alive; } - CopyAssign(int v) : value(v) { ++alive; } - CopyAssign(const CopyAssign &o) : value(o.value) { - ++alive; - ++copy_construct; + constexpr CopyAssign(const CopyAssign& o) + : value(o.value), + alive(o.alive), + copy_construct(o.copy_construct), + copy_assign(o.copy_assign), + move_construct(o.move_construct), + move_assign(o.move_assign) { + ++*alive; + ++*copy_construct; } - CopyAssign(CopyAssign &&o) noexcept : value(o.value) { + constexpr CopyAssign(CopyAssign&& o) noexcept + : value(o.value), + alive(o.alive), + copy_construct(o.copy_construct), + copy_assign(o.copy_assign), + move_construct(o.move_construct), + move_assign(o.move_assign) { o.value = -1; - ++alive; - ++move_construct; + ++*alive; + ++*move_construct; } - CopyAssign &operator=(const CopyAssign &o) { - value = o.value; - ++copy_assign; + constexpr CopyAssign& operator=(const CopyAssign& o) { + value = o.value; + alive = o.alive; + copy_construct = o.copy_construct; + copy_assign = o.copy_assign; + move_construct = o.move_construct; + move_assign = o.move_assign; + ++*copy_assign; return *this; } - CopyAssign &operator=(CopyAssign &&o) noexcept { - value = o.value; - o.value = -1; - ++move_assign; + constexpr CopyAssign& operator=(CopyAssign&& o) noexcept { + value = o.value; + alive = o.alive; + copy_construct = o.copy_construct; + copy_assign = o.copy_assign; + move_construct = o.move_construct; + move_assign = o.move_assign; + o.value = -1; + ++*move_assign; return *this; } - ~CopyAssign() { --alive; } + TEST_CONSTEXPR_CXX20 ~CopyAssign() { --*alive; } int value; + int* alive; + int* copy_construct; + int* copy_assign; + int* move_construct; + int* move_assign; }; -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(const CopyMaybeThrows &); - CopyMaybeThrows &operator=(const CopyMaybeThrows &); + CopyMaybeThrows(const CopyMaybeThrows&); + CopyMaybeThrows& operator=(const CopyMaybeThrows&); }; struct CopyDoesThrow { - CopyDoesThrow(const CopyDoesThrow &) noexcept(false); - CopyDoesThrow &operator=(const CopyDoesThrow &) noexcept(false); + CopyDoesThrow(const CopyDoesThrow&) noexcept(false); + CopyDoesThrow& operator=(const CopyDoesThrow&) noexcept(false); }; - struct NTCopyAssign { constexpr NTCopyAssign(int v) : value(v) {} - NTCopyAssign(const NTCopyAssign &) = default; - NTCopyAssign(NTCopyAssign &&) = default; - NTCopyAssign &operator=(const NTCopyAssign &that) { + NTCopyAssign(const NTCopyAssign&) = default; + NTCopyAssign(NTCopyAssign&&) = default; + NTCopyAssign& operator=(const NTCopyAssign& that) { value = that.value; return *this; }; - NTCopyAssign &operator=(NTCopyAssign &&) = delete; + NTCopyAssign& operator=(NTCopyAssign&&) = delete; int value; }; @@ -1540,10 +1787,10 @@ static_assert(std::is_copy_assignable::value, ""); struct TCopyAssign { constexpr TCopyAssign(int v) : value(v) {} - TCopyAssign(const TCopyAssign &) = default; - TCopyAssign(TCopyAssign &&) = default; - TCopyAssign &operator=(const TCopyAssign &) = default; - TCopyAssign &operator=(TCopyAssign &&) = delete; + TCopyAssign(const TCopyAssign&) = default; + TCopyAssign(TCopyAssign&&) = default; + TCopyAssign& operator=(const TCopyAssign&) = default; + TCopyAssign& operator=(TCopyAssign&&) = delete; int value; }; @@ -1551,11 +1798,11 @@ static_assert(std::is_trivially_copy_assignable::value, ""); struct TCopyAssignNTMoveAssign { constexpr TCopyAssignNTMoveAssign(int v) : value(v) {} - TCopyAssignNTMoveAssign(const TCopyAssignNTMoveAssign &) = default; - TCopyAssignNTMoveAssign(TCopyAssignNTMoveAssign &&) = default; - TCopyAssignNTMoveAssign &operator=(const TCopyAssignNTMoveAssign &) = default; - TCopyAssignNTMoveAssign &operator=(TCopyAssignNTMoveAssign &&that) { - value = that.value; + TCopyAssignNTMoveAssign(const TCopyAssignNTMoveAssign&) = default; + TCopyAssignNTMoveAssign(TCopyAssignNTMoveAssign&&) = default; + TCopyAssignNTMoveAssign& operator=(const TCopyAssignNTMoveAssign&) = default; + TCopyAssignNTMoveAssign& operator=(TCopyAssignNTMoveAssign&& that) { + value = that.value; that.value = -1; return *this; } @@ -1567,17 +1814,20 @@ static_assert(std::is_trivially_copy_assignable_v, ""); #ifndef TEST_HAS_NO_EXCEPTIONS struct CopyThrows { CopyThrows() = default; - CopyThrows(const CopyThrows &) { throw 42; } - CopyThrows &operator=(const CopyThrows &) { throw 42; } + CopyThrows(const CopyThrows&) { throw 42; } + CopyThrows& operator=(const CopyThrows&) { throw 42; } }; struct CopyCannotThrow { static int alive; CopyCannotThrow() { ++alive; } - CopyCannotThrow(const CopyCannotThrow &) noexcept { ++alive; } - CopyCannotThrow(CopyCannotThrow &&) noexcept { assert(false); } - CopyCannotThrow &operator=(const CopyCannotThrow &) noexcept = default; - CopyCannotThrow &operator=(CopyCannotThrow &&) noexcept { assert(false); return *this; } + CopyCannotThrow(const CopyCannotThrow&) noexcept { ++alive; } + CopyCannotThrow(CopyCannotThrow&&) noexcept { assert(false); } + CopyCannotThrow& operator=(const CopyCannotThrow&) noexcept = default; + CopyCannotThrow& operator=(CopyCannotThrow&&) noexcept { + assert(false); + return *this; + } }; int CopyCannotThrow::alive = 0; @@ -1585,10 +1835,10 @@ int CopyCannotThrow::alive = 0; struct MoveThrows { static int alive; MoveThrows() { ++alive; } - MoveThrows(const MoveThrows &) { ++alive; } - MoveThrows(MoveThrows &&) { throw 42; } - MoveThrows &operator=(const MoveThrows &) { return *this; } - MoveThrows &operator=(MoveThrows &&) { throw 42; } + MoveThrows(const MoveThrows&) { ++alive; } + MoveThrows(MoveThrows&&) { throw 42; } + MoveThrows& operator=(const MoveThrows&) { return *this; } + MoveThrows& operator=(MoveThrows&&) { throw 42; } ~MoveThrows() { --alive; } }; @@ -1597,20 +1847,21 @@ int MoveThrows::alive = 0; struct MakeEmptyT { static int alive; MakeEmptyT() { ++alive; } - MakeEmptyT(const MakeEmptyT &) { + MakeEmptyT(const MakeEmptyT&) { ++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=(const MakeEmptyT &) { throw 42; } - MakeEmptyT &operator=(MakeEmptyT &&) { throw 42; } + MakeEmptyT(MakeEmptyT&&) { throw 42; } + MakeEmptyT& operator=(const MakeEmptyT&) { throw 42; } + MakeEmptyT& operator=(MakeEmptyT&&) { throw 42; } ~MakeEmptyT() { --alive; } }; int MakeEmptyT::alive = 0; -template void makeEmpty(Variant &v) { +template +void makeEmpty(Variant& v) { Variant v2(std::in_place_type); try { v = std::move(v2); @@ -1621,7 +1872,7 @@ template void makeEmpty(Variant &v) { } #endif // TEST_HAS_NO_EXCEPTIONS -void test_copy_assignment_not_noexcept() { +constexpr void test_copy_assignment_not_noexcept() { { using V = std::variant; static_assert(!std::is_nothrow_copy_assignable::value, ""); @@ -1632,7 +1883,7 @@ void test_copy_assignment_not_noexcept() { } } -void test_copy_assignment_sfinae() { +constexpr void test_copy_assignment_sfinae() { { using V = std::variant; static_assert(std::is_copy_assignable::value, ""); @@ -1655,7 +1906,6 @@ void test_copy_assignment_sfinae() { } // Make sure we properly propagate triviality (see P0602R4). -#if TEST_STD_VER > 17 { using V = std::variant; static_assert(std::is_trivially_copy_assignable::value, ""); @@ -1677,7 +1927,6 @@ void test_copy_assignment_sfinae() { using V = std::variant; static_assert(std::is_trivially_copy_assignable::value, ""); } -#endif // > C++17 } void test_copy_assignment_empty_empty() { @@ -1689,7 +1938,7 @@ void test_copy_assignment_empty_empty() { makeEmpty(v1); V v2(std::in_place_index<0>); makeEmpty(v2); - V &vref = (v1 = v2); + V& vref = (v1 = v2); assert(&vref == &v1); assert(v1.valueless_by_exception()); assert(v1.index() == std::variant_npos); @@ -1705,7 +1954,7 @@ void test_copy_assignment_non_empty_empty() { V v1(std::in_place_index<0>, 42); V v2(std::in_place_index<0>); makeEmpty(v2); - V &vref = (v1 = v2); + V& vref = (v1 = v2); assert(&vref == &v1); assert(v1.valueless_by_exception()); assert(v1.index() == std::variant_npos); @@ -1715,7 +1964,7 @@ void test_copy_assignment_non_empty_empty() { V v1(std::in_place_index<2>, "hello"); V v2(std::in_place_index<0>); makeEmpty(v2); - V &vref = (v1 = v2); + V& vref = (v1 = v2); assert(&vref == &v1); assert(v1.valueless_by_exception()); assert(v1.index() == std::variant_npos); @@ -1731,7 +1980,7 @@ void test_copy_assignment_empty_non_empty() { V v1(std::in_place_index<0>); makeEmpty(v1); V v2(std::in_place_index<0>, 42); - V &vref = (v1 = v2); + V& vref = (v1 = v2); assert(&vref == &v1); assert(v1.index() == 0); assert(std::get<0>(v1) == 42); @@ -1741,7 +1990,7 @@ void test_copy_assignment_empty_non_empty() { V v1(std::in_place_index<0>); makeEmpty(v1); V v2(std::in_place_type, "hello"); - V &vref = (v1 = v2); + V& vref = (v1 = v2); assert(&vref == &v1); assert(v1.index() == 2); assert(std::get<2>(v1) == "hello"); @@ -1749,14 +1998,18 @@ void test_copy_assignment_empty_non_empty() { #endif // TEST_HAS_NO_EXCEPTIONS } -template struct Result { size_t index; T value; }; +template +struct Result { + std::size_t index; + T value; +}; -void test_copy_assignment_same_index() { +TEST_CONSTEXPR_CXX20 void test_copy_assignment_same_index() { { using V = std::variant; V v1(43); V v2(42); - V &vref = (v1 = v2); + V& vref = (v1 = v2); assert(&vref == &v1); assert(v1.index() == 0); assert(std::get<0>(v1) == 42); @@ -1765,43 +2018,30 @@ void test_copy_assignment_same_index() { using V = std::variant; V v1(43l); V v2(42l); - V &vref = (v1 = v2); + V& vref = (v1 = v2); assert(&vref == &v1); assert(v1.index() == 1); assert(std::get<1>(v1) == 42); } { - using V = std::variant; - V v1(std::in_place_type, 43); - V v2(std::in_place_type, 42); - CopyAssign::reset(); - V &vref = (v1 = v2); + using V = std::variant; + int alive = 0; + int copy_construct = 0; + int copy_assign = 0; + int move_construct = 0; + int move_assign = 0; + V v1(std::in_place_type, 43, &alive, ©_construct, ©_assign, &move_construct, &move_assign); + V v2(std::in_place_type, 42, &alive, ©_construct, ©_assign, &move_construct, &move_assign); + 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); + assert(copy_construct == 0); + assert(move_construct == 0); + assert(copy_assign == 1); } -#ifndef TEST_HAS_NO_EXCEPTIONS - using MET = MakeEmptyT; - { - using V = std::variant; - V v1(std::in_place_type); - MET &mref = std::get<1>(v1); - V v2(std::in_place_type); - try { - v1 = v2; - assert(false); - } catch (...) { - } - assert(v1.index() == 1); - assert(&std::get<1>(v1) == &mref); - } -#endif // TEST_HAS_NO_EXCEPTIONS // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). -#if TEST_STD_VER > 17 { struct { constexpr Result operator()() const { @@ -1858,37 +2098,90 @@ void test_copy_assignment_same_index() { static_assert(result.index == 1, ""); static_assert(result.value == 42, ""); } -#endif // > C++17 } -void test_copy_assignment_different_index() { +TEST_CONSTEXPR_CXX20 void test_copy_assignment_different_index() { { using V = std::variant; V v1(43); V v2(42l); - V &vref = (v1 = v2); + V& vref = (v1 = v2); assert(&vref == &v1); assert(v1.index() == 1); assert(std::get<1>(v1) == 42); } { - using V = std::variant; - CopyAssign::reset(); + using V = std::variant; + int alive = 0; + int copy_construct = 0; + int copy_assign = 0; + int move_construct = 0; + int move_assign = 0; V v1(std::in_place_type, 43u); - V v2(std::in_place_type, 42); - assert(CopyAssign::copy_construct == 0); - assert(CopyAssign::move_construct == 0); - assert(CopyAssign::alive == 1); - V &vref = (v1 = v2); + V v2(std::in_place_type, 42, &alive, ©_construct, ©_assign, &move_construct, &move_assign); + assert(copy_construct == 0); + assert(move_construct == 0); + assert(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); + assert(alive == 2); + assert(copy_construct == 1); + assert(move_construct == 1); + assert(copy_assign == 0); } + + // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). + { + struct { + constexpr Result operator()() const { + using V = std::variant; + V v(43); + V v2(42l); + v = v2; + return {v.index(), std::get<1>(v)}; + } + } test; + constexpr auto result = test(); + static_assert(result.index == 1, ""); + static_assert(result.value == 42l, ""); + } + { + struct { + constexpr Result operator()() const { + using V = std::variant; + V v(std::in_place_type, 43u); + V v2(std::in_place_type, 42); + v = v2; + return {v.index(), std::get<1>(v).value}; + } + } test; + constexpr auto result = test(); + static_assert(result.index == 1, ""); + static_assert(result.value == 42, ""); + } +} + +void test_assignment_throw() { #ifndef TEST_HAS_NO_EXCEPTIONS + using MET = MakeEmptyT; + // same index + { + using V = std::variant; + V v1(std::in_place_type); + MET& mref = std::get<1>(v1); + V v2(std::in_place_type); + try { + v1 = v2; + assert(false); + } catch (...) { + } + assert(v1.index() == 1); + assert(&std::get<1>(v1) == &mref); + } + + // difference indices { using V = std::variant; V v1(std::in_place_type, "hello"); @@ -1928,7 +2221,7 @@ void test_copy_assignment_different_index() { using V = std::variant; V v1(std::in_place_type); V v2(std::in_place_type, "hello"); - V &vref = (v1 = v2); + V& vref = (v1 = v2); assert(&vref == &v1); assert(v1.index() == 2); assert(std::get<2>(v1) == "hello"); @@ -1939,7 +2232,7 @@ void test_copy_assignment_different_index() { using V = std::variant; V v1(std::in_place_type); V v2(std::in_place_type, "hello"); - V &vref = (v1 = v2); + V& vref = (v1 = v2); assert(&vref == &v1); assert(v1.index() == 2); assert(std::get<2>(v1) == "hello"); @@ -1947,74 +2240,84 @@ void test_copy_assignment_different_index() { assert(std::get<2>(v2) == "hello"); } #endif // TEST_HAS_NO_EXCEPTIONS - - // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). -#if TEST_STD_VER > 17 - { - struct { - constexpr Result operator()() const { - using V = std::variant; - V v(43); - V v2(42l); - v = v2; - return {v.index(), std::get<1>(v)}; - } - } test; - constexpr auto result = test(); - static_assert(result.index == 1, ""); - static_assert(result.value == 42l, ""); - } - { - struct { - constexpr Result operator()() const { - using V = std::variant; - V v(std::in_place_type, 43u); - V v2(std::in_place_type, 42); - v = v2; - return {v.index(), std::get<1>(v).value}; - } - } test; - constexpr auto result = test(); - static_assert(result.index == 1, ""); - static_assert(result.value == 42, ""); - } -#endif // > C++17 } -template -constexpr bool test_constexpr_assign_imp( - std::variant&& v, ValueType&& new_value) -{ - const std::variant cp( - std::forward(new_value)); +template +constexpr void test_constexpr_assign_imp(T&& v, ValueType&& new_value) { + using Variant = std::decay_t; + const Variant cp(std::forward(new_value)); v = cp; - return v.index() == NewIdx && - std::get(v) == std::get(cp); + assert(v.index() == NewIdx); + assert(std::get(v) == std::get(cp)); } -void test_constexpr_copy_assignment() { +constexpr void test_constexpr_copy_assignment_trivial() { // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). -#if TEST_STD_VER > 17 using V = std::variant; static_assert(std::is_trivially_copyable::value, ""); static_assert(std::is_trivially_copy_assignable::value, ""); - static_assert(test_constexpr_assign_imp<0>(V(42l), 101l), ""); - static_assert(test_constexpr_assign_imp<0>(V(nullptr), 101l), ""); - static_assert(test_constexpr_assign_imp<1>(V(42l), nullptr), ""); - static_assert(test_constexpr_assign_imp<2>(V(42l), 101), ""); -#endif // > C++17 + test_constexpr_assign_imp<0>(V(42l), 101l); + test_constexpr_assign_imp<0>(V(nullptr), 101l); + test_constexpr_assign_imp<1>(V(42l), nullptr); + test_constexpr_assign_imp<2>(V(42l), 101); } -int run_test() { +struct NonTrivialCopyAssign { + int i = 0; + constexpr NonTrivialCopyAssign(int ii) : i(ii) {} + constexpr NonTrivialCopyAssign(const NonTrivialCopyAssign& other) : i(other.i) {} + constexpr NonTrivialCopyAssign& operator=(const NonTrivialCopyAssign& o) { + i = o.i; + return *this; + } + TEST_CONSTEXPR_CXX20 ~NonTrivialCopyAssign() = default; + friend constexpr bool operator==(const NonTrivialCopyAssign& x, const NonTrivialCopyAssign& y) { return x.i == y.i; } +}; + +constexpr void test_constexpr_copy_assignment_non_trivial() { + // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). + using V = std::variant; + static_assert(!std::is_trivially_copyable::value, ""); + static_assert(!std::is_trivially_copy_assignable::value, ""); + test_constexpr_assign_imp<0>(V(42l), 101l); + test_constexpr_assign_imp<0>(V(nullptr), 101l); + test_constexpr_assign_imp<1>(V(42l), nullptr); + test_constexpr_assign_imp<2>(V(42l), NonTrivialCopyAssign(5)); + test_constexpr_assign_imp<2>(V(NonTrivialCopyAssign(3)), NonTrivialCopyAssign(5)); +} + +void non_constexpr_test() { 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_assignment_throw(); +} + +constexpr bool cxx17_constexpr_test() { test_copy_assignment_sfinae(); test_copy_assignment_not_noexcept(); - test_constexpr_copy_assignment(); + test_constexpr_copy_assignment_trivial(); + return true; +} + +TEST_CONSTEXPR_CXX20 bool cxx20_constexpr_test() { + test_copy_assignment_same_index(); + test_copy_assignment_different_index(); + test_constexpr_copy_assignment_non_trivial(); + + return true; +} + +int run_test() { + non_constexpr_test(); + cxx17_constexpr_test(); + cxx20_constexpr_test(); + + static_assert(cxx17_constexpr_test()); +#if TEST_STD_VER >= 20 + static_assert(cxx20_constexpr_test()); +#endif return 0; } } // namespace assign::copy @@ -2030,18 +2333,12 @@ int run_test() { //===----------------------------------------------------------------------===// -// The following compilers don't generate constexpr special members correctly. -// XFAIL: clang-3.5, clang-3.6, clang-3.7, clang-3.8 -// XFAIL: apple-clang-6, apple-clang-7, apple-clang-8.0 - -// XFAIL: dylib-has-no-bad_variant_access && !libcpp-no-exceptions - // // template class variant; -// variant& operator=(variant&&) noexcept(see below); // constexpr in C++20 +// constexpr variant& operator=(variant&&) noexcept(see below); #include #include @@ -2054,71 +2351,70 @@ int run_test() { namespace assign::move { struct NoCopy { - NoCopy(const NoCopy &) = delete; - NoCopy &operator=(const NoCopy &) = default; + NoCopy(const NoCopy&) = delete; + NoCopy& operator=(const NoCopy&) = default; }; struct CopyOnly { - CopyOnly(const CopyOnly &) = default; - CopyOnly(CopyOnly &&) = delete; - CopyOnly &operator=(const CopyOnly &) = default; - CopyOnly &operator=(CopyOnly &&) = delete; + CopyOnly(const CopyOnly&) = default; + CopyOnly(CopyOnly&&) = delete; + CopyOnly& operator=(const CopyOnly&) = default; + CopyOnly& operator=(CopyOnly&&) = delete; }; struct MoveOnly { - MoveOnly(const MoveOnly &) = delete; - MoveOnly(MoveOnly &&) = default; - MoveOnly &operator=(const MoveOnly &) = delete; - MoveOnly &operator=(MoveOnly &&) = default; + MoveOnly(const MoveOnly&) = delete; + MoveOnly(MoveOnly&&) = default; + MoveOnly& operator=(const MoveOnly&) = delete; + MoveOnly& operator=(MoveOnly&&) = default; }; struct MoveOnlyNT { - MoveOnlyNT(const MoveOnlyNT &) = delete; - MoveOnlyNT(MoveOnlyNT &&) {} - MoveOnlyNT &operator=(const MoveOnlyNT &) = delete; - MoveOnlyNT &operator=(MoveOnlyNT &&) = default; + MoveOnlyNT(const MoveOnlyNT&) = delete; + MoveOnlyNT(MoveOnlyNT&&) {} + MoveOnlyNT& operator=(const MoveOnlyNT&) = delete; + MoveOnlyNT& operator=(MoveOnlyNT&&) = default; }; struct MoveOnlyOddNothrow { - MoveOnlyOddNothrow(MoveOnlyOddNothrow &&) noexcept(false) {} - MoveOnlyOddNothrow(const MoveOnlyOddNothrow &) = delete; - MoveOnlyOddNothrow &operator=(MoveOnlyOddNothrow &&) noexcept = default; - MoveOnlyOddNothrow &operator=(const MoveOnlyOddNothrow &) = delete; + MoveOnlyOddNothrow(MoveOnlyOddNothrow&&) noexcept(false) {} + MoveOnlyOddNothrow(const MoveOnlyOddNothrow&) = delete; + MoveOnlyOddNothrow& operator=(MoveOnlyOddNothrow&&) noexcept = default; + MoveOnlyOddNothrow& operator=(const MoveOnlyOddNothrow&) = delete; }; struct MoveAssignOnly { - MoveAssignOnly(MoveAssignOnly &&) = delete; - MoveAssignOnly &operator=(MoveAssignOnly &&) = default; + 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; + constexpr MoveAssign(int v, int* move_ctor, int* move_assi) + : value(v), move_construct(move_ctor), move_assign(move_assi) {} + constexpr MoveAssign(MoveAssign&& o) : value(o.value), move_construct(o.move_construct), move_assign(o.move_assign) { + ++*move_construct; o.value = -1; } - MoveAssign &operator=(MoveAssign &&o) { - value = o.value; - ++move_assign; + constexpr MoveAssign& operator=(MoveAssign&& o) { + value = o.value; + move_construct = o.move_construct; + move_assign = o.move_assign; + ++*move_assign; o.value = -1; return *this; } int value; + int* move_construct; + int* move_assign; }; -int MoveAssign::move_construct = 0; -int MoveAssign::move_assign = 0; - struct NTMoveAssign { constexpr NTMoveAssign(int v) : value(v) {} - NTMoveAssign(const NTMoveAssign &) = default; - NTMoveAssign(NTMoveAssign &&) = default; - NTMoveAssign &operator=(const NTMoveAssign &that) = default; - NTMoveAssign &operator=(NTMoveAssign &&that) { - value = that.value; + NTMoveAssign(const NTMoveAssign&) = default; + NTMoveAssign(NTMoveAssign&&) = default; + NTMoveAssign& operator=(const NTMoveAssign& that) = default; + NTMoveAssign& operator=(NTMoveAssign&& that) { + value = that.value; that.value = -1; return *this; }; @@ -2130,10 +2426,10 @@ static_assert(std::is_move_assignable::value, ""); struct TMoveAssign { constexpr TMoveAssign(int v) : value(v) {} - TMoveAssign(const TMoveAssign &) = delete; - TMoveAssign(TMoveAssign &&) = default; - TMoveAssign &operator=(const TMoveAssign &) = delete; - TMoveAssign &operator=(TMoveAssign &&) = default; + TMoveAssign(const TMoveAssign&) = delete; + TMoveAssign(TMoveAssign&&) = default; + TMoveAssign& operator=(const TMoveAssign&) = delete; + TMoveAssign& operator=(TMoveAssign&&) = default; int value; }; @@ -2141,13 +2437,13 @@ static_assert(std::is_trivially_move_assignable::value, ""); struct TMoveAssignNTCopyAssign { constexpr TMoveAssignNTCopyAssign(int v) : value(v) {} - TMoveAssignNTCopyAssign(const TMoveAssignNTCopyAssign &) = default; - TMoveAssignNTCopyAssign(TMoveAssignNTCopyAssign &&) = default; - TMoveAssignNTCopyAssign &operator=(const TMoveAssignNTCopyAssign &that) { + TMoveAssignNTCopyAssign(const TMoveAssignNTCopyAssign&) = default; + TMoveAssignNTCopyAssign(TMoveAssignNTCopyAssign&&) = default; + TMoveAssignNTCopyAssign& operator=(const TMoveAssignNTCopyAssign& that) { value = that.value; return *this; } - TMoveAssignNTCopyAssign &operator=(TMoveAssignNTCopyAssign &&) = default; + TMoveAssignNTCopyAssign& operator=(TMoveAssignNTCopyAssign&&) = default; int value; }; @@ -2157,16 +2453,13 @@ struct TrivialCopyNontrivialMove { TrivialCopyNontrivialMove(TrivialCopyNontrivialMove const&) = default; TrivialCopyNontrivialMove(TrivialCopyNontrivialMove&&) noexcept {} TrivialCopyNontrivialMove& operator=(TrivialCopyNontrivialMove const&) = default; - TrivialCopyNontrivialMove& operator=(TrivialCopyNontrivialMove&&) noexcept { - return *this; - } + TrivialCopyNontrivialMove& operator=(TrivialCopyNontrivialMove&&) noexcept { return *this; } }; static_assert(std::is_trivially_copy_assignable_v, ""); static_assert(!std::is_trivially_move_assignable_v, ""); - -void test_move_assignment_noexcept() { +constexpr void test_move_assignment_noexcept() { { using V = std::variant; static_assert(std::is_nothrow_move_assignable::value, ""); @@ -2193,7 +2486,7 @@ void test_move_assignment_noexcept() { } } -void test_move_assignment_sfinae() { +constexpr void test_move_assignment_sfinae() { { using V = std::variant; static_assert(std::is_move_assignable::value, ""); @@ -2222,7 +2515,6 @@ void test_move_assignment_sfinae() { } // Make sure we properly propagate triviality (see P0602R4). -#if TEST_STD_VER > 17 { using V = std::variant; static_assert(std::is_trivially_move_assignable::value, ""); @@ -2248,7 +2540,6 @@ void test_move_assignment_sfinae() { using V = std::variant; static_assert(std::is_trivially_move_assignable::value, ""); } -#endif // > C++17 } void test_move_assignment_empty_empty() { @@ -2260,7 +2551,7 @@ void test_move_assignment_empty_empty() { makeEmpty(v1); V v2(std::in_place_index<0>); makeEmpty(v2); - V &vref = (v1 = std::move(v2)); + V& vref = (v1 = std::move(v2)); assert(&vref == &v1); assert(v1.valueless_by_exception()); assert(v1.index() == std::variant_npos); @@ -2276,7 +2567,7 @@ void test_move_assignment_non_empty_empty() { V v1(std::in_place_index<0>, 42); V v2(std::in_place_index<0>); makeEmpty(v2); - V &vref = (v1 = std::move(v2)); + V& vref = (v1 = std::move(v2)); assert(&vref == &v1); assert(v1.valueless_by_exception()); assert(v1.index() == std::variant_npos); @@ -2286,7 +2577,7 @@ void test_move_assignment_non_empty_empty() { V v1(std::in_place_index<2>, "hello"); V v2(std::in_place_index<0>); makeEmpty(v2); - V &vref = (v1 = std::move(v2)); + V& vref = (v1 = std::move(v2)); assert(&vref == &v1); assert(v1.valueless_by_exception()); assert(v1.index() == std::variant_npos); @@ -2302,7 +2593,7 @@ void test_move_assignment_empty_non_empty() { V v1(std::in_place_index<0>); makeEmpty(v1); V v2(std::in_place_index<0>, 42); - V &vref = (v1 = std::move(v2)); + V& vref = (v1 = std::move(v2)); assert(&vref == &v1); assert(v1.index() == 0); assert(std::get<0>(v1) == 42); @@ -2312,7 +2603,7 @@ void test_move_assignment_empty_non_empty() { V v1(std::in_place_index<0>); makeEmpty(v1); V v2(std::in_place_type, "hello"); - V &vref = (v1 = std::move(v2)); + V& vref = (v1 = std::move(v2)); assert(&vref == &v1); assert(v1.index() == 2); assert(std::get<2>(v1) == "hello"); @@ -2320,14 +2611,18 @@ void test_move_assignment_empty_non_empty() { #endif // TEST_HAS_NO_EXCEPTIONS } -template struct Result { size_t index; T value; }; +template +struct Result { + std::size_t index; + T value; +}; -void test_move_assignment_same_index() { +TEST_CONSTEXPR_CXX20 void test_move_assignment_same_index() { { using V = std::variant; V v1(43); V v2(42); - V &vref = (v1 = std::move(v2)); + V& vref = (v1 = std::move(v2)); assert(&vref == &v1); assert(v1.index() == 0); assert(std::get<0>(v1) == 42); @@ -2336,42 +2631,26 @@ void test_move_assignment_same_index() { using V = std::variant; V v1(43l); V v2(42l); - V &vref = (v1 = std::move(v2)); + V& vref = (v1 = std::move(v2)); assert(&vref == &v1); assert(v1.index() == 1); assert(std::get<1>(v1) == 42); } { - using V = std::variant; - V v1(std::in_place_type, 43); - V v2(std::in_place_type, 42); - MoveAssign::reset(); - V &vref = (v1 = std::move(v2)); + using V = std::variant; + int move_construct = 0; + int move_assign = 0; + V v1(std::in_place_type, 43, &move_construct, &move_assign); + V v2(std::in_place_type, 42, &move_construct, &move_assign); + 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); + assert(move_construct == 0); + assert(move_assign == 1); } -#ifndef TEST_HAS_NO_EXCEPTIONS - using MET = MakeEmptyT; - { - using V = std::variant; - V v1(std::in_place_type); - MET &mref = std::get<1>(v1); - V v2(std::in_place_type); - try { - v1 = std::move(v2); - assert(false); - } catch (...) { - } - assert(v1.index() == 1); - assert(&std::get<1>(v1) == &mref); - } -#endif // TEST_HAS_NO_EXCEPTIONS // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). -#if TEST_STD_VER > 17 { struct { constexpr Result operator()() const { @@ -2414,58 +2693,33 @@ void test_move_assignment_same_index() { static_assert(result.index == 1, ""); static_assert(result.value == 42, ""); } -#endif // > C++17 } -void test_move_assignment_different_index() { +TEST_CONSTEXPR_CXX20 void test_move_assignment_different_index() { { using V = std::variant; V v1(43); V v2(42l); - V &vref = (v1 = std::move(v2)); + V& vref = (v1 = std::move(v2)); assert(&vref == &v1); assert(v1.index() == 1); assert(std::get<1>(v1) == 42); } { - using V = std::variant; + using V = std::variant; + int move_construct = 0; + int move_assign = 0; V v1(std::in_place_type, 43u); - V v2(std::in_place_type, 42); - MoveAssign::reset(); - V &vref = (v1 = std::move(v2)); + V v2(std::in_place_type, 42, &move_construct, &move_assign); + 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); + assert(move_construct == 1); + assert(move_assign == 0); } -#ifndef TEST_HAS_NO_EXCEPTIONS - using MET = MakeEmptyT; - { - using V = std::variant; - V v1(std::in_place_type); - V v2(std::in_place_type); - try { - v1 = std::move(v2); - assert(false); - } catch (...) { - } - assert(v1.valueless_by_exception()); - assert(v1.index() == std::variant_npos); - } - { - using V = std::variant; - V v1(std::in_place_type); - V v2(std::in_place_type, "hello"); - V &vref = (v1 = std::move(v2)); - assert(&vref == &v1); - assert(v1.index() == 2); - assert(std::get<2>(v1) == "hello"); - } -#endif // TEST_HAS_NO_EXCEPTIONS // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). -#if TEST_STD_VER > 17 { struct { constexpr Result operator()() const { @@ -2494,44 +2748,129 @@ void test_move_assignment_different_index() { static_assert(result.index == 1, ""); static_assert(result.value == 42, ""); } -#endif // > C++17 } -template -constexpr bool test_constexpr_assign_imp( - std::variant&& v, ValueType&& new_value) -{ - std::variant v2( - std::forward(new_value)); +void test_assignment_throw() { +#ifndef TEST_HAS_NO_EXCEPTIONS + using MET = MakeEmptyT; + // same index + { + using V = std::variant; + V v1(std::in_place_type); + MET& mref = std::get<1>(v1); + V v2(std::in_place_type); + try { + v1 = std::move(v2); + assert(false); + } catch (...) { + } + assert(v1.index() == 1); + assert(&std::get<1>(v1) == &mref); + } + + // different indices + { + using V = std::variant; + V v1(std::in_place_type); + V v2(std::in_place_type); + try { + v1 = std::move(v2); + assert(false); + } catch (...) { + } + assert(v1.valueless_by_exception()); + assert(v1.index() == std::variant_npos); + } + { + using V = std::variant; + V v1(std::in_place_type); + V v2(std::in_place_type, "hello"); + V& vref = (v1 = std::move(v2)); + assert(&vref == &v1); + assert(v1.index() == 2); + assert(std::get<2>(v1) == "hello"); + } +#endif // TEST_HAS_NO_EXCEPTIONS +} + +template +constexpr void test_constexpr_assign_imp(T&& v, ValueType&& new_value) { + using Variant = std::decay_t; + Variant v2(std::forward(new_value)); const auto cp = v2; - v = std::move(v2); - return v.index() == NewIdx && - std::get(v) == std::get(cp); + v = std::move(v2); + assert(v.index() == NewIdx); + assert(std::get(v) == std::get(cp)); } -void test_constexpr_move_assignment() { +constexpr void test_constexpr_move_assignment_trivial() { // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). -#if TEST_STD_VER > 17 using V = std::variant; static_assert(std::is_trivially_copyable::value, ""); static_assert(std::is_trivially_move_assignable::value, ""); - static_assert(test_constexpr_assign_imp<0>(V(42l), 101l), ""); - static_assert(test_constexpr_assign_imp<0>(V(nullptr), 101l), ""); - static_assert(test_constexpr_assign_imp<1>(V(42l), nullptr), ""); - static_assert(test_constexpr_assign_imp<2>(V(42l), 101), ""); -#endif // > C++17 + test_constexpr_assign_imp<0>(V(42l), 101l); + test_constexpr_assign_imp<0>(V(nullptr), 101l); + test_constexpr_assign_imp<1>(V(42l), nullptr); + test_constexpr_assign_imp<2>(V(42l), 101); } -int run_test() { +struct NonTrivialMoveAssign { + int i = 0; + constexpr NonTrivialMoveAssign(int ii) : i(ii) {} + constexpr NonTrivialMoveAssign(const NonTrivialMoveAssign& other) = default; + constexpr NonTrivialMoveAssign(NonTrivialMoveAssign&& other) : i(other.i) {} + constexpr NonTrivialMoveAssign& operator=(const NonTrivialMoveAssign&) = default; + constexpr NonTrivialMoveAssign& operator=(NonTrivialMoveAssign&& o) { + i = o.i; + return *this; + } + TEST_CONSTEXPR_CXX20 ~NonTrivialMoveAssign() = default; + friend constexpr bool operator==(const NonTrivialMoveAssign& x, const NonTrivialMoveAssign& y) { return x.i == y.i; } +}; + +TEST_CONSTEXPR_CXX20 void test_constexpr_move_assignment_non_trivial() { + using V = std::variant; + static_assert(!std::is_trivially_copyable::value); + static_assert(!std::is_trivially_move_assignable::value); + test_constexpr_assign_imp<0>(V(42l), 101l); + test_constexpr_assign_imp<0>(V(nullptr), 101l); + test_constexpr_assign_imp<1>(V(42l), nullptr); + test_constexpr_assign_imp<2>(V(42l), NonTrivialMoveAssign(5)); + test_constexpr_assign_imp<2>(V(NonTrivialMoveAssign(3)), NonTrivialMoveAssign(5)); +} + +void non_constexpr_test() { 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_assignment_throw(); +} + +constexpr bool cxx17_constexpr_test() { test_move_assignment_sfinae(); test_move_assignment_noexcept(); - test_constexpr_move_assignment(); + test_constexpr_move_assignment_trivial(); + return true; +} + +TEST_CONSTEXPR_CXX20 bool cxx20_constexpr_test() { + test_move_assignment_same_index(); + test_move_assignment_different_index(); + test_constexpr_move_assignment_non_trivial(); + + return true; +} + +int run_test() { + non_constexpr_test(); + cxx17_constexpr_test(); + cxx20_constexpr_test(); + + static_assert(cxx17_constexpr_test()); +#if TEST_STD_VER >= 20 + static_assert(cxx20_constexpr_test()); +#endif return 0; } } // namespace assign::move @@ -2547,7 +2886,6 @@ int run_test() { //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_variant_access && !libcpp-no-exceptions // @@ -2560,6 +2898,7 @@ int run_test() { #include #include #include +#include #include #include "test_macros.h" @@ -2574,17 +2913,17 @@ struct Dummy { struct ThrowsCtorT { ThrowsCtorT(int) noexcept(false) {} - ThrowsCtorT &operator=(int) noexcept { return *this; } + ThrowsCtorT& operator=(int) noexcept { return *this; } }; struct ThrowsAssignT { ThrowsAssignT(int) noexcept {} - ThrowsAssignT &operator=(int) noexcept(false) { return *this; } + ThrowsAssignT& operator=(int) noexcept(false) { return *this; } }; struct NoThrowT { NoThrowT(int) noexcept {} - NoThrowT &operator=(int) noexcept { return *this; } + NoThrowT& operator=(int) noexcept { return *this; } }; } // namespace MetaHelpers @@ -2596,7 +2935,7 @@ struct ThrowsCtorT { int value; ThrowsCtorT() : value(0) {} ThrowsCtorT(int) noexcept(false) { throw 42; } - ThrowsCtorT &operator=(int v) noexcept { + ThrowsCtorT& operator=(int v) noexcept { value = v; return *this; } @@ -2605,9 +2944,12 @@ struct ThrowsCtorT { struct MoveCrashes { int value; MoveCrashes(int v = 0) noexcept : value{v} {} - MoveCrashes(MoveCrashes &&) noexcept { assert(false); } - MoveCrashes &operator=(MoveCrashes &&) noexcept { assert(false); return *this; } - MoveCrashes &operator=(int v) noexcept { + MoveCrashes(MoveCrashes&&) noexcept { assert(false); } + MoveCrashes& operator=(MoveCrashes&&) noexcept { + assert(false); + return *this; + } + MoveCrashes& operator=(int v) noexcept { value = v; return *this; } @@ -2617,8 +2959,8 @@ struct ThrowsCtorTandMove { int value; ThrowsCtorTandMove() : value(0) {} ThrowsCtorTandMove(int) noexcept(false) { throw 42; } - ThrowsCtorTandMove(ThrowsCtorTandMove &&) noexcept(false) { assert(false); } - ThrowsCtorTandMove &operator=(int v) noexcept { + ThrowsCtorTandMove(ThrowsCtorTandMove&&) noexcept(false) { assert(false); } + ThrowsCtorTandMove& operator=(int v) noexcept { value = v; return *this; } @@ -2628,14 +2970,14 @@ struct ThrowsAssignT { int value; ThrowsAssignT() : value(0) {} ThrowsAssignT(int v) noexcept : value(v) {} - ThrowsAssignT &operator=(int) noexcept(false) { throw 42; } + 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 { + NoThrowT& operator=(int v) noexcept { value = v; return *this; } @@ -2644,7 +2986,7 @@ struct NoThrowT { #endif // !defined(TEST_HAS_NO_EXCEPTIONS) } // namespace RuntimeHelpers -void test_T_assignment_noexcept() { +constexpr void test_T_assignment_noexcept() { using namespace MetaHelpers; { using V = std::variant; @@ -2660,53 +3002,45 @@ void test_T_assignment_noexcept() { } } -void test_T_assignment_sfinae() { +constexpr void test_T_assignment_sfinae() { { using V = std::variant; static_assert(!std::is_assignable::value, "ambiguous"); } { using V = std::variant; - static_assert(!std::is_assignable::value, "ambiguous"); + static_assert(!std::is_assignable::value, "ambiguous"); } { - using V = std::variant; + using V = std::variant; static_assert(!std::is_assignable::value, "no matching operator="); } -#if _HAS_CXX20 { using V = std::variant; - static_assert(std::is_assignable::value == false, - "no matching operator="); + static_assert(!std::is_assignable::value, "no matching operator="); } { using V = std::variant, bool>; - static_assert(!std::is_assignable>::value, - "no explicit bool in operator="); + static_assert(!std::is_assignable>::value, "no explicit bool in operator="); struct X { operator void*(); }; #ifndef __EDG__ // TRANSITION, VSO-1327220 - static_assert(!std::is_assignable::value, - "no boolean conversion in operator="); -#endif // !__EDG__ - // libc++ is missing P1957R2 - static_assert(std::is_assignable::value, - "no converted to bool in operator="); + static_assert(!std::is_assignable::value, "no boolean conversion in operator="); +#endif // ^^^ no workaround ^^^ + static_assert(std::is_assignable::value, "converted to bool in operator="); } -#endif // _HAS_CXX20 { struct X {}; struct Y { operator X(); }; using V = std::variant; - static_assert(std::is_assignable::value, - "regression on user-defined conversions in operator="); + static_assert(std::is_assignable::value, "regression on user-defined conversions in operator="); } } -void test_T_assignment_basic() { +TEST_CONSTEXPR_CXX20 void test_T_assignment_basic() { { std::variant v(43); v = 42; @@ -2722,8 +3056,6 @@ void test_T_assignment_basic() { assert(v.index() == 1); assert(std::get<1>(v) == 43); } -#if _HAS_CXX20 -#ifndef TEST_VARIANT_ALLOWS_NARROWING_CONVERSIONS { std::variant v; v = 42; @@ -2733,35 +3065,153 @@ void test_T_assignment_basic() { assert(v.index() == 0); assert(std::get<0>(v) == 43); } -#endif { std::variant v = true; - v = "bar"; + v = "bar"; assert(v.index() == 0); assert(std::get<0>(v) == "bar"); } -#ifndef TEST_PERMISSIVE - { - std::variant> v; - v = nullptr; +} + +void test_T_assignment_basic_no_constexpr() { + std::variant> v; + v = nullptr; + if constexpr (is_permissive) { + assert(v.index() == 0); + assert(std::get<0>(v) == false); + } else { assert(v.index() == 1); assert(std::get<1>(v) == nullptr); } -#endif // !TEST_PERMISSIVE -#endif // _HAS_CXX20 +} + +struct TraceStat { + int construct = 0; + int copy_construct = 0; + int copy_assign = 0; + int move_construct = 0; + int move_assign = 0; + int T_copy_assign = 0; + int T_move_assign = 0; + int destroy = 0; +}; + +template +struct Trace { + struct T {}; + + constexpr Trace(TraceStat* s) noexcept(CtorNoexcept) : stat(s) { ++s->construct; } + constexpr Trace(T) noexcept(CtorNoexcept) : stat(nullptr) {} + constexpr Trace(const Trace& o) : stat(o.stat) { ++stat->copy_construct; } + constexpr Trace(Trace&& o) noexcept(MoveCtorNoexcept) : stat(o.stat) { ++stat->move_construct; } + constexpr Trace& operator=(const Trace&) { + ++stat->copy_assign; + return *this; + } + constexpr Trace& operator=(Trace&&) noexcept { + ++stat->move_assign; + return *this; + } + + constexpr Trace& operator=(const T&) { + ++stat->T_copy_assign; + return *this; + } + constexpr Trace& operator=(T&&) noexcept { + ++stat->T_move_assign; + return *this; + } + TEST_CONSTEXPR_CXX20 ~Trace() { ++stat->destroy; } + + TraceStat* stat; +}; + +TEST_CONSTEXPR_CXX20 void test_T_assignment_performs_construction() { { - std::variant v = 42; - v = false; - assert(v.index() == 0); - assert(!std::get<0>(v)); - bool lvt = true; - v = lvt; - assert(v.index() == 0); - assert(std::get<0>(v)); + using V = std::variant>; + TraceStat stat; + V v{1}; + v = &stat; + assert(stat.construct == 1); + assert(stat.copy_construct == 0); + assert(stat.move_construct == 0); + assert(stat.copy_assign == 0); + assert(stat.move_assign == 0); + assert(stat.destroy == 0); + } + { + using V = std::variant>; + TraceStat stat; + V v{1}; + v = &stat; + assert(stat.construct == 1); + assert(stat.copy_construct == 0); + assert(stat.move_construct == 1); + assert(stat.copy_assign == 0); + assert(stat.move_assign == 0); + assert(stat.destroy == 1); + } + + { + using V = std::variant>; + TraceStat stat; + V v{1}; + v = &stat; + assert(stat.construct == 1); + assert(stat.copy_construct == 0); + assert(stat.move_construct == 0); + assert(stat.copy_assign == 0); + assert(stat.move_assign == 0); + assert(stat.destroy == 0); + } + + { + using V = std::variant>; + TraceStat stat; + V v{1}; + v = &stat; + assert(stat.construct == 1); + assert(stat.copy_construct == 0); + assert(stat.move_construct == 0); + assert(stat.copy_assign == 0); + assert(stat.move_assign == 0); + assert(stat.destroy == 0); } } -void test_T_assignment_performs_construction() { +TEST_CONSTEXPR_CXX20 void test_T_assignment_performs_assignment() { + { + using V = std::variant>; + TraceStat stat; + V v{&stat}; + v = Trace::T{}; + assert(stat.construct == 1); + assert(stat.copy_construct == 0); + assert(stat.move_construct == 0); + assert(stat.copy_assign == 0); + assert(stat.move_assign == 0); + assert(stat.T_copy_assign == 0); + assert(stat.T_move_assign == 1); + assert(stat.destroy == 0); + } + { + using V = std::variant>; + TraceStat stat; + V v{&stat}; + Trace::T t; + v = t; + assert(stat.construct == 1); + assert(stat.copy_construct == 0); + assert(stat.move_construct == 0); + assert(stat.copy_assign == 0); + assert(stat.move_assign == 0); + assert(stat.T_copy_assign == 1); + assert(stat.T_move_assign == 0); + assert(stat.destroy == 0); + } +} + +void test_T_assignment_performs_construction_throw() { using namespace RuntimeHelpers; #ifndef TEST_HAS_NO_EXCEPTIONS { @@ -2785,7 +3235,7 @@ void test_T_assignment_performs_construction() { #endif // TEST_HAS_NO_EXCEPTIONS } -void test_T_assignment_performs_assignment() { +void test_T_assignment_performs_assignment_throw() { using namespace RuntimeHelpers; #ifndef TEST_HAS_NO_EXCEPTIONS { @@ -2827,13 +3277,38 @@ void test_T_assignment_performs_assignment() { #endif // TEST_HAS_NO_EXCEPTIONS } -int run_test() { +TEST_CONSTEXPR_CXX20 void test_T_assignment_vector_bool() { + std::vector vec = {true}; + std::variant v; + v = vec[0]; + assert(v.index() == 0); + assert(std::get<0>(v) == true); +} + +void non_constexpr_test() { + test_T_assignment_basic_no_constexpr(); + test_T_assignment_performs_construction_throw(); + test_T_assignment_performs_assignment_throw(); +} + +TEST_CONSTEXPR_CXX20 bool test() { test_T_assignment_basic(); test_T_assignment_performs_construction(); test_T_assignment_performs_assignment(); test_T_assignment_noexcept(); test_T_assignment_sfinae(); + test_T_assignment_vector_bool(); + return true; +} + +int run_test() { + test(); + non_constexpr_test(); + +#if TEST_STD_VER >= 20 + static_assert(test()); +#endif return 0; } } // namespace assign::T @@ -2849,6 +3324,7 @@ int run_test() { //===----------------------------------------------------------------------===// + // // template class variant; @@ -2866,26 +3342,18 @@ int run_test() { static_assert(!std::is_constructible, int>::value, ""); static_assert(!std::is_constructible, int>::value, ""); -#if _HAS_CXX20 - static_assert(std::is_constructible, int>::value == false, ""); + static_assert(!std::is_constructible, int>::value, ""); - static_assert(std::is_constructible, int>::value - == false, ""); - static_assert(std::is_constructible, int>::value - == false, ""); + static_assert(!std::is_constructible, int>::value, ""); + static_assert(!std::is_constructible, int>::value, ""); static_assert(!std::is_constructible, int>::value, ""); static_assert(!std::is_constructible, decltype("meow")>::value, ""); static_assert(!std::is_constructible, decltype("meow")>::value, ""); - static_assert(!std::is_constructible, decltype("meow")>::value, ""); - // libc++ is missing P1957R2 static_assert(std::is_constructible, std::true_type>::value, ""); static_assert(!std::is_constructible, std::unique_ptr >::value, ""); -#ifndef TEST_PERMISSIVE - static_assert(!std::is_constructible, decltype(nullptr)>::value, ""); -#endif // !TEST_PERMISSIVE -#endif // _HAS_CXX20 + static_assert(std::is_constructible, decltype(nullptr)>::value == is_permissive, ""); return 0; } @@ -2902,13 +3370,12 @@ int run_test() //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_variant_access && !libcpp-no-exceptions // // template class variant; -// variant(variant const&); // constexpr in C++20 +// constexpr variant(variant const&); #include #include @@ -2919,30 +3386,30 @@ int run_test() namespace ctor::copy { struct NonT { - NonT(int v) : value(v) {} - NonT(const NonT &o) : value(o.value) {} + constexpr NonT(int v) : value(v) {} + constexpr NonT(const NonT& o) : value(o.value) {} int value; }; static_assert(!std::is_trivially_copy_constructible::value, ""); struct NoCopy { - NoCopy(const NoCopy &) = delete; + NoCopy(const NoCopy&) = delete; }; struct MoveOnly { - MoveOnly(const MoveOnly &) = delete; - MoveOnly(MoveOnly &&) = default; + MoveOnly(const MoveOnly&) = delete; + MoveOnly(MoveOnly&&) = default; }; struct MoveOnlyNT { - MoveOnlyNT(const MoveOnlyNT &) = delete; - MoveOnlyNT(MoveOnlyNT &&) {} + MoveOnlyNT(const MoveOnlyNT&) = delete; + MoveOnlyNT(MoveOnlyNT&&) {} }; struct NTCopy { constexpr NTCopy(int v) : value(v) {} - NTCopy(const NTCopy &that) : value(that.value) {} - NTCopy(NTCopy &&) = delete; + NTCopy(const NTCopy& that) : value(that.value) {} + NTCopy(NTCopy&&) = delete; int value; }; @@ -2951,8 +3418,8 @@ static_assert(std::is_copy_constructible::value, ""); struct TCopy { constexpr TCopy(int v) : value(v) {} - TCopy(TCopy const &) = default; - TCopy(TCopy &&) = delete; + TCopy(TCopy const&) = default; + TCopy(TCopy&&) = delete; int value; }; @@ -2971,20 +3438,21 @@ static_assert(std::is_trivially_copy_constructible::value, ""); struct MakeEmptyT { static int alive; MakeEmptyT() { ++alive; } - MakeEmptyT(const MakeEmptyT &) { + MakeEmptyT(const MakeEmptyT&) { ++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=(const MakeEmptyT &) { throw 42; } - MakeEmptyT &operator=(MakeEmptyT &&) { throw 42; } + MakeEmptyT(MakeEmptyT&&) { throw 42; } + MakeEmptyT& operator=(const MakeEmptyT&) { throw 42; } + MakeEmptyT& operator=(MakeEmptyT&&) { throw 42; } ~MakeEmptyT() { --alive; } }; int MakeEmptyT::alive = 0; -template void makeEmpty(Variant &v) { +template +void makeEmpty(Variant& v) { Variant v2(std::in_place_type); try { v = std::move(v2); @@ -2995,7 +3463,7 @@ template void makeEmpty(Variant &v) { } #endif // TEST_HAS_NO_EXCEPTIONS -void test_copy_ctor_sfinae() { +constexpr void test_copy_ctor_sfinae() { { using V = std::variant; static_assert(std::is_copy_constructible::value, ""); @@ -3014,7 +3482,6 @@ void test_copy_ctor_sfinae() { } // Make sure we properly propagate triviality (see P0602R4). -#if TEST_STD_VER > 17 { using V = std::variant; static_assert(std::is_trivially_copy_constructible::value, ""); @@ -3032,10 +3499,9 @@ void test_copy_ctor_sfinae() { using V = std::variant; static_assert(std::is_trivially_copy_constructible::value, ""); } -#endif // > C++17 } -void test_copy_ctor_basic() { +TEST_CONSTEXPR_CXX20 void test_copy_ctor_basic() { { std::variant v(std::in_place_index<0>, 42); std::variant v2 = v; @@ -3064,7 +3530,6 @@ void test_copy_ctor_basic() { } // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). -#if TEST_STD_VER > 17 { constexpr std::variant v(std::in_place_index<0>, 42); static_assert(v.index() == 0, ""); @@ -3107,7 +3572,6 @@ void test_copy_ctor_basic() { static_assert(v2.index() == 1, ""); static_assert(std::get<1>(v2).value == 42, ""); } -#endif // > C++17 } void test_copy_ctor_valueless_by_exception() { @@ -3115,23 +3579,22 @@ void test_copy_ctor_valueless_by_exception() { using V = std::variant; V v1; makeEmpty(v1); - const V &cv1 = v1; + const V& cv1 = v1; V v(cv1); assert(v.valueless_by_exception()); #endif // TEST_HAS_NO_EXCEPTIONS } -template -constexpr bool test_constexpr_copy_ctor_imp(std::variant const& v) { +template +constexpr void test_constexpr_copy_ctor_imp(const T& v) { auto v2 = v; - return v2.index() == v.index() && - v2.index() == Idx && - std::get(v2) == std::get(v); + assert(v2.index() == v.index()); + assert(v2.index() == Idx); + assert(std::get(v2) == std::get(v)); } -void test_constexpr_copy_ctor() { +constexpr void test_constexpr_copy_ctor_trivial() { // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). -#if TEST_STD_VER > 17 using V = std::variant; #ifdef TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE static_assert(std::is_trivially_destructible::value, ""); @@ -3139,30 +3602,58 @@ void test_constexpr_copy_ctor() { static_assert(std::is_trivially_move_constructible::value, ""); static_assert(!std::is_copy_assignable::value, ""); static_assert(!std::is_move_assignable::value, ""); -#else // TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE +#else // TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE static_assert(std::is_trivially_copyable::value, ""); #endif // TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE - static_assert(test_constexpr_copy_ctor_imp<0>(V(42l)), ""); - static_assert(test_constexpr_copy_ctor_imp<1>(V(nullptr)), ""); - static_assert(test_constexpr_copy_ctor_imp<2>(V(101)), ""); -#endif // > C++17 + static_assert(std::is_trivially_copy_constructible::value, ""); + test_constexpr_copy_ctor_imp<0>(V(42l)); + test_constexpr_copy_ctor_imp<1>(V(nullptr)); + test_constexpr_copy_ctor_imp<2>(V(101)); +} + +struct NonTrivialCopyCtor { + int i = 0; + constexpr NonTrivialCopyCtor(int ii) : i(ii) {} + constexpr NonTrivialCopyCtor(const NonTrivialCopyCtor& other) : i(other.i) {} + constexpr NonTrivialCopyCtor(NonTrivialCopyCtor&& other) = default; + TEST_CONSTEXPR_CXX20 ~NonTrivialCopyCtor() = default; + friend constexpr bool operator==(const NonTrivialCopyCtor& x, const NonTrivialCopyCtor& y) { return x.i == y.i; } +}; + +TEST_CONSTEXPR_CXX20 void test_constexpr_copy_ctor_non_trivial() { + // Test !is_trivially_move_constructible + using V = std::variant; + static_assert(!std::is_trivially_copy_constructible::value, ""); + test_constexpr_copy_ctor_imp<0>(V(42l)); + test_constexpr_copy_ctor_imp<1>(V(NonTrivialCopyCtor(5))); + test_constexpr_copy_ctor_imp<2>(V(nullptr)); +} + +void non_constexpr_test() { test_copy_ctor_valueless_by_exception(); } + +constexpr bool cxx17_constexpr_test() { + test_copy_ctor_sfinae(); + test_constexpr_copy_ctor_trivial(); + + return true; +} + +TEST_CONSTEXPR_CXX20 bool cxx20_constexpr_test() { + test_copy_ctor_basic(); + test_constexpr_copy_ctor_non_trivial(); + + return true; } int run_test() { - test_copy_ctor_basic(); - test_copy_ctor_valueless_by_exception(); - test_copy_ctor_sfinae(); - test_constexpr_copy_ctor(); -#if 0 -// disable this for the moment; it fails on older compilers. -// Need to figure out which compilers will support it. -{ // This is the motivating example from P0739R0 - std::variant v1(3); - std::variant v2 = v1; - (void) v2; -} -#endif + non_constexpr_test(); + cxx17_constexpr_test(); + cxx20_constexpr_test(); + static_assert(cxx17_constexpr_test()); +#if TEST_STD_VER >= 20 + static_assert(cxx20_constexpr_test()); +#endif return 0; } } // namespace ctor::copy @@ -3178,7 +3669,6 @@ int run_test() { //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_variant_access && !libcpp-no-exceptions // @@ -3189,6 +3679,7 @@ int run_test() { #include #include #include +#include #include "test_macros.h" #include "variant_test_helpers.h" @@ -3208,7 +3699,7 @@ struct DefaultCtorThrows { }; #endif -void test_default_ctor_sfinae() { +constexpr void test_default_ctor_sfinae() { { using V = std::variant; static_assert(std::is_default_constructible::value, ""); @@ -3219,7 +3710,7 @@ void test_default_ctor_sfinae() { } } -void test_default_ctor_noexcept() { +constexpr void test_default_ctor_noexcept() { { using V = std::variant; static_assert(std::is_nothrow_default_constructible::value, ""); @@ -3236,7 +3727,7 @@ void test_default_ctor_throws() { try { V v; assert(false); - } catch (const int &ex) { + } catch (const int& ex) { assert(ex == 42); } catch (...) { assert(false); @@ -3244,7 +3735,7 @@ void test_default_ctor_throws() { #endif } -void test_default_ctor_basic() { +constexpr void test_default_ctor_basic() { { std::variant v; assert(v.index() == 0); @@ -3280,12 +3771,25 @@ void test_default_ctor_basic() { } } -int run_test() { +constexpr void issue_86686() { +#if TEST_STD_VER >= 20 + static_assert(std::variant{}.index() == 0); +#endif +} + +constexpr bool test() { test_default_ctor_basic(); test_default_ctor_sfinae(); test_default_ctor_noexcept(); - test_default_ctor_throws(); + issue_86686(); + return true; +} + +int run_test() { + test(); + test_default_ctor_throws(); + static_assert(test()); return 0; } } // namespace ctor::default_ @@ -3301,7 +3805,6 @@ int run_test() { //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_variant_access && !libcpp-no-exceptions // @@ -3409,7 +3912,7 @@ int run_test() { // //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_variant_access && !libcpp-no-exceptions + // @@ -3524,7 +4027,6 @@ int run_test() { //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_variant_access && !libcpp-no-exceptions // @@ -3642,7 +4144,7 @@ int run_test() { // //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_variant_access && !libcpp-no-exceptions + // @@ -3758,13 +4260,12 @@ int run_test() { //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_variant_access && !libcpp-no-exceptions // // template class variant; -// variant(variant&&) noexcept(see below); // constexpr in C++20 +// constexpr variant(variant&&) noexcept(see below); #include #include @@ -3776,31 +4277,31 @@ int run_test() { namespace ctor::move { struct ThrowsMove { - ThrowsMove(ThrowsMove &&) noexcept(false) {} + ThrowsMove(ThrowsMove&&) noexcept(false) {} }; struct NoCopy { - NoCopy(const NoCopy &) = delete; + NoCopy(const NoCopy&) = delete; }; struct MoveOnly { int value; - MoveOnly(int v) : value(v) {} - MoveOnly(const MoveOnly &) = delete; - MoveOnly(MoveOnly &&) = default; + constexpr MoveOnly(int v) : value(v) {} + MoveOnly(const MoveOnly&) = delete; + MoveOnly(MoveOnly&&) = default; }; struct MoveOnlyNT { int value; - MoveOnlyNT(int v) : value(v) {} - MoveOnlyNT(const MoveOnlyNT &) = delete; - MoveOnlyNT(MoveOnlyNT &&other) : value(other.value) { other.value = -1; } + constexpr MoveOnlyNT(int v) : value(v) {} + MoveOnlyNT(const MoveOnlyNT&) = delete; + constexpr MoveOnlyNT(MoveOnlyNT&& other) : value(other.value) { other.value = -1; } }; struct NTMove { constexpr NTMove(int v) : value(v) {} - NTMove(const NTMove &) = delete; - NTMove(NTMove &&that) : value(that.value) { that.value = -1; } + NTMove(const NTMove&) = delete; + NTMove(NTMove&& that) : value(that.value) { that.value = -1; } int value; }; @@ -3809,8 +4310,8 @@ static_assert(std::is_move_constructible::value, ""); struct TMove { constexpr TMove(int v) : value(v) {} - TMove(const TMove &) = delete; - TMove(TMove &&) = default; + TMove(const TMove&) = delete; + TMove(TMove&&) = default; int value; }; @@ -3829,20 +4330,21 @@ static_assert(std::is_trivially_move_constructible::value, ""); struct MakeEmptyT { static int alive; MakeEmptyT() { ++alive; } - MakeEmptyT(const MakeEmptyT &) { + MakeEmptyT(const MakeEmptyT&) { ++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=(const MakeEmptyT &) { throw 42; } - MakeEmptyT &operator=(MakeEmptyT &&) { throw 42; } + MakeEmptyT(MakeEmptyT&&) { throw 42; } + MakeEmptyT& operator=(const MakeEmptyT&) { throw 42; } + MakeEmptyT& operator=(MakeEmptyT&&) { throw 42; } ~MakeEmptyT() { --alive; } }; int MakeEmptyT::alive = 0; -template void makeEmpty(Variant &v) { +template +void makeEmpty(Variant& v) { Variant v2(std::in_place_type); try { v = std::move(v2); @@ -3853,7 +4355,7 @@ template void makeEmpty(Variant &v) { } #endif // TEST_HAS_NO_EXCEPTIONS -void test_move_noexcept() { +constexpr void test_move_noexcept() { { using V = std::variant; static_assert(std::is_nothrow_move_constructible::value, ""); @@ -3872,7 +4374,7 @@ void test_move_noexcept() { } } -void test_move_ctor_sfinae() { +constexpr void test_move_ctor_sfinae() { { using V = std::variant; static_assert(std::is_move_constructible::value, ""); @@ -3891,7 +4393,6 @@ void test_move_ctor_sfinae() { } // Make sure we properly propagate triviality (see P0602R4). -#if TEST_STD_VER > 17 { using V = std::variant; static_assert(std::is_trivially_move_constructible::value, ""); @@ -3909,13 +4410,15 @@ void test_move_ctor_sfinae() { using V = std::variant; static_assert(std::is_trivially_move_constructible::value, ""); } -#endif // > C++17 } template -struct Result { size_t index; T value; }; +struct Result { + std::size_t index; + T value; +}; -void test_move_ctor_basic() { +TEST_CONSTEXPR_CXX20 void test_move_ctor_basic() { { std::variant v(std::in_place_index<0>, 42); std::variant v2 = std::move(v); @@ -3960,7 +4463,6 @@ void test_move_ctor_basic() { } // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). -#if TEST_STD_VER > 17 { struct { constexpr Result operator()() const { @@ -4033,7 +4535,6 @@ void test_move_ctor_basic() { static_assert(result.index == 1, ""); static_assert(result.value.value == 42, ""); } -#endif // > C++17 } void test_move_ctor_valueless_by_exception() { @@ -4046,18 +4547,17 @@ void test_move_ctor_valueless_by_exception() { #endif // TEST_HAS_NO_EXCEPTIONS } -template -constexpr bool test_constexpr_ctor_imp(std::variant const& v) { +template +constexpr void test_constexpr_ctor_imp(const T& v) { auto copy = v; - auto v2 = std::move(copy); - return v2.index() == v.index() && - v2.index() == Idx && - std::get(v2) == std::get(v); + auto v2 = std::move(copy); + assert(v2.index() == v.index()); + assert(v2.index() == Idx); + assert(std::get(v2) == std::get(v)); } -void test_constexpr_move_ctor() { +constexpr void test_constexpr_move_ctor_trivial() { // Make sure we properly propagate triviality, which implies constexpr-ness (see P0602R4). -#if TEST_STD_VER > 17 using V = std::variant; #ifdef TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE static_assert(std::is_trivially_destructible::value, ""); @@ -4065,22 +4565,58 @@ void test_constexpr_move_ctor() { static_assert(std::is_trivially_move_constructible::value, ""); static_assert(!std::is_copy_assignable::value, ""); static_assert(!std::is_move_assignable::value, ""); -#else // TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE +#else // TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE static_assert(std::is_trivially_copyable::value, ""); #endif // TEST_WORKAROUND_MSVC_BROKEN_IS_TRIVIALLY_COPYABLE static_assert(std::is_trivially_move_constructible::value, ""); - static_assert(test_constexpr_ctor_imp<0>(V(42l)), ""); - static_assert(test_constexpr_ctor_imp<1>(V(nullptr)), ""); - static_assert(test_constexpr_ctor_imp<2>(V(101)), ""); -#endif // > C++17 + test_constexpr_ctor_imp<0>(V(42l)); + test_constexpr_ctor_imp<1>(V(nullptr)); + test_constexpr_ctor_imp<2>(V(101)); +} + +struct NonTrivialMoveCtor { + int i = 0; + constexpr NonTrivialMoveCtor(int ii) : i(ii) {} + constexpr NonTrivialMoveCtor(const NonTrivialMoveCtor& other) = default; + constexpr NonTrivialMoveCtor(NonTrivialMoveCtor&& other) : i(other.i) {} + TEST_CONSTEXPR_CXX20 ~NonTrivialMoveCtor() = default; + friend constexpr bool operator==(const NonTrivialMoveCtor& x, const NonTrivialMoveCtor& y) { return x.i == y.i; } +}; + +TEST_CONSTEXPR_CXX20 void test_constexpr_move_ctor_non_trivial() { + using V = std::variant; + static_assert(!std::is_trivially_move_constructible::value, ""); + test_constexpr_ctor_imp<0>(V(42l)); + test_constexpr_ctor_imp<1>(V(NonTrivialMoveCtor(5))); + test_constexpr_ctor_imp<2>(V(nullptr)); +} + +void non_constexpr_test() { test_move_ctor_valueless_by_exception(); } + +constexpr bool cxx17_constexpr_test() { + test_move_noexcept(); + test_move_ctor_sfinae(); + test_constexpr_move_ctor_trivial(); + + return true; +} + +TEST_CONSTEXPR_CXX20 bool cxx20_constexpr_test() { + test_move_ctor_basic(); + test_constexpr_move_ctor_non_trivial(); + + return true; } int run_test() { - test_move_ctor_basic(); - test_move_ctor_valueless_by_exception(); - test_move_noexcept(); - test_move_ctor_sfinae(); - test_constexpr_move_ctor(); + non_constexpr_test(); + cxx17_constexpr_test(); + cxx20_constexpr_test(); + + static_assert(cxx17_constexpr_test()); +#if TEST_STD_VER >= 20 + static_assert(cxx20_constexpr_test()); +#endif return 0; } @@ -4096,7 +4632,7 @@ int run_test() { // //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_variant_access && !libcpp-no-exceptions + // @@ -4109,6 +4645,7 @@ int run_test() { #include #include #include +#include #include "test_macros.h" #include "variant_test_helpers.h" @@ -4126,10 +4663,17 @@ struct NoThrowT { NoThrowT(int) noexcept(true) {} }; -struct AnyConstructible { template AnyConstructible(T&&) {} }; -struct NoConstructible { NoConstructible() = delete; }; +struct AnyConstructible { + template + AnyConstructible(T&&) {} +}; +struct NoConstructible { + NoConstructible() = delete; +}; template -struct RValueConvertibleFrom { RValueConvertibleFrom(T&&) {} }; +struct RValueConvertibleFrom { + RValueConvertibleFrom(T&&) {} +}; void test_T_ctor_noexcept() { { @@ -4149,55 +4693,40 @@ void test_T_ctor_sfinae() { } { using V = std::variant; - static_assert(!std::is_constructible::value, "ambiguous"); + static_assert(!std::is_constructible::value, "ambiguous"); } { - using V = std::variant; - static_assert(!std::is_constructible::value, - "no matching constructor"); + using V = std::variant; + static_assert(!std::is_constructible::value, "no matching constructor"); } -#if _HAS_CXX20 { using V = std::variant; - static_assert(std::is_constructible::value == false, - "no matching constructor"); + static_assert(!std::is_constructible::value, "no matching constructor"); } { using V = std::variant, bool>; - static_assert(!std::is_constructible>::value, - "no explicit bool in constructor"); + static_assert(!std::is_constructible>::value, "no explicit bool in constructor"); struct X { operator void*(); }; #ifndef __EDG__ // TRANSITION, VSO-1327220 - static_assert(!std::is_constructible::value, - "no boolean conversion in constructor"); -#endif // !__EDG__ - // libc++ is missing P1957R2 - static_assert(std::is_constructible::value, - "no converted to bool in constructor"); + static_assert(!std::is_constructible::value, "no boolean conversion in constructor"); +#endif // ^^^ no workaround ^^^ + static_assert(std::is_constructible::value, "converted to bool in constructor"); } -#endif // _HAS_CXX20 { struct X {}; struct Y { operator X(); }; using V = std::variant; - static_assert(std::is_constructible::value, - "regression on user-defined conversions in constructor"); + static_assert(std::is_constructible::value, "regression on user-defined conversions in constructor"); } { using V = std::variant; - static_assert( - !std::is_constructible>::value, - "no matching constructor"); - static_assert(!std::is_constructible>::value, - "no matching constructor"); + static_assert(!std::is_constructible>::value, "no matching constructor"); + static_assert(!std::is_constructible>::value, "no matching constructor"); } - - - } void test_T_ctor_basic() { @@ -4211,29 +4740,28 @@ void test_T_ctor_basic() { static_assert(v.index() == 1, ""); static_assert(std::get<1>(v) == 42, ""); } -#if _HAS_CXX20 -#ifndef TEST_VARIANT_ALLOWS_NARROWING_CONVERSIONS { constexpr std::variant v(42); static_assert(v.index() == 1, ""); static_assert(std::get<1>(v) == 42, ""); } -#endif { - std::variant v = "meow"; + std::variant v = "foo"; assert(v.index() == 0); - assert(std::get<0>(v) == "meow"); + assert(std::get<0>(v) == "foo"); } -#ifndef TEST_PERMISSIVE { - std::variant> v = nullptr; - assert(v.index() == 1); - assert(std::get<1>(v) == nullptr); + std::variant> v = nullptr; + if constexpr (is_permissive) { + assert(v.index() == 0); + assert(std::get<0>(v) == false); + } else { + assert(v.index() == 1); + assert(std::get<1>(v) == nullptr); + } } -#endif // !TEST_PERMISSIVE -#endif // _HAS_CXX20 { - std::variant v = true; + std::variant v = true; assert(v.index() == 0); assert(std::get<0>(v)); } @@ -4241,25 +4769,27 @@ void test_T_ctor_basic() { std::variant> v1 = 42; assert(v1.index() == 0); - int x = 42; + int x = 42; std::variant, AnyConstructible> v2 = x; assert(v2.index() == 1); } } -#if !_HAS_CXX20 // Narrowing check occurs with P0608R3 -struct FailOnAnything { +struct BoomOnAnything { template - constexpr FailOnAnything(T) { static_assert(!std::is_same::value, ""); } + constexpr BoomOnAnything(T) { + static_assert(!std::is_same::value, ""); + } }; void test_no_narrowing_check_for_class_types() { - using V = std::variant; +#ifndef __clang__ // TRANSITION, not yet investigated + using V = std::variant; V v(42); assert(v.index() == 0); assert(std::get<0>(v) == 42); +#endif // ^^^ no workaround ^^^ } -#endif // Narrowing check occurs with P0608R3 struct Bar {}; struct Baz {}; @@ -4272,14 +4802,20 @@ void test_construction_with_repeated_types() { static_assert(std::is_constructible::value, ""); } +void test_vector_bool() { + std::vector vec = {true}; + std::variant v = vec[0]; + assert(v.index() == 0); + assert(std::get<0>(v) == true); +} + int run_test() { test_T_ctor_basic(); test_T_ctor_noexcept(); test_T_ctor_sfinae(); -#if !_HAS_CXX20 // Narrowing check occurs with P0608R3 test_no_narrowing_check_for_class_types(); -#endif // Narrowing check occurs with P0608R3 test_construction_with_repeated_types(); + test_vector_bool(); return 0; } } // namespace ctor::T @@ -4295,6 +4831,7 @@ int run_test() { //===----------------------------------------------------------------------===// + // // template class variant; @@ -4309,56 +4846,77 @@ int run_test() { namespace dtor { struct NonTDtor { - static int count; - NonTDtor() = default; - ~NonTDtor() { ++count; } + int* count; + constexpr NonTDtor(int* a, int*) : count(a) {} + TEST_CONSTEXPR_CXX20 ~NonTDtor() { ++*count; } }; -int NonTDtor::count = 0; static_assert(!std::is_trivially_destructible::value, ""); struct NonTDtor1 { - static int count; - NonTDtor1() = default; - ~NonTDtor1() { ++count; } + int* count; + constexpr NonTDtor1(int*, int* b) : count(b) {} + TEST_CONSTEXPR_CXX20 ~NonTDtor1() { ++*count; } }; -int NonTDtor1::count = 0; static_assert(!std::is_trivially_destructible::value, ""); struct TDtor { - TDtor(const TDtor &) {} // non-trivial copy - ~TDtor() = default; + constexpr TDtor() = default; + constexpr TDtor(const TDtor&) {} // non-trivial copy + TEST_CONSTEXPR_CXX20 ~TDtor() = default; }; static_assert(!std::is_trivially_copy_constructible::value, ""); static_assert(std::is_trivially_destructible::value, ""); -int run_test() { +TEST_CONSTEXPR_CXX20 bool test() { { using V = std::variant; static_assert(std::is_trivially_destructible::value, ""); + [[maybe_unused]] V v(std::in_place_index<2>); } { using V = std::variant; static_assert(!std::is_trivially_destructible::value, ""); { - V v(std::in_place_index<0>); - assert(NonTDtor::count == 0); - assert(NonTDtor1::count == 0); + int count0 = 0; + int count1 = 0; + { + V v(std::in_place_index<0>, &count0, &count1); + assert(count0 == 0); + assert(count1 == 0); + } + assert(count0 == 1); + assert(count1 == 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); + int count0 = 0; + int count1 = 0; + { V v(std::in_place_index<1>); } + assert(count0 == 0); + assert(count1 == 0); + } + { + int count0 = 0; + int count1 = 0; + { + V v(std::in_place_index<2>, &count0, &count1); + assert(count0 == 0); + assert(count1 == 0); + } + assert(count0 == 0); + assert(count1 == 1); } - assert(NonTDtor::count == 0); - assert(NonTDtor1::count == 1); } + return true; +} + +int run_test() { + test(); + +#if TEST_STD_VER >= 20 + static_assert(test()); +#endif + return 0; } } // namespace dtor @@ -4374,7 +4932,6 @@ int run_test() { //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_variant_access && !libcpp-no-exceptions // @@ -4393,40 +4950,44 @@ int run_test() { #include "test_macros.h" #include "variant_test_helpers.h" -namespace emplace::index { -template -constexpr auto test_emplace_exists_imp(int) -> decltype( - std::declval().template emplace(std::declval()...), true) { +namespace emplace::index_args { +template +constexpr auto test_emplace_exists_imp(int) + -> decltype(std::declval().template emplace(std::declval()...), true) { return true; } -template +template constexpr auto test_emplace_exists_imp(long) -> bool { return false; } -template constexpr bool emplace_exists() { +template +constexpr bool emplace_exists() { return test_emplace_exists_imp(0); } -void test_emplace_sfinae() { +constexpr void test_emplace_sfinae() { { - using V = std::variant; + using V = std::variant; static_assert(emplace_exists(), ""); static_assert(emplace_exists(), ""); - static_assert(!emplace_exists(), - "cannot construct"); + static_assert(!emplace_exists(), "cannot construct"); static_assert(emplace_exists(), ""); - static_assert(emplace_exists(), ""); - static_assert(!emplace_exists(), ""); + static_assert(emplace_exists(), ""); + static_assert(!emplace_exists(), ""); static_assert(!emplace_exists(), "cannot construct"); - static_assert(emplace_exists(), ""); - static_assert(emplace_exists(), ""); + static_assert(emplace_exists(), ""); + static_assert(emplace_exists(), ""); static_assert(!emplace_exists(), "cannot construct"); } } -void test_basic() { +struct NoCtor { + NoCtor() = delete; +}; + +TEST_CONSTEXPR_CXX20 void test_basic() { { using V = std::variant; V v(42); @@ -4439,9 +5000,9 @@ void test_basic() { assert(std::get<0>(v) == 42); assert(&ref2 == &std::get<0>(v)); } + { - using V = - std::variant; + using V = std::variant; const int x = 100; V v(std::in_place_index<0>, -1); // default emplace a value @@ -4461,13 +5022,23 @@ void test_basic() { } } -int run_test() { +TEST_CONSTEXPR_CXX20 bool test() { test_basic(); test_emplace_sfinae(); + return true; +} + +int run_test() { + test(); + +#if TEST_STD_VER >= 20 + static_assert(test()); +#endif + return 0; } -} // namespace emplace::index +} // namespace emplace::index_args // -- END: test/std/utilities/variant/variant.variant/variant.mod/emplace_index_args.pass.cpp // -- BEGIN: test/std/utilities/variant/variant.variant/variant.mod/emplace_index_init_list_args.pass.cpp @@ -4480,7 +5051,6 @@ int run_test() { //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_variant_access && !libcpp-no-exceptions // @@ -4507,28 +5077,27 @@ struct InitList { struct InitListArg { std::size_t size; int value; - constexpr InitListArg(std::initializer_list il, int v) - : size(il.size()), value(v) {} + constexpr InitListArg(std::initializer_list il, int v) : size(il.size()), value(v) {} }; -template -constexpr auto test_emplace_exists_imp(int) -> decltype( - std::declval().template emplace(std::declval()...), true) { +template +constexpr auto test_emplace_exists_imp(int) + -> decltype(std::declval().template emplace(std::declval()...), true) { return true; } -template +template constexpr auto test_emplace_exists_imp(long) -> bool { return false; } -template constexpr bool emplace_exists() { +template +constexpr bool emplace_exists() { return test_emplace_exists_imp(0); } -void test_emplace_sfinae() { - using V = - std::variant; +constexpr void test_emplace_sfinae() { + using V = std::variant; using IL = std::initializer_list; static_assert(!emplace_exists(), "no such constructor"); static_assert(emplace_exists(), ""); @@ -4540,8 +5109,12 @@ void test_emplace_sfinae() { static_assert(!emplace_exists(), "too many args"); } -void test_basic() { - using V = std::variant; +struct NoCtor { + NoCtor() = delete; +}; + +TEST_CONSTEXPR_CXX20 void test_basic() { + using V = std::variant; V v; auto& ref1 = v.emplace<1>({1, 2, 3}); static_assert(std::is_same_v, ""); @@ -4558,10 +5131,20 @@ void test_basic() { assert(&ref3 == &std::get<1>(v)); } -int run_test() { +TEST_CONSTEXPR_CXX20 bool test() { test_basic(); test_emplace_sfinae(); + return true; +} + +int run_test() { + test(); + +#if TEST_STD_VER >= 20 + static_assert(test()); +#endif + return 0; } } // namespace emplace::index_init_list_args @@ -4577,7 +5160,6 @@ int run_test() { //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_variant_access && !libcpp-no-exceptions // @@ -4597,8 +5179,8 @@ int run_test() { namespace emplace::type_args { template -constexpr auto test_emplace_exists_imp(int) -> decltype( - std::declval().template emplace(std::declval()...), true) { +constexpr auto test_emplace_exists_imp(int) + -> decltype(std::declval().template emplace(std::declval()...), true) { return true; } @@ -4607,28 +5189,32 @@ constexpr auto test_emplace_exists_imp(long) -> bool { return false; } -template constexpr bool emplace_exists() { +template +constexpr bool emplace_exists() { return test_emplace_exists_imp(0); } -void test_emplace_sfinae() { +constexpr void test_emplace_sfinae() { { - using V = std::variant; + using V = std::variant; static_assert(emplace_exists(), ""); static_assert(emplace_exists(), ""); - static_assert(!emplace_exists(), - "cannot construct"); - static_assert(emplace_exists(), ""); - static_assert(!emplace_exists(), "cannot construct"); - static_assert(emplace_exists(), ""); - static_assert(!emplace_exists(), ""); - static_assert(emplace_exists(), ""); - static_assert(emplace_exists(), ""); + static_assert(!emplace_exists(), "cannot construct"); + static_assert(emplace_exists(), ""); + static_assert(!emplace_exists(), "cannot construct"); + static_assert(emplace_exists(), ""); + static_assert(!emplace_exists(), ""); + static_assert(emplace_exists(), ""); + static_assert(emplace_exists(), ""); static_assert(!emplace_exists(), "cannot construct"); } } -void test_basic() { +struct NoCtor { + NoCtor() = delete; +}; + +TEST_CONSTEXPR_CXX20 void test_basic() { { using V = std::variant; V v(42); @@ -4642,8 +5228,7 @@ void test_basic() { assert(&ref2 == &std::get<0>(v)); } { - using V = - std::variant; + using V = std::variant; const int x = 100; V v(std::in_place_type, -1); // default emplace a value @@ -4651,8 +5236,8 @@ void test_basic() { static_assert(std::is_same_v, ""); assert(std::get<1>(v) == 0); assert(&ref1 == &std::get<1>(v)); - auto& ref2 = v.emplace(&x); - static_assert(std::is_same_v, ""); + auto& ref2 = v.emplace(&x); + static_assert(std::is_same_v, ""); assert(std::get<2>(v) == &x); assert(&ref2 == &std::get<2>(v)); // emplace with multiple args @@ -4663,10 +5248,20 @@ void test_basic() { } } -int run_test() { +TEST_CONSTEXPR_CXX20 bool test() { test_basic(); test_emplace_sfinae(); + return true; +} + +int run_test() { + test(); + +#if TEST_STD_VER >= 20 + static_assert(test()); +#endif + return 0; } } // namespace emplace::type_args @@ -4682,7 +5277,6 @@ int run_test() { //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_variant_access && !libcpp-no-exceptions // @@ -4709,13 +5303,12 @@ struct InitList { struct InitListArg { std::size_t size; int value; - constexpr InitListArg(std::initializer_list il, int v) - : size(il.size()), value(v) {} + constexpr InitListArg(std::initializer_list il, int v) : size(il.size()), value(v) {} }; template -constexpr auto test_emplace_exists_imp(int) -> decltype( - std::declval().template emplace(std::declval()...), true) { +constexpr auto test_emplace_exists_imp(int) + -> decltype(std::declval().template emplace(std::declval()...), true) { return true; } @@ -4724,13 +5317,13 @@ constexpr auto test_emplace_exists_imp(long) -> bool { return false; } -template constexpr bool emplace_exists() { +template +constexpr bool emplace_exists() { return test_emplace_exists_imp(0); } -void test_emplace_sfinae() { - using V = - std::variant; +constexpr void test_emplace_sfinae() { + using V = std::variant; using IL = std::initializer_list; static_assert(emplace_exists(), ""); static_assert(!emplace_exists(), "args don't match"); @@ -4738,32 +5331,45 @@ void test_emplace_sfinae() { static_assert(emplace_exists(), ""); static_assert(!emplace_exists(), "args don't match"); static_assert(!emplace_exists(), "too few args"); - static_assert(!emplace_exists(), - "too many args"); + static_assert(!emplace_exists(), "too many args"); } -void test_basic() { - using V = std::variant; +struct NoCtor { + NoCtor() = delete; +}; + +TEST_CONSTEXPR_CXX20 void test_basic() { + using V = std::variant; V v; auto& ref1 = v.emplace({1, 2, 3}); - static_assert(std::is_same_v, ""); + static_assert(std::is_same_v, ""); assert(std::get(v).size == 3); assert(&ref1 == &std::get(v)); auto& ref2 = v.emplace({1, 2, 3, 4}, 42); - static_assert(std::is_same_v, ""); + static_assert(std::is_same_v, ""); assert(std::get(v).size == 4); assert(std::get(v).value == 42); assert(&ref2 == &std::get(v)); auto& ref3 = v.emplace({1}); - static_assert(std::is_same_v, ""); + static_assert(std::is_same_v, ""); assert(std::get(v).size == 1); assert(&ref3 == &std::get(v)); } -int run_test() { +TEST_CONSTEXPR_CXX20 bool test() { test_basic(); test_emplace_sfinae(); + return true; +} + +int run_test() { + test(); + +#if TEST_STD_VER >= 20 + static_assert(test()); +#endif + return 0; } } // namespace emplace::type_init_list_args @@ -4779,6 +5385,7 @@ int run_test() { //===----------------------------------------------------------------------===// + // // template class variant; @@ -4844,6 +5451,7 @@ int run_test() { //===----------------------------------------------------------------------===// + // // template class variant; @@ -4902,7 +5510,6 @@ int run_test() { //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_variant_access && !libcpp-no-exceptions // @@ -4911,6 +5518,7 @@ int run_test() { // void swap(variant& rhs) noexcept(see below) #include +#include #include #include #include @@ -4921,37 +5529,39 @@ int run_test() { namespace member_swap { struct NotSwappable {}; -void swap(NotSwappable &, NotSwappable &) = delete; +void swap(NotSwappable&, NotSwappable&) = delete; struct NotCopyable { - NotCopyable() = default; - NotCopyable(const NotCopyable &) = delete; - NotCopyable &operator=(const NotCopyable &) = delete; + NotCopyable() = default; + NotCopyable(const NotCopyable&) = delete; + NotCopyable& operator=(const NotCopyable&) = delete; }; struct NotCopyableWithSwap { - NotCopyableWithSwap() = default; - NotCopyableWithSwap(const NotCopyableWithSwap &) = delete; - NotCopyableWithSwap &operator=(const NotCopyableWithSwap &) = delete; + NotCopyableWithSwap() = default; + NotCopyableWithSwap(const NotCopyableWithSwap&) = delete; + NotCopyableWithSwap& operator=(const NotCopyableWithSwap&) = delete; }; -void swap(NotCopyableWithSwap &, NotCopyableWithSwap) {} +constexpr void swap(NotCopyableWithSwap&, NotCopyableWithSwap) {} struct NotMoveAssignable { - NotMoveAssignable() = default; - NotMoveAssignable(NotMoveAssignable &&) = default; - NotMoveAssignable &operator=(NotMoveAssignable &&) = delete; + NotMoveAssignable() = default; + NotMoveAssignable(NotMoveAssignable&&) = default; + NotMoveAssignable& operator=(NotMoveAssignable&&) = delete; }; struct NotMoveAssignableWithSwap { - NotMoveAssignableWithSwap() = default; - NotMoveAssignableWithSwap(NotMoveAssignableWithSwap &&) = default; - NotMoveAssignableWithSwap &operator=(NotMoveAssignableWithSwap &&) = delete; + NotMoveAssignableWithSwap() = default; + NotMoveAssignableWithSwap(NotMoveAssignableWithSwap&&) = default; + NotMoveAssignableWithSwap& operator=(NotMoveAssignableWithSwap&&) = delete; }; -void swap(NotMoveAssignableWithSwap &, NotMoveAssignableWithSwap &) noexcept {} +constexpr void swap(NotMoveAssignableWithSwap&, NotMoveAssignableWithSwap&) noexcept {} -template void do_throw() {} +template +constexpr void do_throw() {} -template <> void do_throw() { +template <> +void do_throw() { #ifndef TEST_HAS_NO_EXCEPTIONS throw 42; #else @@ -4959,60 +5569,49 @@ template <> void do_throw() { #endif } -template +template 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(const NothrowTypeImp &o) noexcept(NT_Copy) : value(o.value) { - assert(false); - } // never called by test - NothrowTypeImp(NothrowTypeImp &&o) noexcept(NT_Move) : value(o.value) { - ++move_called; + int value; + int* move_called; + int* move_assign_called; + int* swap_called; + + constexpr NothrowTypeImp(int v, int* mv_ctr, int* mv_assign, int* swap) + : value(v), move_called(mv_ctr), move_assign_called(mv_assign), swap_called(swap) {} + + NothrowTypeImp(const NothrowTypeImp& o) noexcept(NT_Copy) : value(o.value) { assert(false); } // never called by test + + constexpr NothrowTypeImp(NothrowTypeImp&& o) noexcept(NT_Move) + : value(o.value), + move_called(o.move_called), + move_assign_called(o.move_assign_called), + swap_called(o.swap_called) { + ++*move_called; do_throw(); o.value = -1; } - NothrowTypeImp &operator=(const NothrowTypeImp &) noexcept(NT_CopyAssign) { + + NothrowTypeImp& operator=(const NothrowTypeImp&) noexcept(NT_CopyAssign) { assert(false); return *this; } // never called by the tests - NothrowTypeImp &operator=(NothrowTypeImp &&o) noexcept(NT_MoveAssign) { - ++move_assign_called; + + constexpr NothrowTypeImp& operator=(NothrowTypeImp&& o) noexcept(NT_MoveAssign) { + ++*move_assign_called; do_throw(); - value = o.value; + value = o.value; o.value = -1; return *this; } - int value; }; -template -int NothrowTypeImp::move_called = 0; -template -int NothrowTypeImp::move_assign_called = 0; -template -int NothrowTypeImp::swap_called = 0; -template -void swap(NothrowTypeImp &lhs, - NothrowTypeImp &rhs) noexcept(NT_Swap) { - lhs.swap_called++; +template +constexpr void +swap(NothrowTypeImp& lhs, + NothrowTypeImp& rhs) noexcept(NT_Swap) { + ++*lhs.swap_called; do_throw(); - int tmp = lhs.value; - lhs.value = rhs.value; - rhs.value = tmp; + std::swap(lhs.value, rhs.value); } // throwing copy, nothrow move ctor/assign, no swap provided @@ -5020,53 +5619,42 @@ using NothrowMoveable = NothrowTypeImp; // throwing copy and move assign, nothrow move ctor, no swap provided using NothrowMoveCtor = NothrowTypeImp; // nothrow move ctor, throwing move assignment, swap provided -using NothrowMoveCtorWithThrowingSwap = - NothrowTypeImp; +using NothrowMoveCtorWithThrowingSwap = NothrowTypeImp; // throwing move ctor, nothrow move assignment, no swap provided -using ThrowingMoveCtor = - NothrowTypeImp; +using ThrowingMoveCtor = NothrowTypeImp; // throwing special members, nothrowing swap -using ThrowingTypeWithNothrowSwap = - NothrowTypeImp; -using NothrowTypeWithThrowingSwap = - NothrowTypeImp; +using ThrowingTypeWithNothrowSwap = NothrowTypeImp; +using NothrowTypeWithThrowingSwap = NothrowTypeImp; // throwing move assign with nothrow move and nothrow swap -using ThrowingMoveAssignNothrowMoveCtorWithSwap = - NothrowTypeImp; +using ThrowingMoveAssignNothrowMoveCtorWithSwap = NothrowTypeImp; // throwing move assign with nothrow move but no swap. -using ThrowingMoveAssignNothrowMoveCtor = - NothrowTypeImp; +using ThrowingMoveAssignNothrowMoveCtor = NothrowTypeImp; 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; + int value; + int* move_called; + constexpr NonThrowingNonNoexceptType(int v, int* mv_called) : value(v), move_called(mv_called) {} + constexpr NonThrowingNonNoexceptType(NonThrowingNonNoexceptType&& o) noexcept(false) + : value(o.value), move_called(o.move_called) { + ++*move_called; o.value = -1; } - NonThrowingNonNoexceptType & - operator=(NonThrowingNonNoexceptType &&) noexcept(false) { + 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) { + ThrowsOnSecondMove(ThrowsOnSecondMove&& o) noexcept(false) : value(o.value), move_count(o.move_count + 1) { if (move_count == 2) do_throw(); o.value = -1; } - ThrowsOnSecondMove &operator=(ThrowsOnSecondMove &&) { + ThrowsOnSecondMove& operator=(ThrowsOnSecondMove&&) { assert(false); // not called by test return *this; } @@ -5120,265 +5708,293 @@ void test_swap_valueless_by_exception() { #endif } -void test_swap_same_alternative() { +TEST_CONSTEXPR_CXX20 void test_swap_same_alternative() { { - using T = ThrowingTypeWithNothrowSwap; - using V = std::variant; - T::reset(); - V v1(std::in_place_index<0>, 42); - V v2(std::in_place_index<0>, 100); + using V = std::variant; + int move_called = 0; + int move_assign_called = 0; + int swap_called = 0; + V v1(std::in_place_index<0>, 42, &move_called, &move_assign_called, &swap_called); + V v2(std::in_place_index<0>, 100, &move_called, &move_assign_called, &swap_called); v1.swap(v2); - assert(T::swap_called == 1); + assert(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(swap_called == 2); assert(std::get<0>(v1).value == 42); assert(std::get<0>(v2).value == 100); + + assert(move_called == 0); + assert(move_assign_called == 0); } { - using T = NothrowMoveable; - using V = std::variant; - T::reset(); - V v1(std::in_place_index<0>, 42); - V v2(std::in_place_index<0>, 100); + using V = std::variant; + int move_called = 0; + int move_assign_called = 0; + int swap_called = 0; + V v1(std::in_place_index<0>, 42, &move_called, &move_assign_called, &swap_called); + V v2(std::in_place_index<0>, 100, &move_called, &move_assign_called, &swap_called); v1.swap(v2); - assert(T::swap_called == 0); - assert(T::move_called == 1); - assert(T::move_assign_called == 2); + assert(swap_called == 0); + assert(move_called == 1); + assert(move_assign_called == 2); assert(std::get<0>(v1).value == 100); assert(std::get<0>(v2).value == 42); - T::reset(); + + move_called = 0; + move_assign_called = 0; + swap_called = 0; + swap(v1, v2); - assert(T::swap_called == 0); - assert(T::move_called == 1); - assert(T::move_assign_called == 2); + assert(swap_called == 0); + assert(move_called == 1); + assert(move_assign_called == 2); assert(std::get<0>(v1).value == 42); assert(std::get<0>(v2).value == 100); } +} + +void test_swap_same_alternative_throws(){ #ifndef TEST_HAS_NO_EXCEPTIONS - { - using T = NothrowTypeWithThrowingSwap; - using V = std::variant; - 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 V = std::variant; +int move_called = 0; +int move_assign_called = 0; +int swap_called = 0; +V v1(std::in_place_index<0>, 42, &move_called, &move_assign_called, &swap_called); +V v2(std::in_place_index<0>, 100, &move_called, &move_assign_called, &swap_called); +try { + v1.swap(v2); + assert(false); +} catch (int) { +} +assert(swap_called == 1); +assert(move_called == 0); +assert(move_assign_called == 0); +assert(std::get<0>(v1).value == 42); +assert(std::get<0>(v2).value == 100); +} + +{ + using V = std::variant; + int move_called = 0; + int move_assign_called = 0; + int swap_called = 0; + V v1(std::in_place_index<0>, 42, &move_called, &move_assign_called, &swap_called); + V v2(std::in_place_index<0>, 100, &move_called, &move_assign_called, &swap_called); + try { + v1.swap(v2); + assert(false); + } catch (int) { } - { - using T = ThrowingMoveCtor; - using V = std::variant; - 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::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); + assert(move_called == 1); // call threw + assert(move_assign_called == 0); + assert(swap_called == 0); + assert(std::get<0>(v1).value == 42); // throw happened before v1 was moved from + assert(std::get<0>(v2).value == 100); +} +{ + using V = std::variant; + int move_called = 0; + int move_assign_called = 0; + int swap_called = 0; + V v1(std::in_place_index<0>, 42, &move_called, &move_assign_called, &swap_called); + V v2(std::in_place_index<0>, 100, &move_called, &move_assign_called, &swap_called); + try { + v1.swap(v2); + assert(false); + } catch (int) { } + assert(move_called == 1); + assert(move_assign_called == 1); // call threw and didn't complete + assert(swap_called == 0); + assert(std::get<0>(v1).value == -1); // v1 was moved from + assert(std::get<0>(v2).value == 100); +} #endif } -void test_swap_different_alternatives() { +TEST_CONSTEXPR_CXX20 void test_swap_different_alternatives() { { - using T = NothrowMoveCtorWithThrowingSwap; - using V = std::variant; - T::reset(); - V v1(std::in_place_index<0>, 42); + using V = std::variant; + int move_called = 0; + int move_assign_called = 0; + int swap_called = 0; + V v1(std::in_place_index<0>, 42, &move_called, &move_assign_called, &swap_called); V v2(std::in_place_index<1>, 100); v1.swap(v2); - assert(T::swap_called == 0); + assert(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); + LIBCPP_ASSERT(move_called == 1); + assert(move_called <= 2); + assert(move_assign_called == 0); assert(std::get<1>(v1) == 100); assert(std::get<0>(v2).value == 42); - T::reset(); + + move_called = 0; + move_assign_called = 0; + swap_called = 0; + 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(swap_called == 0); + LIBCPP_ASSERT(move_called == 2); + assert(move_called <= 2); + assert(move_assign_called == 0); assert(std::get<0>(v1).value == 42); assert(std::get<1>(v2) == 100); } +} + +void test_swap_different_alternatives_throws() { #ifndef TEST_HAS_NO_EXCEPTIONS { - using T1 = ThrowingTypeWithNothrowSwap; - using T2 = NonThrowingNonNoexceptType; - using V = std::variant; - T1::reset(); - T2::reset(); - V v1(std::in_place_index<0>, 42); - V v2(std::in_place_index<1>, 100); + using V = std::variant; + int move_called1 = 0; + int move_assign_called1 = 0; + int swap_called1 = 0; + int move_called2 = 0; + V v1(std::in_place_index<0>, 42, &move_called1, &move_assign_called1, &swap_called1); + V v2(std::in_place_index<1>, 100, &move_called2); 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); + assert(swap_called1 == 0); + assert(move_called1 == 1); // throws + assert(move_assign_called1 == 0); // TODO: libc++ shouldn't move from T2 here. - LIBCPP_ASSERT(T2::move_called == 1); - assert(T2::move_called <= 1); + LIBCPP_ASSERT(move_called2 == 1); + assert(move_called2 <= 1); assert(std::get<0>(v1).value == 42); - if (T2::move_called != 0) + if (move_called2 != 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::reset(); - T2::reset(); - V v1(std::in_place_index<0>, 42); - V v2(std::in_place_index<1>, 100); + using V = std::variant; + int move_called1 = 0; + int move_called2 = 0; + int move_assign_called2 = 0; + int swap_called2 = 0; + V v1(std::in_place_index<0>, 42, &move_called1); + V v2(std::in_place_index<1>, 100, &move_called2, &move_assign_called2, &swap_called2); 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) + LIBCPP_ASSERT(move_called1 == 0); + assert(move_called1 <= 1); + assert(swap_called2 == 0); + assert(move_called2 == 1); // throws + assert(move_assign_called2 == 0); + if (move_called1 != 0) assert(v1.valueless_by_exception()); else assert(std::get<0>(v1).value == 42); assert(std::get<1>(v2).value == 100); } // TODO: The tests below are just very libc++ specific -#ifdef _LIBCPP_VERSION +# ifdef _LIBCPP_VERSION { - using T1 = ThrowsOnSecondMove; - using T2 = NonThrowingNonNoexceptType; - using V = std::variant; - T2::reset(); + using V = std::variant; + int move_called = 0; V v1(std::in_place_index<0>, 42); - V v2(std::in_place_index<1>, 100); + V v2(std::in_place_index<1>, 100, &move_called); v1.swap(v2); - assert(T2::move_called == 2); + assert(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::reset(); - V v1(std::in_place_index<0>, 42); + using V = std::variant; + int move_called = 0; + V v1(std::in_place_index<0>, 42, &move_called); V v2(std::in_place_index<1>, 100); try { v1.swap(v2); assert(false); } catch (int) { } - assert(T1::move_called == 1); + assert(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 +# 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::reset(); - T2::reset(); - V v1(std::in_place_index<0>, 42); - V v2(std::in_place_index<1>, 100); + using V = std::variant; + int move_called1 = 0; + int move_assign_called1 = 0; + int swap_called1 = 0; + int move_called2 = 0; + int move_assign_called2 = 0; + int swap_called2 = 0; + V v1(std::in_place_index<0>, 42, &move_called1, &move_assign_called1, &swap_called1); + V v2(std::in_place_index<1>, 100, &move_called2, &move_assign_called2, &swap_called2); 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(swap_called1 == 0); + assert(move_called1 == 1); + assert(move_assign_called1 == 0); + assert(swap_called2 == 0); + assert(move_called2 == 2); + assert(move_assign_called2 == 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(); + + move_called1 = 0; + move_assign_called1 = 0; + swap_called1 = 0; + move_called2 = 0; + move_assign_called2 = 0; + swap_called2 = 0; + 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(swap_called1 == 0); + assert(move_called1 == 1); + assert(move_assign_called1 == 0); + assert(swap_called2 == 0); + assert(move_called2 == 2); + assert(move_assign_called2 == 0); assert(std::get<0>(v1).value == 42); assert(std::get<1>(v2).value == 100); } -#endif // _LIBCPP_VERSION +# endif // _LIBCPP_VERSION #endif } template -constexpr auto has_swap_member_imp(int) - -> decltype(std::declval().swap(std::declval()), true) { +constexpr auto has_swap_member_imp(int) -> decltype(std::declval().swap(std::declval()), true) { return true; } -template constexpr auto has_swap_member_imp(long) -> bool { +template +constexpr auto has_swap_member_imp(long) -> bool { return false; } -template constexpr bool has_swap_member() { +template +constexpr bool has_swap_member() { return has_swap_member_imp(0); } -void test_swap_sfinae() { +constexpr 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 @@ -5404,7 +6020,7 @@ void test_swap_sfinae() { } } -void test_swap_noexcept() { +TEST_CONSTEXPR_CXX20 void test_swap_noexcept() { { using V = std::variant; static_assert(std::is_swappable_v && has_swap_member(), ""); @@ -5477,13 +6093,29 @@ void test_swap_noexcept() { template class std::variant; #endif -int run_test() { +void non_constexpr_test() { test_swap_valueless_by_exception(); + test_swap_same_alternative_throws(); + test_swap_different_alternatives_throws(); +} + +TEST_CONSTEXPR_CXX20 bool test() { test_swap_same_alternative(); test_swap_different_alternatives(); test_swap_sfinae(); test_swap_noexcept(); + return true; +} + +int run_test() { + non_constexpr_test(); + test(); + +#if TEST_STD_VER >= 20 + static_assert(test()); +#endif + return 0; } } // namespace member_swap @@ -5499,7 +6131,6 @@ int run_test() { //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_variant_access && !libcpp-no-exceptions // // template @@ -5524,7 +6155,7 @@ constexpr bool test(bool do_it) std::visit([](auto){}, v); std::visit([](auto) -> Holder* { return nullptr; }, v); #endif -#endif // _M_CEE +#endif // ^^^ no workaround ^^^ } return true; } @@ -5548,8 +6179,8 @@ int run_test() // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// -// Throwing bad_variant_access is supported starting in macosx10.13 -// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12}} && !no-exceptions + + // // template @@ -5558,6 +6189,7 @@ int run_test() #include #include #include +#include #include #include #include @@ -5566,7 +6198,6 @@ int run_test() #include "variant_test_helpers.h" namespace visit { -#if _HAS_CXX20 && !defined(TEST_PERMISSIVE) void test_call_operator_forwarding() { using Fn = ForwardingCallObject; Fn obj{}; @@ -5860,7 +6491,7 @@ struct MyVariant : std::variant {}; } // namespace visit namespace std { -template +template void get(const ::visit::MyVariant&) { assert(false); } @@ -5931,7 +6562,7 @@ void test_sfinae() { struct GoodVariant2 : GoodVariant {}; static_assert(!has_visit(0)); - static_assert(!has_visit(0)); + static_assert(has_visit(0) == is_permissive); static_assert(!has_visit(0)); static_assert(has_visit>(0)); static_assert(has_visit(0)); @@ -5950,12 +6581,6 @@ int run_test() { return 0; } -#else // ^^^ no workaround / workaround vvv -int run_test() { - return 0; -} -#endif // ^^^ workaround ^^^ - } // namespace visit // -- END: test/std/utilities/variant/variant.visit/visit.pass.cpp @@ -5967,8 +6592,8 @@ int run_test() { // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// -// Throwing bad_variant_access is supported starting in macosx10.13 -// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12}} && !no-exceptions + + // // template @@ -5977,6 +6602,7 @@ int run_test() { #include #include #include +#include #include #include #include @@ -5985,7 +6611,7 @@ int run_test() { #include "variant_test_helpers.h" namespace visit::return_type { -#if _HAS_CXX20 && !defined(TEST_PERMISSIVE) +#if _HAS_CXX20 template void test_call_operator_forwarding() { using Fn = ForwardingCallObject; @@ -6428,7 +7054,7 @@ void test_sfinae() { struct BadVariant : std::variant, std::variant {}; static_assert(has_visit >(int())); - static_assert(!has_visit(int())); + static_assert(has_visit(int()) == is_permissive); } int run_test() { @@ -6450,849 +7076,653 @@ int run_test() { return 0; } -#else // ^^^ no workaround / workaround vvv +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv int run_test() { return 0; } -#endif // ^^^ workaround ^^^ +#endif // ^^^ !_HAS_CXX20 ^^^ } // namespace visit::return_type // -- END: test/std/utilities/variant/variant.visit/visit_return_type.pass.cpp -// LLVM SOURCES END -// clang-format on +// -- BEGIN: test/std/utilities/variant/variant.visit.member/robust_against_adl.pass.cpp +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + + +// The tested functionality needs deducing this. + + + +// + +// class variant; +// template +// constexpr decltype(auto) visit(this Self&&, Visitor&&); // since C++26 +// template +// constexpr R visit(this Self&&, Visitor&&); // since C++26 + +#include + +#include "test_macros.h" + +namespace member_visit::robust_against_adl { +#if __cpp_lib_variant >= 202306L +struct Incomplete; +template +struct Holder { + T t; +}; + +constexpr bool test(bool do_it) { + if (do_it) { + std::variant*, int> v = nullptr; + + v.visit([](auto) {}); + v.visit([](auto) -> Holder* { return nullptr; }); + v.visit([](auto) {}); + v.visit([](auto) -> Holder* { return nullptr; }); + } + return true; +} + +int run_test() { + test(true); + static_assert(test(true)); + + return 0; +} +#else // ^^^ P2637R3 available / P2637R3 unavailable vvv +int run_test() { + return 0; +} +#endif // ^^^ P2637R3 unavailable ^^^ +} // namespace member_visit::robust_against_adl +// -- END: test/std/utilities/variant/variant.visit.member/robust_against_adl.pass.cpp + +// -- BEGIN: test/std/utilities/variant/variant.visit.member/visit.pass.cpp +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + + +// The tested functionality needs deducing this. + + + +// + +// class variant; + +// template +// constexpr decltype(auto) visit(this Self&&, Visitor&&); // since C++26 #include -#include -#include -#include #include #include +#include #include #include #include -// Include llvm-project/libcxx/test/support headers: -#include -#include -#include +#include "test_macros.h" +#include "variant_test_helpers.h" -namespace msvc { - namespace size { - template - using element = - std::conditional::value, std::reference_wrapper>, T>; +namespace member_visit { +#if __cpp_lib_variant >= 202306L +void test_call_operator_forwarding() { + using Fn = ForwardingCallObject; + Fn obj{}; + const Fn& cobj = obj; - template - using index_t = - std::conditional_t<(N < static_cast(std::numeric_limits::max())), signed char, - std::conditional_t<(N < static_cast(std::numeric_limits::max())), short, int>>; + { // test call operator forwarding - single variant, single arg + using V = std::variant; + V v(42); - template - struct fake_variant { - std::aligned_union_t<0, typename element::type...> data_; - index_t index_; - }; + v.visit(obj); + assert(Fn::check_call(CT_NonConst | CT_LValue)); + v.visit(cobj); + assert(Fn::check_call(CT_Const | CT_LValue)); + v.visit(std::move(obj)); + assert(Fn::check_call(CT_NonConst | CT_RValue)); + v.visit(std::move(cobj)); + assert(Fn::check_call(CT_Const | CT_RValue)); + } + { // test call operator forwarding - single variant, multi arg + using V = std::variant; + V v(42L); - template - constexpr bool check_size = sizeof(std::variant) == sizeof(fake_variant); + v.visit(obj); + assert(Fn::check_call(CT_NonConst | CT_LValue)); + v.visit(cobj); + assert(Fn::check_call(CT_Const | CT_LValue)); + v.visit(std::move(obj)); + assert(Fn::check_call(CT_NonConst | CT_RValue)); + v.visit(std::move(cobj)); + assert(Fn::check_call(CT_Const | CT_RValue)); + } +} - template - struct empty {}; +// Applies to non-member `std::visit` only. +void test_argument_forwarding() { + using Fn = ForwardingCallObject; + Fn obj{}; + const auto val = CT_LValue | CT_NonConst; - struct not_empty { - int i; - }; - struct __declspec(empty_bases) many_bases : empty<0>, empty<1>, empty<2>, empty<3> {}; + { // single argument - value type + using V = std::variant; + V v(42); + const V& cv = v; - static_assert(check_size); - static_assert(check_size); - static_assert(check_size); - static_assert(check_size); - static_assert(check_size); - static_assert(check_size); - static_assert(check_size); - static_assert(check_size); - static_assert(check_size); - static_assert(check_size); - static_assert(check_size>); - static_assert(check_size); - static_assert(check_size); + v.visit(obj); + assert(Fn::check_call(val)); + cv.visit(obj); + assert(Fn::check_call(val)); + std::move(v).visit(obj); + assert(Fn::check_call(val)); + std::move(cv).visit(obj); + assert(Fn::check_call(val)); + } +} - static_assert(check_size, - empty<1>, not_empty, many_bases>); - } // namespace size +void test_return_type() { + using Fn = ForwardingCallObject; + Fn obj{}; + const Fn& cobj = obj; - namespace big_variant { -#ifdef __EDG__ - constexpr std::size_t big = 20; -#else // C1XX and Clang - constexpr std::size_t big = 64; -#endif // tune value of "big" to a bit less than the largest variant the front-end can handle - constexpr std::size_t n = 16; + { // test call operator forwarding - single variant, single arg + using V = std::variant; + V v(42); - template - void test_gets() { - using V = std::_Meta_repeat_n_c; - V v{std::in_place_index, "Hello, world!"}; - assert(std::get(v) == "Hello, world!"); - if constexpr (N != 0) { - test_gets(); - } - } + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + } + { // test call operator forwarding - single variant, multi arg + using V = std::variant; + V v(42L); - template - void test_size() { - if constexpr (Size <= big) { - using V = std::_Meta_repeat_n_c; - // test 0, and n equally spaced indices including big - 1 - test_gets < Size, 0, n(); - constexpr std::size_t i = Size / 2; - V v1{std::in_place_index, "Hello, world!"}; - V v2 = v1; - assert(std::get(v2) == "Hello, world!"); - V v3 = std::move(v1); - assert(std::get(v3) == "Hello, world!"); - constexpr std::size_t j = Size <= 2 ? 0 : i + 1; - v2.template emplace("Goodbye, world!"); - v1 = v2; - assert(std::get(v1) == "Goodbye, world!"); - v2 = std::move(v3); - assert(std::get(v2) == "Hello, world!"); - v1.swap(v2); - assert(std::get(v2) == "Goodbye, world!"); - assert(std::get(v1) == "Hello, world!"); - auto visitor = [](const std::string& s) { return s; }; - assert(std::visit(visitor, v1) == "Hello, world!"); - } - } + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + static_assert(std::is_same_v); + } +} - void run_test() { - // test with all of the power-of-four sizes used for the switch blocks in visit and _Variant_raw_visit - test_size<1>(); - test_size<3>(); - test_size<15>(); - test_size<63>(); - test_size<255>(); - test_size(); - } - } // namespace big_variant +void test_constexpr() { + constexpr ReturnFirst obj{}; - namespace derived_variant { - void run_test() { - // Extension: std::visit accepts types derived from a specialization of variant. - { - struct my_variant : std::variant { - using std::variant::variant; - }; + { + using V = std::variant; + constexpr V v(42); - my_variant v1{42}; - my_variant v2{3.14}; - auto visitor1 = [](auto&& x) { return static_cast(x); }; - assert(std::visit(visitor1, v1) == 42.0); - assert(std::visit(visitor1, v2) == 3.14); - auto visitor2 = [](auto&& x, auto&& y) { return static_cast(x + y); }; - assert(std::visit(visitor2, v1, v2) == 45.14); - } - { - struct MakeEmptyT { - MakeEmptyT() = default; - MakeEmptyT(MakeEmptyT&&) { - throw 42; - } - MakeEmptyT& operator=(MakeEmptyT&&) { - throw 42; - } - }; - struct my_variant : std::variant { - using std::variant::variant; - }; + static_assert(v.visit(obj) == 42); + } + { + using V = std::variant; + constexpr V v(42L); - my_variant v{42}; - try { - v = my_variant{std::in_place_type}; - abort(); - } catch (int) { - assert(v.valueless_by_exception()); - } + static_assert(v.visit(obj) == 42); + } +} - auto very_useful_visitor = [](auto&&...) { abort(); }; - try { - std::visit(very_useful_visitor, v); - abort(); - } catch (const std::bad_variant_access&) { - } - try { - std::visit(very_useful_visitor, my_variant{42}, v); - abort(); - } catch (const std::bad_variant_access&) { - } - try { - std::visit(very_useful_visitor, v, my_variant{42}); - abort(); - } catch (const std::bad_variant_access&) { - } - } - } - } // namespace derived_variant - - template - struct mobile_visitor { - mobile_visitor() = default; - - mobile_visitor(const mobile_visitor&) { - abort(); - } - mobile_visitor(mobile_visitor&&) { - abort(); - } - mobile_visitor& operator=(const mobile_visitor&) { - abort(); - return *this; - } - mobile_visitor& operator=(mobile_visitor&&) { - abort(); - return *this; - } - - template - constexpr decltype(auto) operator()(Args&&... args) const { - return Fn{}(std::forward(args)...); - } - }; - - template - struct immobile_visitor : mobile_visitor { - immobile_visitor() = default; - immobile_visitor(const immobile_visitor&) = delete; - immobile_visitor& operator=(const immobile_visitor&) = delete; - }; - - template - struct convert_to { - template , int> = 0> - constexpr T operator()(U&& u) const { - return std::forward(u); - } - }; - - namespace visit { - void test_immobile_function() { - { - // Validate that visit need not copy or move the visitor - using V = std::variant; - immobile_visitor> visitor{}; - assert(std::visit(visitor, V{42}) == 42.0); - assert(std::visit(std::as_const(visitor), V{3.14}) == 3.14); - assert(std::visit(std::move(visitor), V{1729}) == 1729.0); - assert(std::visit(std::move(std::as_const(visitor)), V{1.414}) == 1.414); - } - { - // Validate that visit does not copy or move the visitor - using V = std::variant; - mobile_visitor> visitor{}; - assert(std::visit(visitor, V{42}) == 42.0); - assert(std::visit(std::as_const(visitor), V{3.14}) == 3.14); - assert(std::visit(std::move(visitor), V{1729}) == 1729.0); - assert(std::visit(std::move(std::as_const(visitor)), V{1.414}) == 1.414); - } - } - - void run_test() { - test_immobile_function(); - } - } // namespace visit - - namespace visit_R { -#if _HAS_CXX20 - //===----------------------------------------------------------------------===// - // - // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. - // See https://llvm.org/LICENSE.txt for license information. - // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - // - //===----------------------------------------------------------------------===// - - enum CallType : unsigned int { CT_None, CT_NonConst = 1, CT_Const = 2, CT_LValue = 4, CT_RValue = 8 }; - - constexpr CallType operator|(CallType LHS, CallType RHS) { - return static_cast(static_cast(LHS) | static_cast(RHS)); - } - - struct ForwardingCallObject { - template - bool operator()(Args&&...) & { - set_call(CT_NonConst | CT_LValue); - return {}; - } - - template - char operator()(Args&&...) const& { - set_call(CT_Const | CT_LValue); - return {}; - } - - // Don't allow the call operator to be invoked as an rvalue. - template - short operator()(Args&&...) && { - set_call(CT_NonConst | CT_RValue); - return {}; - } - - template - int operator()(Args&&...) const&& { - set_call(CT_Const | CT_RValue); - return {}; - } - - template - static void set_call(CallType type) { - assert(last_call_type == CT_None); - assert(last_call_args == nullptr); - last_call_type = type; - last_call_args = std::addressof(makeArgumentID()); - } - - template - static bool check_call(CallType type) { - bool result = last_call_type == type && last_call_args && *last_call_args == makeArgumentID(); - last_call_type = CT_None; - last_call_args = nullptr; - return result; - } - - static CallType last_call_type; - static const TypeID* last_call_args; - }; - - CallType ForwardingCallObject::last_call_type = CT_None; - const TypeID* ForwardingCallObject::last_call_args = nullptr; - - template - void test_call_operator_forwarding() { - using Fn = ForwardingCallObject; - Fn obj{}; - const Fn& cobj = obj; - { // test call operator forwarding - no variant - std::visit(obj); - assert(Fn::check_call<>(CT_NonConst | CT_LValue)); - std::visit(cobj); - assert(Fn::check_call<>(CT_Const | CT_LValue)); - std::visit(std::move(obj)); - assert(Fn::check_call<>(CT_NonConst | CT_RValue)); - std::visit(std::move(cobj)); - assert(Fn::check_call<>(CT_Const | CT_RValue)); - } - { // test call operator forwarding - single variant, single arg - using V = std::variant; - V v(42); - std::visit(obj, v); - assert(Fn::check_call(CT_NonConst | CT_LValue)); - std::visit(cobj, v); - assert(Fn::check_call(CT_Const | CT_LValue)); - std::visit(std::move(obj), v); - assert(Fn::check_call(CT_NonConst | CT_RValue)); - std::visit(std::move(cobj), v); - assert(Fn::check_call(CT_Const | CT_RValue)); - } - { // test call operator forwarding - single variant, multi arg - using V = std::variant; - V v(42l); - std::visit(obj, v); - assert(Fn::check_call(CT_NonConst | CT_LValue)); - std::visit(cobj, v); - assert(Fn::check_call(CT_Const | CT_LValue)); - std::visit(std::move(obj), v); - assert(Fn::check_call(CT_NonConst | CT_RValue)); - std::visit(std::move(cobj), v); - assert(Fn::check_call(CT_Const | CT_RValue)); - } - { // test call operator forwarding - multi variant, multi arg - using V = std::variant; - using V2 = std::variant; - V v(42l); - V2 v2("hello"); - std::visit(obj, v, v2); - assert((Fn::check_call(CT_NonConst | CT_LValue))); - std::visit(cobj, v, v2); - assert((Fn::check_call(CT_Const | CT_LValue))); - std::visit(std::move(obj), v, v2); - assert((Fn::check_call(CT_NonConst | CT_RValue))); - std::visit(std::move(cobj), v, v2); - assert((Fn::check_call(CT_Const | CT_RValue))); - } - } - - template - void test_argument_forwarding() { - using Fn = ForwardingCallObject; - Fn obj{}; - const auto Val = CT_LValue | CT_NonConst; - { // single argument - value type - using V = std::variant; - V v(42); - const V& cv = v; - std::visit(obj, v); - assert(Fn::check_call(Val)); - std::visit(obj, cv); - assert(Fn::check_call(Val)); - std::visit(obj, std::move(v)); - assert(Fn::check_call(Val)); - std::visit(obj, std::move(cv)); - assert(Fn::check_call(Val)); - } - } - - struct ReturnFirst { - template - constexpr F operator()(F f, Args&&...) const { - return f; - } - }; - - struct ReturnArity { - template - constexpr int operator()(Args&&...) const { - return sizeof...(Args); - } - }; - - struct simple_base { - int x; - - constexpr explicit simple_base(int i) noexcept : x{i} {} - }; - - template - struct simple_derived : simple_base { - using simple_base::simple_base; - }; - - constexpr bool test_constexpr() { - constexpr ReturnFirst obj{}; - constexpr ReturnArity aobj{}; - { - using V = std::variant; - constexpr V v(42); - static_assert(std::visit(obj, v) == 42); - } - { - using V = std::variant; - constexpr V v(42l); - static_assert(std::visit(obj, v) == 42); - } - { - using V1 = std::variant; - using V2 = std::variant; - using V3 = std::variant; - constexpr V1 v1; - constexpr V2 v2(nullptr); - constexpr V3 v3; - static_assert(std::visit(aobj, v1, v2, v3) == 3.0); - } - { - using V1 = std::variant; - using V2 = std::variant; - using V3 = std::variant; - constexpr V1 v1; - constexpr V2 v2(nullptr); - constexpr V3 v3; - static_assert(std::visit(aobj, v1, v2, v3) == 3LL); - } - { - using V = std::variant, simple_derived<1>, simple_derived<2>>; - V v{simple_derived<1>{42}}; - auto&& b = std::visit(std::identity{}, v); - ASSERT_SAME_TYPE(decltype(b), simple_base&); - assert(b.x == 42); - auto&& cb = std::visit(std::identity{}, std::as_const(v)); - ASSERT_SAME_TYPE(decltype(cb), const simple_base&); - assert(cb.x == 42); - auto&& rb = std::visit(std::identity{}, std::move(v)); - ASSERT_SAME_TYPE(decltype(rb), simple_base&&); - assert(rb.x == 42); - auto&& crb = std::visit(std::identity{}, std::move(std::as_const(v))); - ASSERT_SAME_TYPE(decltype(crb), const simple_base&&); - assert(crb.x == 42); - } - return true; - } - - void test_exceptions() { +void test_exceptions() { #ifndef TEST_HAS_NO_EXCEPTIONS - ReturnArity obj{}; - auto test = [&](auto&&... args) { - try { - std::visit(obj, args...); - } catch (const std::bad_variant_access&) { - return true; - } catch (...) { - } - return false; - }; - { - using V = std::variant; - V v; - makeEmpty(v); - assert(test(v)); - } - { - using V = std::variant; - using V2 = std::variant; - V v; - makeEmpty(v); - V2 v2("hello"); - assert(test(v, v2)); - } - { - using V = std::variant; - using V2 = std::variant; - V v; - makeEmpty(v); - V2 v2("hello"); - assert(test(v2, v)); - } - { - using V = std::variant; - using V2 = std::variant; - V v; - makeEmpty(v); - V2 v2; - makeEmpty(v2); - assert(test(v, v2)); - } + ReturnArity obj{}; + + auto test = [&](auto&& v) { + try { + v.visit(obj); + } catch (const std::bad_variant_access&) { + return true; + } catch (...) { + } + return false; + }; + + { + using V = std::variant; + V v; + makeEmpty(v); + + assert(test(v)); + } #endif - } +} - // See LLVM-31916 - void test_caller_accepts_nonconst() { - struct A {}; - struct Visitor { - void operator()(A&) {} - }; - std::variant v; - std::visit(Visitor{}, v); - } +// See https://llvm.org/PR31916 +void test_caller_accepts_nonconst() { + struct A {}; + struct Visitor { + void operator()(A&) {} + }; + std::variant v; - struct mobile_data { - int x; + v.visit(Visitor{}); +} - /* implicit */ mobile_data(int i) : x{i} {} - mobile_data(const mobile_data&) { - abort(); - } - mobile_data(mobile_data&&) { - abort(); - } - mobile_data& operator=(const mobile_data&) { - abort(); - return *this; - } - mobile_data& operator=(mobile_data&&) { - abort(); - return *this; - } - }; +struct MyVariant : std::variant {}; +} // namespace member_visit - struct immobile_data : mobile_data { - using mobile_data::mobile_data; - immobile_data(const immobile_data&) = delete; - immobile_data& operator=(const immobile_data&) = delete; - }; +namespace std { +template +void get(const ::member_visit::MyVariant&) { + assert(false); +} +} // namespace std - void test_perfect_return() { - { - // Verify that a return object need not be copied/moved - using R = immobile_data; - assert(std::visit(std::identity{}, std::variant{13}).x == 13); - assert(std::visit(std::identity{}, std::variant{short{42}}).x == 42); +namespace member_visit { +void test_derived_from_variant() { + auto v1 = MyVariant{42}; + const auto cv1 = MyVariant{142}; - // Verify that conversions to an object that can't be copied/moved are correctly handled - struct convertible_to_immobile_one { - operator immobile_data() const { - return immobile_data{1729}; - } - }; + v1.visit([](auto x) { assert(x == 42); }); + cv1.visit([](auto x) { assert(x == 142); }); + MyVariant{-1.25f}.visit([](auto x) { assert(x == -1.25f); }); + std::move(v1).visit([](auto x) { assert(x == 42); }); + std::move(cv1).visit([](auto x) { assert(x == 142); }); - struct convertible_to_immobile_other { - operator immobile_data() const { - return immobile_data{1138}; - } - }; + // Check that visit does not take index nor valueless_by_exception members from the base class. + struct EvilVariantBase { + int index; + char valueless_by_exception; + }; - using VarTestConv = std::variant; - assert(std::visit(std::identity{}, VarTestConv{convertible_to_immobile_one{}}).x == 1729); - assert(std::visit(std::identity{}, VarTestConv{convertible_to_immobile_other{}}).x == 1138); - auto immobile_converter = [](auto src) -> immobile_data { return src; }; - assert(std::visit(immobile_converter, VarTestConv{convertible_to_immobile_one{}}).x == 1729); - assert(std::visit(immobile_converter, VarTestConv{convertible_to_immobile_other{}}).x == 1138); - } - { - // Verify that a returned object is not copied/moved/modified - using R = mobile_data; - assert(std::visit(std::identity{}, std::variant{13}).x == 13); - assert(std::visit(std::identity{}, std::variant{short{42}}).x == 42); + struct EvilVariant1 : std::variant, std::tuple, EvilVariantBase { + using std::variant::variant; + }; - // Verify that a returned reference is not copied/moved/modified - auto visitor1 = [x = R{1729}](auto) mutable -> R& { return x; }; - assert(std::visit(visitor1, std::variant{13}).x == 1729); - auto visitor2 = [x = R{1138}](auto) mutable -> R&& { return std::move(x); }; - assert(std::visit(visitor2, std::variant{13}).x == 1138); - } - } + EvilVariant1{12}.visit([](auto x) { assert(x == 12); }); + EvilVariant1{12.3}.visit([](auto x) { assert(x == 12.3); }); - void test_immobile_function() { - { - // Validate that visit need not copy or move the visitor - using V = std::variant; - immobile_visitor visitor{}; - assert(std::visit(visitor, V{42}) == 42.0); - assert(std::visit(std::as_const(visitor), V{3.14}) == 3.14); - assert(std::visit(std::move(visitor), V{1729}) == 1729.0); - assert(std::visit(std::move(std::as_const(visitor)), V{1.414}) == 1.414); - } - { - // Validate that visit does not copy or move the visitor - using V = std::variant; - mobile_visitor visitor{}; - assert(std::visit(visitor, V{42}) == 42.0); - assert(std::visit(std::as_const(visitor), V{3.14}) == 3.14); - assert(std::visit(std::move(visitor), V{1729}) == 1729.0); - assert(std::visit(std::move(std::as_const(visitor)), V{1.414}) == 1.414); - } - } + // Check that visit unambiguously picks the variant, even if the other base has __impl member. + struct ImplVariantBase { + struct Callable { + bool operator()() const { + assert(false); + return false; + } + }; - void run_test() { - test_call_operator_forwarding(); - test_call_operator_forwarding(); - test_call_operator_forwarding(); - test_argument_forwarding(); - test_argument_forwarding(); - test_argument_forwarding(); - test_constexpr(); - static_assert(test_constexpr()); - test_exceptions(); - test_caller_accepts_nonconst(); - test_perfect_return(); - test_immobile_function(); - } -#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv - void run_test() {} -#endif // _HAS_CXX20 - } // namespace visit_R + Callable __impl; + }; - namespace visit_pointer_to_member { - struct base { - int x; + struct EvilVariant2 : std::variant, ImplVariantBase { + using std::variant::variant; + }; - int f() const { - return x; - } - }; - struct derived : base { - int y; - }; + EvilVariant2{12}.visit([](auto x) { assert(x == 12); }); + EvilVariant2{12.3}.visit([](auto x) { assert(x == 12.3); }); +} - void run_test() { - using V = std::variant; - assert(std::visit(&base::x, V{base{13}}) == 13); - assert(std::visit(&base::x, V{derived{{42}, 29}}) == 42); +int run_test() { + test_call_operator_forwarding(); + test_argument_forwarding(); + test_return_type(); + test_constexpr(); + test_exceptions(); + test_caller_accepts_nonconst(); + test_derived_from_variant(); - assert(std::visit(&base::f, V{base{13}}) == 13); - assert(std::visit(&base::f, V{derived{{42}, 29}}) == 42); - } - } // namespace visit_pointer_to_member + return 0; +} +#else // ^^^ P2637R3 available / P2637R3 unavailable vvv +int run_test() { + return 0; +} +#endif // ^^^ P2637R3 unavailable ^^^ +} // namespace member_visit +// -- END: test/std/utilities/variant/variant.visit.member/visit.pass.cpp - template - constexpr bool has_type = false; - template - constexpr bool has_type> = true; +// -- BEGIN: test/std/utilities/variant/variant.visit.member/visit_return_type.pass.cpp +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// - // Verify that `_Meta_at_<_Meta_list<>, size_t(-1)>` has no member named `type`, and that instantiating it doesn't - // consume the entire compiler heap. - static_assert(!has_type, static_cast(-1)>>); - namespace vso468746 { - // Defend against regression of VSO-468746 - // "std::function's converting constructor/assignment should be unusable for performing copy/move assignments" +// The tested functionality needs deducing this. - void run_test() { - struct S { - std::variant> member; - }; - static_assert( - sizeof(std::variant>) == sizeof(std::function) + alignof(std::function)); - static_assert(sizeof(S::member) == sizeof(std::variant>)); - static_assert(sizeof(S) >= sizeof(S::member)); - } - } // namespace vso468746 - namespace vso492097 { - // Defend against regression of VSO-492097 - // The compiler was not correctly emitting constant data for variants constructed from the address of static - // duration variables with a constexpr operator& due to mishandling initialization of nested classes with - // anonymous union members. - template - struct wrap { - T val_; - constexpr T* operator&() { - return &val_; - } - }; +// - void run_test() { - static wrap intVar = {42}; - static wrap doubleVar = {3.14}; +// class variant; - using V = std::variant; - static constexpr V v1(&intVar); - static constexpr V v2(&doubleVar); +// template +// constexpr R visit(this Self&&, Visitor&&); // since C++26 - static_assert(v1.index() == 0); - assert(*std::get<0>(v1) == 42); - static_assert(v2.index() == 1); - assert(*std::get<1>(v2) == 3.14); - } - } // namespace vso492097 +#include +#include +#include +#include +#include +#include +#include - namespace vso508126 { - void run_test() { - struct S {}; - static_assert(!std::is_copy_constructible_v); - static_assert(!std::is_copy_constructible_v>); - } - } // namespace vso508126 +#include "test_macros.h" +#include "variant_test_helpers.h" - namespace DevCom1031281 { - // Compilers may warn when initializing a variant from a "weird" argument, e.g., std::variant{some_int} - // is potentially narrowing. Compilers should not, however, emit such diagnostics from the metaprogramming that - // determines which alternative a variant initialization would activate. We don't want to emit warnings when - // determining implicit conversion sequences early in overload resolution. +namespace member_visit::return_type { +#if __cpp_lib_variant >= 202306L +template +struct overloaded : Ts... { + using Ts::operator()...; +}; - void Overload(int) {} - void Overload(std::variant) {} +template +overloaded(Ts...) -> overloaded; - void run_test() { - Overload(42); - } - } // namespace DevCom1031281 +void test_overload_ambiguity() { + using V = std::variant; + using namespace std::string_literals; + V v{"baba"s}; - namespace gh2770 { - // Previous metaprogramming to validate the type requirements for std::visit required typelists too long for - // Clang. - struct S { - template - int operator()(T0, T1, T2, T3, T4) const { - return 1729; - } - }; + v.visit( + overloaded{[]([[maybe_unused]] auto x) { assert(false); }, [](const std::string& x) { assert(x == "baba"s); }}); + assert(std::get(v) == "baba"s); - void run_test() { - using V = std::variant; - assert(std::visit(S{}, V{'a'}, V{'b'}, V{10}, V{20L}, V{30LL}) == 1729); -#if _HAS_CXX20 - assert(std::visit(S{}, V{'a'}, V{'b'}, V{10}, V{20L}, V{30LL}) == 1729); -#endif // _HAS_CXX20 - } - } // namespace gh2770 + // Test the constraint. + v = std::move(v).visit(overloaded{ + []([[maybe_unused]] auto x) { + assert(false); + return 0; + }, + [](const std::string& x) { + assert(x == "baba"s); + return x + " zmt"s; + }}); + assert(std::get(v) == "baba zmt"s); +} - namespace assign_cv { - template - struct TypeIdentityImpl { - using type = T; - }; - template - using TypeIdentity = typename TypeIdentityImpl::type; +template +void test_call_operator_forwarding() { + using Fn = ForwardingCallObject; + Fn obj{}; + const Fn& cobj = obj; - struct CvAssignable { - CvAssignable() = default; - CvAssignable(const CvAssignable&) = default; - CvAssignable(CvAssignable&&) = default; - CvAssignable& operator=(const CvAssignable&) = default; - CvAssignable& operator=(CvAssignable&&) = default; + { // test call operator forwarding - single variant, single arg + using V = std::variant; + V v(42); - template - CvAssignable(const volatile TypeIdentity&) noexcept {} - template - CvAssignable(const volatile TypeIdentity&&) noexcept {} + v.visit(obj); + assert(Fn::check_call(CT_NonConst | CT_LValue)); + v.visit(cobj); + assert(Fn::check_call(CT_Const | CT_LValue)); + v.visit(std::move(obj)); + assert(Fn::check_call(CT_NonConst | CT_RValue)); + v.visit(std::move(cobj)); + assert(Fn::check_call(CT_Const | CT_RValue)); + } + { // test call operator forwarding - single variant, multi arg + using V = std::variant; + V v(42L); - template - constexpr CvAssignable& operator=(const volatile TypeIdentity&) noexcept { - return *this; - } - template - constexpr CvAssignable& operator=(const volatile TypeIdentity&&) noexcept { - return *this; - } + v.visit(obj); + assert(Fn::check_call(CT_NonConst | CT_LValue)); + v.visit(cobj); + assert(Fn::check_call(CT_Const | CT_LValue)); + v.visit(std::move(obj)); + assert(Fn::check_call(CT_NonConst | CT_RValue)); + v.visit(std::move(cobj)); + assert(Fn::check_call(CT_Const | CT_RValue)); + } +} - template - constexpr const volatile CvAssignable& operator=(const volatile TypeIdentity&) const volatile noexcept { - return *this; - } - template - constexpr const volatile CvAssignable& operator=(const volatile TypeIdentity&&) const volatile noexcept { - return *this; - } - }; +template +void test_argument_forwarding() { + using Fn = ForwardingCallObject; + Fn obj{}; + const auto val = CT_LValue | CT_NonConst; - void run_test() { - using std::swap; - { - std::variant oc{}; - oc.emplace<0>(0); - static_assert(!std::is_copy_assignable_v); - static_assert(!std::is_move_assignable_v); - static_assert(!std::is_swappable_v); + { // single argument - value type + using V = std::variant; + V v(42); + const V& cv = v; - std::variant ov{}; - std::variant ov2{}; - ov.emplace<0>(0); - swap(ov, ov); - ov = ov2; - ov = std::move(ov2); + v.visit(obj); + assert(Fn::check_call(val)); + cv.visit(obj); + assert(Fn::check_call(val)); + std::move(v).visit(obj); + assert(Fn::check_call(val)); + std::move(cv).visit(obj); + assert(Fn::check_call(val)); + } +} - std::variant ocv{}; - ocv.emplace<0>(0); - static_assert(!std::is_copy_assignable_v); - static_assert(!std::is_move_assignable_v); - static_assert(!std::is_swappable_v); - } - { - std::variant oc{}; - std::variant oc2{}; - oc.emplace<0>(CvAssignable{}); - swap(oc, oc); - oc = oc2; - oc = std::move(oc2); +template +void test_return_type() { + using Fn = ForwardingCallObject; + Fn obj{}; + const Fn& cobj = obj; - std::variant ov{}; - std::variant ov2{}; - ov.emplace<0>(CvAssignable{}); - swap(ov, ov); - ov = ov2; - ov = std::move(ov2); + { // test call operator forwarding - no variant + // non-member + { + static_assert(std::is_same_v(obj)), ReturnType>); + static_assert(std::is_same_v(cobj)), ReturnType>); + static_assert(std::is_same_v(std::move(obj))), ReturnType>); + static_assert(std::is_same_v(std::move(cobj))), ReturnType>); + } + } + { // test call operator forwarding - single variant, single arg + using V = std::variant; + V v(42); - std::variant ocv{}; - std::variant ocv2{}; - ocv.emplace<0>(CvAssignable{}); - swap(ocv, ocv); - ocv = ocv2; - ocv = std::move(ocv2); - } - } - } // namespace assign_cv -} // namespace msvc + static_assert(std::is_same_v(obj)), ReturnType>); + static_assert(std::is_same_v(cobj)), ReturnType>); + static_assert(std::is_same_v(std::move(obj))), ReturnType>); + static_assert(std::is_same_v(std::move(cobj))), ReturnType>); + } + { // test call operator forwarding - single variant, multi arg + using V = std::variant; + V v(42L); + + static_assert(std::is_same_v(obj)), ReturnType>); + static_assert(std::is_same_v(cobj)), ReturnType>); + static_assert(std::is_same_v(std::move(obj))), ReturnType>); + static_assert(std::is_same_v(std::move(cobj))), ReturnType>); + } +} + +void test_constexpr_void() { + constexpr ReturnFirst obj{}; + + { + using V = std::variant; + constexpr V v(42); + + static_assert((v.visit(obj), 42) == 42); + } + { + using V = std::variant; + constexpr V v(42L); + + static_assert((v.visit(obj), 42) == 42); + } +} + +void test_constexpr_int() { + constexpr ReturnFirst obj{}; + + { + using V = std::variant; + constexpr V v(42); + + static_assert(v.visit(obj) == 42); + } + { + using V = std::variant; + constexpr V v(42L); + + static_assert(v.visit(obj) == 42); + } +} + +template +void test_exceptions() { +#ifndef TEST_HAS_NO_EXCEPTIONS + ReturnArity obj{}; + + auto test = [&](auto&& v) { + try { + v.template visit(obj); + } catch (const std::bad_variant_access&) { + return true; + } catch (...) { + } + return false; + }; + + { + using V = std::variant; + V v; + makeEmpty(v); + + assert(test(v)); + } +#endif +} + +// See https://bugs.llvm.org/show_bug.cgi?id=31916 +template +void test_caller_accepts_nonconst() { + struct A {}; + struct Visitor { + auto operator()(A&) { + if constexpr (!std::is_void_v) { + return ReturnType{}; + } + } + }; + std::variant v; + + v.template visit(Visitor{}); +} + +void test_constexpr_explicit_side_effect() { + auto test_lambda = [](int arg) constexpr { + std::variant v = 101; + + { + v.template visit([arg](int& x) constexpr { x = arg; }); + } + + return std::get(v); + }; + + static_assert(test_lambda(202) == 202); +} + +void test_derived_from_variant() { + struct MyVariant : std::variant {}; + + MyVariant{42}.template visit([](auto x) { + assert(x == 42); + return true; + }); + MyVariant{-1.3f}.template visit([](auto x) { + assert(x == -1.3f); + return true; + }); + + // Check that visit does not take index nor valueless_by_exception members from the base class. + struct EvilVariantBase { + int index; + char valueless_by_exception; + }; + + struct EvilVariant1 : std::variant, std::tuple, EvilVariantBase { + using std::variant::variant; + }; + + EvilVariant1{12}.template visit([](auto x) { + assert(x == 12); + return true; + }); + EvilVariant1{12.3}.template visit([](auto x) { + assert(x == 12.3); + return true; + }); + + // Check that visit unambiguously picks the variant, even if the other base has __impl member. + struct ImplVariantBase { + struct Callable { + bool operator()() const { + assert(false); + return false; + } + }; + + Callable __impl; + }; + + struct EvilVariant2 : std::variant, ImplVariantBase { + using std::variant::variant; + }; + + EvilVariant2{12}.template visit([](auto x) { + assert(x == 12); + return true; + }); + EvilVariant2{12.3}.template visit([](auto x) { + assert(x == 12.3); + return true; + }); +} + +int run_test() { + test_overload_ambiguity(); + test_call_operator_forwarding(); + test_argument_forwarding(); + test_return_type(); + test_constexpr_void(); + test_exceptions(); + test_caller_accepts_nonconst(); + test_call_operator_forwarding(); + test_argument_forwarding(); + test_return_type(); + test_constexpr_int(); + test_exceptions(); + test_caller_accepts_nonconst(); + test_constexpr_explicit_side_effect(); + test_derived_from_variant(); + + return 0; +} +#else // ^^^ P2637R3 available / P2637R3 unavailable vvv +int run_test() { + return 0; +} +#endif // ^^^ P2637R3 unavailable ^^^ +} // namespace member_visit::return_type +// -- END: test/std/utilities/variant/variant.visit.member/visit_return_type.pass.cpp +// LLVM SOURCES END +// clang-format on int main() { bad_variant_access::run_test(); - get_if::index::run_test(); - get_if::type::run_test(); - get::index::run_test(); - get::type::run_test(); - holds_alternative::run_test(); + get::get_if_index::run_test(); + get::get_if_type::run_test(); + get::get_index::run_test(); + get::get_type::run_test(); + get::holds_alternative::run_test(); - enabled_hash::run_test(); + hash::enabled_hash::run_test(); hash::run_test(); helpers::variant_alternative::run_test(); @@ -7301,10 +7731,13 @@ int main() { monostate::properties::run_test(); monostate::relops::run_test(); - relops::run_test(); + relops::relops::run_test(); + relops::three_way::run_test(); npos::run_test(); + implicit_ctad::run_test(); + assign::conv::run_test(); assign::copy::run_test(); assign::move::run_test(); @@ -7314,15 +7747,15 @@ int main() { ctor::copy::run_test(); ctor::default_::run_test(); ctor::in_place_index_args::run_test(); - ctor::in_place_type_args::run_test(); ctor::in_place_index_init_list_args::run_test(); + ctor::in_place_type_args::run_test(); ctor::in_place_type_init_list_args::run_test(); ctor::move::run_test(); ctor::T::run_test(); dtor::run_test(); - emplace::index::run_test(); + emplace::index_args::run_test(); emplace::index_init_list_args::run_test(); emplace::type_args::run_test(); emplace::type_init_list_args::run_test(); @@ -7336,18 +7769,9 @@ int main() { visit::run_test(); visit::return_type::run_test(); - msvc::big_variant::run_test(); - msvc::derived_variant::run_test(); - msvc::visit::run_test(); - msvc::visit_R::run_test(); - msvc::visit_pointer_to_member::run_test(); - - msvc::vso468746::run_test(); - msvc::vso508126::run_test(); - msvc::vso492097::run_test(); - msvc::DevCom1031281::run_test(); - msvc::gh2770::run_test(); - msvc::assign_cv::run_test(); + member_visit::robust_against_adl::run_test(); + member_visit::run_test(); + member_visit::return_type::run_test(); } #else // ^^^ not x86 or not /analyze / x86 /analyze vvv int main() {} diff --git a/tests/std/tests/P0088R3_variant_msvc/env.lst b/tests/std/tests/P0088R3_variant_msvc/env.lst new file mode 100644 index 000000000..2de7aab29 --- /dev/null +++ b/tests/std/tests/P0088R3_variant_msvc/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_17_matrix.lst diff --git a/tests/std/tests/P0088R3_variant_msvc/test.cpp b/tests/std/tests/P0088R3_variant_msvc/test.cpp new file mode 100644 index 000000000..adcb2967a --- /dev/null +++ b/tests/std/tests/P0088R3_variant_msvc/test.cpp @@ -0,0 +1,844 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#define _SILENCE_CXX23_ALIGNED_UNION_DEPRECATION_WARNING +#define _LIBCXX_IN_DEVCRT +#include // Must precede any other libc++ headers + +// Include Standard headers: +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Include llvm-project/libcxx/test/support headers: +#include +#include +#include + +namespace msvc { + namespace size { + template + using element = + std::conditional::value, std::reference_wrapper>, T>; + + template + using index_t = + std::conditional_t<(N < static_cast(std::numeric_limits::max())), signed char, + std::conditional_t<(N < static_cast(std::numeric_limits::max())), short, int>>; + + template + struct fake_variant { + std::aligned_union_t<0, typename element::type...> data_; + index_t index_; + }; + + template + constexpr bool check_size = sizeof(std::variant) == sizeof(fake_variant); + + template + struct empty {}; + + struct not_empty { + int i; + }; + struct __declspec(empty_bases) many_bases : empty<0>, empty<1>, empty<2>, empty<3> {}; + + static_assert(check_size); + static_assert(check_size); + static_assert(check_size); + static_assert(check_size); + static_assert(check_size); + static_assert(check_size); + static_assert(check_size); + static_assert(check_size); + static_assert(check_size); + static_assert(check_size); + static_assert(check_size>); + static_assert(check_size); + static_assert(check_size); + + static_assert(check_size, + empty<1>, not_empty, many_bases>); + } // namespace size + + namespace big_variant { +#ifdef __EDG__ + constexpr std::size_t big = 20; +#else // C1XX and Clang + constexpr std::size_t big = 64; +#endif // tune value of "big" to a bit less than the largest variant the front-end can handle + constexpr std::size_t n = 16; + + template + void test_gets() { + using V = std::_Meta_repeat_n_c; + V v{std::in_place_index, "Hello, world!"}; + assert(std::get(v) == "Hello, world!"); + if constexpr (N != 0) { + test_gets(); + } + } + + template + void test_size() { + if constexpr (Size <= big) { + using V = std::_Meta_repeat_n_c; + // test 0, and n equally spaced indices including big - 1 + test_gets < Size, 0, n(); + constexpr std::size_t i = Size / 2; + V v1{std::in_place_index, "Hello, world!"}; + V v2 = v1; + assert(std::get(v2) == "Hello, world!"); + V v3 = std::move(v1); + assert(std::get(v3) == "Hello, world!"); + constexpr std::size_t j = Size <= 2 ? 0 : i + 1; + v2.template emplace("Goodbye, world!"); + v1 = v2; + assert(std::get(v1) == "Goodbye, world!"); + v2 = std::move(v3); + assert(std::get(v2) == "Hello, world!"); + v1.swap(v2); + assert(std::get(v2) == "Goodbye, world!"); + assert(std::get(v1) == "Hello, world!"); + auto visitor = [](const std::string& s) { return s; }; + assert(std::visit(visitor, v1) == "Hello, world!"); + } + } + + void run_test() { + // test with all of the power-of-four sizes used for the switch blocks in visit and _Variant_raw_visit + test_size<1>(); + test_size<3>(); + test_size<15>(); + test_size<63>(); + test_size<255>(); + test_size(); + } + } // namespace big_variant + + namespace derived_variant { + void run_test() { + // Extension: std::visit accepts types derived from a specialization of variant. + { + struct my_variant : std::variant { + using std::variant::variant; + }; + + my_variant v1{42}; + my_variant v2{3.14}; + auto visitor1 = [](auto&& x) { return static_cast(x); }; + assert(std::visit(visitor1, v1) == 42.0); + assert(std::visit(visitor1, v2) == 3.14); + auto visitor2 = [](auto&& x, auto&& y) { return static_cast(x + y); }; + assert(std::visit(visitor2, v1, v2) == 45.14); + } + { + struct MakeEmptyT { + MakeEmptyT() = default; + MakeEmptyT(MakeEmptyT&&) { + throw 42; + } + MakeEmptyT& operator=(MakeEmptyT&&) { + throw 42; + } + }; + struct my_variant : std::variant { + using std::variant::variant; + }; + + my_variant v{42}; + try { + v = my_variant{std::in_place_type}; + abort(); + } catch (int) { + assert(v.valueless_by_exception()); + } + + auto very_useful_visitor = [](auto&&...) { abort(); }; + try { + std::visit(very_useful_visitor, v); + abort(); + } catch (const std::bad_variant_access&) { + } + try { + std::visit(very_useful_visitor, my_variant{42}, v); + abort(); + } catch (const std::bad_variant_access&) { + } + try { + std::visit(very_useful_visitor, v, my_variant{42}); + abort(); + } catch (const std::bad_variant_access&) { + } + } + } + } // namespace derived_variant + + template + struct mobile_visitor { + mobile_visitor() = default; + + mobile_visitor(const mobile_visitor&) { + abort(); + } + mobile_visitor(mobile_visitor&&) { + abort(); + } + mobile_visitor& operator=(const mobile_visitor&) { + abort(); + return *this; + } + mobile_visitor& operator=(mobile_visitor&&) { + abort(); + return *this; + } + + template + constexpr decltype(auto) operator()(Args&&... args) const { + return Fn{}(std::forward(args)...); + } + }; + + template + struct immobile_visitor : mobile_visitor { + immobile_visitor() = default; + immobile_visitor(const immobile_visitor&) = delete; + immobile_visitor& operator=(const immobile_visitor&) = delete; + }; + + template + struct convert_to { + template , int> = 0> + constexpr T operator()(U&& u) const { + return std::forward(u); + } + }; + + namespace visit { + void test_immobile_function() { + { + // Validate that visit need not copy or move the visitor + using V = std::variant; + immobile_visitor> visitor{}; + assert(std::visit(visitor, V{42}) == 42.0); + assert(std::visit(std::as_const(visitor), V{3.14}) == 3.14); + assert(std::visit(std::move(visitor), V{1729}) == 1729.0); + assert(std::visit(std::move(std::as_const(visitor)), V{1.414}) == 1.414); + } + { + // Validate that visit does not copy or move the visitor + using V = std::variant; + mobile_visitor> visitor{}; + assert(std::visit(visitor, V{42}) == 42.0); + assert(std::visit(std::as_const(visitor), V{3.14}) == 3.14); + assert(std::visit(std::move(visitor), V{1729}) == 1729.0); + assert(std::visit(std::move(std::as_const(visitor)), V{1.414}) == 1.414); + } + } + + void run_test() { + test_immobile_function(); + } + } // namespace visit + + namespace visit_R { +#if _HAS_CXX20 + //===----------------------------------------------------------------------===// + // + // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. + // See https://llvm.org/LICENSE.txt for license information. + // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + // + //===----------------------------------------------------------------------===// + + enum CallType : unsigned int { CT_None, CT_NonConst = 1, CT_Const = 2, CT_LValue = 4, CT_RValue = 8 }; + + constexpr CallType operator|(CallType LHS, CallType RHS) { + return static_cast(static_cast(LHS) | static_cast(RHS)); + } + + struct ForwardingCallObject { + template + bool operator()(Args&&...) & { + set_call(CT_NonConst | CT_LValue); + return {}; + } + + template + char operator()(Args&&...) const& { + set_call(CT_Const | CT_LValue); + return {}; + } + + // Don't allow the call operator to be invoked as an rvalue. + template + short operator()(Args&&...) && { + set_call(CT_NonConst | CT_RValue); + return {}; + } + + template + int operator()(Args&&...) const&& { + set_call(CT_Const | CT_RValue); + return {}; + } + + template + static void set_call(CallType type) { + assert(last_call_type == CT_None); + assert(last_call_args == nullptr); + last_call_type = type; + last_call_args = std::addressof(makeArgumentID()); + } + + template + static bool check_call(CallType type) { + bool result = last_call_type == type && last_call_args && *last_call_args == makeArgumentID(); + last_call_type = CT_None; + last_call_args = nullptr; + return result; + } + + static CallType last_call_type; + static const TypeID* last_call_args; + }; + + CallType ForwardingCallObject::last_call_type = CT_None; + const TypeID* ForwardingCallObject::last_call_args = nullptr; + + template + void test_call_operator_forwarding() { + using Fn = ForwardingCallObject; + Fn obj{}; + const Fn& cobj = obj; + { // test call operator forwarding - no variant + std::visit(obj); + assert(Fn::check_call<>(CT_NonConst | CT_LValue)); + std::visit(cobj); + assert(Fn::check_call<>(CT_Const | CT_LValue)); + std::visit(std::move(obj)); + assert(Fn::check_call<>(CT_NonConst | CT_RValue)); + std::visit(std::move(cobj)); + assert(Fn::check_call<>(CT_Const | CT_RValue)); + } + { // test call operator forwarding - single variant, single arg + using V = std::variant; + V v(42); + std::visit(obj, v); + assert(Fn::check_call(CT_NonConst | CT_LValue)); + std::visit(cobj, v); + assert(Fn::check_call(CT_Const | CT_LValue)); + std::visit(std::move(obj), v); + assert(Fn::check_call(CT_NonConst | CT_RValue)); + std::visit(std::move(cobj), v); + assert(Fn::check_call(CT_Const | CT_RValue)); + } + { // test call operator forwarding - single variant, multi arg + using V = std::variant; + V v(42l); + std::visit(obj, v); + assert(Fn::check_call(CT_NonConst | CT_LValue)); + std::visit(cobj, v); + assert(Fn::check_call(CT_Const | CT_LValue)); + std::visit(std::move(obj), v); + assert(Fn::check_call(CT_NonConst | CT_RValue)); + std::visit(std::move(cobj), v); + assert(Fn::check_call(CT_Const | CT_RValue)); + } + { // test call operator forwarding - multi variant, multi arg + using V = std::variant; + using V2 = std::variant; + V v(42l); + V2 v2("hello"); + std::visit(obj, v, v2); + assert((Fn::check_call(CT_NonConst | CT_LValue))); + std::visit(cobj, v, v2); + assert((Fn::check_call(CT_Const | CT_LValue))); + std::visit(std::move(obj), v, v2); + assert((Fn::check_call(CT_NonConst | CT_RValue))); + std::visit(std::move(cobj), v, v2); + assert((Fn::check_call(CT_Const | CT_RValue))); + } + } + + template + void test_argument_forwarding() { + using Fn = ForwardingCallObject; + Fn obj{}; + const auto Val = CT_LValue | CT_NonConst; + { // single argument - value type + using V = std::variant; + V v(42); + const V& cv = v; + std::visit(obj, v); + assert(Fn::check_call(Val)); + std::visit(obj, cv); + assert(Fn::check_call(Val)); + std::visit(obj, std::move(v)); + assert(Fn::check_call(Val)); + std::visit(obj, std::move(cv)); + assert(Fn::check_call(Val)); + } + } + + struct ReturnFirst { + template + constexpr F operator()(F f, Args&&...) const { + return f; + } + }; + + struct ReturnArity { + template + constexpr int operator()(Args&&...) const { + return sizeof...(Args); + } + }; + + struct simple_base { + int x; + + constexpr explicit simple_base(int i) noexcept : x{i} {} + }; + + template + struct simple_derived : simple_base { + using simple_base::simple_base; + }; + + constexpr bool test_constexpr() { + constexpr ReturnFirst obj{}; + constexpr ReturnArity aobj{}; + { + using V = std::variant; + constexpr V v(42); + static_assert(std::visit(obj, v) == 42); + } + { + using V = std::variant; + constexpr V v(42l); + static_assert(std::visit(obj, v) == 42); + } + { + using V1 = std::variant; + using V2 = std::variant; + using V3 = std::variant; + constexpr V1 v1; + constexpr V2 v2(nullptr); + constexpr V3 v3; + static_assert(std::visit(aobj, v1, v2, v3) == 3.0); + } + { + using V1 = std::variant; + using V2 = std::variant; + using V3 = std::variant; + constexpr V1 v1; + constexpr V2 v2(nullptr); + constexpr V3 v3; + static_assert(std::visit(aobj, v1, v2, v3) == 3LL); + } + { + using V = std::variant, simple_derived<1>, simple_derived<2>>; + V v{simple_derived<1>{42}}; + auto&& b = std::visit(std::identity{}, v); + ASSERT_SAME_TYPE(decltype(b), simple_base&); + assert(b.x == 42); + auto&& cb = std::visit(std::identity{}, std::as_const(v)); + ASSERT_SAME_TYPE(decltype(cb), const simple_base&); + assert(cb.x == 42); + auto&& rb = std::visit(std::identity{}, std::move(v)); + ASSERT_SAME_TYPE(decltype(rb), simple_base&&); + assert(rb.x == 42); + auto&& crb = std::visit(std::identity{}, std::move(std::as_const(v))); + ASSERT_SAME_TYPE(decltype(crb), const simple_base&&); + assert(crb.x == 42); + } + return true; + } + + void test_exceptions() { +#ifndef TEST_HAS_NO_EXCEPTIONS + ReturnArity obj{}; + auto test = [&](auto&&... args) { + try { + std::visit(obj, args...); + } catch (const std::bad_variant_access&) { + return true; + } catch (...) { + } + return false; + }; + { + using V = std::variant; + V v; + makeEmpty(v); + assert(test(v)); + } + { + using V = std::variant; + using V2 = std::variant; + V v; + makeEmpty(v); + V2 v2("hello"); + assert(test(v, v2)); + } + { + using V = std::variant; + using V2 = std::variant; + V v; + makeEmpty(v); + V2 v2("hello"); + assert(test(v2, v)); + } + { + using V = std::variant; + using V2 = std::variant; + V v; + makeEmpty(v); + V2 v2; + makeEmpty(v2); + assert(test(v, v2)); + } +#endif + } + + // See LLVM-31916 + void test_caller_accepts_nonconst() { + struct A {}; + struct Visitor { + void operator()(A&) {} + }; + std::variant v; + std::visit(Visitor{}, v); + } + + struct mobile_data { + int x; + + /* implicit */ mobile_data(int i) : x{i} {} + mobile_data(const mobile_data&) { + abort(); + } + mobile_data(mobile_data&&) { + abort(); + } + mobile_data& operator=(const mobile_data&) { + abort(); + return *this; + } + mobile_data& operator=(mobile_data&&) { + abort(); + return *this; + } + }; + + struct immobile_data : mobile_data { + using mobile_data::mobile_data; + immobile_data(const immobile_data&) = delete; + immobile_data& operator=(const immobile_data&) = delete; + }; + + void test_perfect_return() { + { + // Verify that a return object need not be copied/moved + using R = immobile_data; + assert(std::visit(std::identity{}, std::variant{13}).x == 13); + assert(std::visit(std::identity{}, std::variant{short{42}}).x == 42); + + // Verify that conversions to an object that can't be copied/moved are correctly handled + struct convertible_to_immobile_one { + operator immobile_data() const { + return immobile_data{1729}; + } + }; + + struct convertible_to_immobile_other { + operator immobile_data() const { + return immobile_data{1138}; + } + }; + + using VarTestConv = std::variant; + assert(std::visit(std::identity{}, VarTestConv{convertible_to_immobile_one{}}).x == 1729); + assert(std::visit(std::identity{}, VarTestConv{convertible_to_immobile_other{}}).x == 1138); + auto immobile_converter = [](auto src) -> immobile_data { return src; }; + assert(std::visit(immobile_converter, VarTestConv{convertible_to_immobile_one{}}).x == 1729); + assert(std::visit(immobile_converter, VarTestConv{convertible_to_immobile_other{}}).x == 1138); + } + { + // Verify that a returned object is not copied/moved/modified + using R = mobile_data; + assert(std::visit(std::identity{}, std::variant{13}).x == 13); + assert(std::visit(std::identity{}, std::variant{short{42}}).x == 42); + + // Verify that a returned reference is not copied/moved/modified + auto visitor1 = [x = R{1729}](auto) mutable -> R& { return x; }; + assert(std::visit(visitor1, std::variant{13}).x == 1729); + auto visitor2 = [x = R{1138}](auto) mutable -> R&& { return std::move(x); }; + assert(std::visit(visitor2, std::variant{13}).x == 1138); + } + } + + void test_immobile_function() { + { + // Validate that visit need not copy or move the visitor + using V = std::variant; + immobile_visitor visitor{}; + assert(std::visit(visitor, V{42}) == 42.0); + assert(std::visit(std::as_const(visitor), V{3.14}) == 3.14); + assert(std::visit(std::move(visitor), V{1729}) == 1729.0); + assert(std::visit(std::move(std::as_const(visitor)), V{1.414}) == 1.414); + } + { + // Validate that visit does not copy or move the visitor + using V = std::variant; + mobile_visitor visitor{}; + assert(std::visit(visitor, V{42}) == 42.0); + assert(std::visit(std::as_const(visitor), V{3.14}) == 3.14); + assert(std::visit(std::move(visitor), V{1729}) == 1729.0); + assert(std::visit(std::move(std::as_const(visitor)), V{1.414}) == 1.414); + } + } + + void run_test() { + test_call_operator_forwarding(); + test_call_operator_forwarding(); + test_call_operator_forwarding(); + test_argument_forwarding(); + test_argument_forwarding(); + test_argument_forwarding(); + test_constexpr(); + static_assert(test_constexpr()); + test_exceptions(); + test_caller_accepts_nonconst(); + test_perfect_return(); + test_immobile_function(); + } +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv + void run_test() {} +#endif // _HAS_CXX20 + } // namespace visit_R + + namespace visit_pointer_to_member { + struct base { + int x; + + int f() const { + return x; + } + }; + struct derived : base { + int y; + }; + + void run_test() { + using V = std::variant; + assert(std::visit(&base::x, V{base{13}}) == 13); + assert(std::visit(&base::x, V{derived{{42}, 29}}) == 42); + + assert(std::visit(&base::f, V{base{13}}) == 13); + assert(std::visit(&base::f, V{derived{{42}, 29}}) == 42); + } + } // namespace visit_pointer_to_member + + template + constexpr bool has_type = false; + template + constexpr bool has_type> = true; + + // Verify that `_Meta_at_<_Meta_list<>, size_t(-1)>` has no member named `type`, and that instantiating it doesn't + // consume the entire compiler heap. + static_assert(!has_type, static_cast(-1)>>); + + namespace vso468746 { + // Defend against regression of VSO-468746 + // "std::function's converting constructor/assignment should be unusable for performing copy/move assignments" + + void run_test() { + struct S { + std::variant> member; + }; + + static_assert( + sizeof(std::variant>) == sizeof(std::function) + alignof(std::function)); + static_assert(sizeof(S::member) == sizeof(std::variant>)); + static_assert(sizeof(S) >= sizeof(S::member)); + } + } // namespace vso468746 + + namespace vso492097 { + // Defend against regression of VSO-492097 + // The compiler was not correctly emitting constant data for variants constructed from the address of static + // duration variables with a constexpr operator& due to mishandling initialization of nested classes with + // anonymous union members. + template + struct wrap { + T val_; + constexpr T* operator&() { + return &val_; + } + }; + + void run_test() { + static wrap intVar = {42}; + static wrap doubleVar = {3.14}; + + using V = std::variant; + static constexpr V v1(&intVar); + static constexpr V v2(&doubleVar); + + static_assert(v1.index() == 0); + assert(*std::get<0>(v1) == 42); + static_assert(v2.index() == 1); + assert(*std::get<1>(v2) == 3.14); + } + } // namespace vso492097 + + namespace vso508126 { + void run_test() { + struct S {}; + static_assert(!std::is_copy_constructible_v); + static_assert(!std::is_copy_constructible_v>); + } + } // namespace vso508126 + + namespace DevCom1031281 { + // Compilers may warn when initializing a variant from a "weird" argument, e.g., std::variant{some_int} + // is potentially narrowing. Compilers should not, however, emit such diagnostics from the metaprogramming that + // determines which alternative a variant initialization would activate. We don't want to emit warnings when + // determining implicit conversion sequences early in overload resolution. + + void Overload(int) {} + void Overload(std::variant) {} + + void run_test() { + Overload(42); + } + } // namespace DevCom1031281 + + namespace gh2770 { + // Previous metaprogramming to validate the type requirements for std::visit required typelists too long for + // Clang. + struct S { + template + int operator()(T0, T1, T2, T3, T4) const { + return 1729; + } + }; + + void run_test() { + using V = std::variant; + assert(std::visit(S{}, V{'a'}, V{'b'}, V{10}, V{20L}, V{30LL}) == 1729); +#if _HAS_CXX20 + assert(std::visit(S{}, V{'a'}, V{'b'}, V{10}, V{20L}, V{30LL}) == 1729); +#endif // _HAS_CXX20 + } + } // namespace gh2770 + + namespace assign_cv { + template + struct TypeIdentityImpl { + using type = T; + }; + template + using TypeIdentity = typename TypeIdentityImpl::type; + + struct CvAssignable { + CvAssignable() = default; + CvAssignable(const CvAssignable&) = default; + CvAssignable(CvAssignable&&) = default; + CvAssignable& operator=(const CvAssignable&) = default; + CvAssignable& operator=(CvAssignable&&) = default; + + template + CvAssignable(const volatile TypeIdentity&) noexcept {} + template + CvAssignable(const volatile TypeIdentity&&) noexcept {} + + template + constexpr CvAssignable& operator=(const volatile TypeIdentity&) noexcept { + return *this; + } + template + constexpr CvAssignable& operator=(const volatile TypeIdentity&&) noexcept { + return *this; + } + + template + constexpr const volatile CvAssignable& operator=(const volatile TypeIdentity&) const volatile noexcept { + return *this; + } + template + constexpr const volatile CvAssignable& operator=(const volatile TypeIdentity&&) const volatile noexcept { + return *this; + } + }; + + void run_test() { + using std::swap; + { + std::variant oc{}; + oc.emplace<0>(0); + static_assert(!std::is_copy_assignable_v); + static_assert(!std::is_move_assignable_v); + static_assert(!std::is_swappable_v); + + std::variant ov{}; + std::variant ov2{}; + ov.emplace<0>(0); + swap(ov, ov); + ov = ov2; + ov = std::move(ov2); + + std::variant ocv{}; + ocv.emplace<0>(0); + static_assert(!std::is_copy_assignable_v); + static_assert(!std::is_move_assignable_v); + static_assert(!std::is_swappable_v); + } + { + std::variant oc{}; + std::variant oc2{}; + oc.emplace<0>(CvAssignable{}); + swap(oc, oc); + oc = oc2; + oc = std::move(oc2); + + std::variant ov{}; + std::variant ov2{}; + ov.emplace<0>(CvAssignable{}); + swap(ov, ov); + ov = ov2; + ov = std::move(ov2); + + std::variant ocv{}; + std::variant ocv2{}; + ocv.emplace<0>(CvAssignable{}); + swap(ocv, ocv); + ocv = ocv2; + ocv = std::move(ocv2); + } + } + } // namespace assign_cv +} // namespace msvc + +int main() { + msvc::big_variant::run_test(); + msvc::derived_variant::run_test(); + msvc::visit::run_test(); + msvc::visit_R::run_test(); + msvc::visit_pointer_to_member::run_test(); + + msvc::vso468746::run_test(); + msvc::vso508126::run_test(); + msvc::vso492097::run_test(); + msvc::DevCom1031281::run_test(); + msvc::gh2770::run_test(); + msvc::assign_cv::run_test(); +} diff --git a/tests/std/tests/P0220R1_any/test.cpp b/tests/std/tests/P0220R1_any/test.cpp index 3c89a23fe..971363bfb 100644 --- a/tests/std/tests/P0220R1_any/test.cpp +++ b/tests/std/tests/P0220R1_any/test.cpp @@ -4,7 +4,7 @@ // Organization of this file: // * a short header (including this comment) // * `// LLVM SOURCES BEGIN` -// * The contents of several libc++ `test/std/utilities/any` test files, each delimited by `// -- BEGIN/END: ` +// * The contents of several libc++ test files, each delimited by `// -- BEGIN/END: ` // comments. These contents have been modified to merge many tests into one by: // (1) changing `int main(int, char**)` to `int run_test()`, and // (2) wrapping everything other than comments and includes in a unique namespace per-file, using namespace nesting @@ -15,23 +15,17 @@ // the MSVC-specific test cases. // // The LLVM sources are updated manually: -// 1. Navigate a bash prompt to `libcxx` in an LLVM monorepo. -// 2. Redirect the output of the bash loop: -// for f in $(find test/std/utilities/any -name '*.pass.cpp'); -// do echo "// -- BEGIN: $f"; -// sed -e 's/int main(int, char\*\*)/int run_test()/; s/FIXME/TODO/g' < $f; -// echo -e "// -- END: $f\n"; -// done +// 1. Navigate a bash prompt to `llvm-project/libcxx`. +// 2. Redirect the output of: +// ../../tools/scripts/transform_llvm.sh test/std/utilities/any // into a file. // 3. Replicate the namespace structure from here into that file, use its content to replace everything between the // "LLVM SOURCES BEGIN"/"END" delimiters, and ensure that `main` properly calls each of the `run_test` functions. +// 4. Restore the TRANSITION-commented workarounds. // // Yes, this is an awkward hand process; notably the required headers can change without notice. We should investigate // running the libc++ tests directly in all of our configurations so we needn't replicate this subset of files. -#define _HAS_DEPRECATED_IS_LITERAL_TYPE 1 -#define _SILENCE_CXX17_IS_LITERAL_TYPE_DEPRECATION_WARNING -#define _SILENCE_CXX20_CISO646_REMOVED_WARNING #define _LIBCXX_IN_DEVCRT #include // Must precede any other libc++ headers #include @@ -41,6 +35,9 @@ #include "count_new.h" #pragma warning(pop) +// Silence a warning emitted by test/std/utilities/any/any.class/any.cons/default.pass.cpp below. +#pragma warning(disable : 4640) // construction of local static object is not thread-safe + // clang-format off // LLVM SOURCES BEGIN // -- BEGIN: test/std/utilities/any/any.class/any.assign/copy.pass.cpp @@ -53,7 +50,6 @@ //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_any_cast && !libcpp-no-exceptions // @@ -255,7 +251,6 @@ int run_test() { //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_any_cast && !libcpp-no-exceptions // @@ -364,7 +359,6 @@ int run_test() { //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_any_cast && !libcpp-no-exceptions // @@ -577,7 +571,6 @@ int run_test() { //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_any_cast && !libcpp-no-exceptions // @@ -681,6 +674,7 @@ int run_test() { //===----------------------------------------------------------------------===// + // // any() noexcept; @@ -702,15 +696,15 @@ int run_test() , "Must be default constructible" ); } +#ifndef _M_CEE // TRANSITION, VSO-1664382 { struct TestConstexpr : public std::any { constexpr TestConstexpr() : std::any() {} }; -#ifdef _LIBCPP_SAFE_STATIC - _LIBCPP_SAFE_STATIC static std::any a; - ((void)a); -#endif + static TEST_CONSTINIT std::any a; + (void)a; } +#endif // ^^^ no workaround ^^^ { DisableAllocationGuard g; ((void)g); const std::any a; @@ -732,7 +726,6 @@ int run_test() //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_any_cast && !libcpp-no-exceptions // @@ -755,7 +748,7 @@ int run_test() #include "test_macros.h" #include "test_convertible.h" -namespace ctor::in_place { +namespace ctor::in_place_type { template void test_in_place_type() { // constructing from a small type should perform no allocations. @@ -917,7 +910,7 @@ int run_test() { return 0; } -} // namespace ctor::in_place +} // namespace ctor::in_place_type // -- END: test/std/utilities/any/any.class/any.cons/in_place_type.pass.cpp // -- BEGIN: test/std/utilities/any/any.class/any.cons/move.pass.cpp @@ -930,7 +923,6 @@ int run_test() { //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_any_cast && !libcpp-no-exceptions // @@ -1034,7 +1026,6 @@ int run_test() //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_any_cast && !libcpp-no-exceptions // @@ -1192,7 +1183,6 @@ int run_test() { //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_any_cast && !libcpp-no-exceptions // @@ -1209,11 +1199,11 @@ int run_test() { namespace modifiers::emplace { struct Tracked { - static int count; - Tracked() {++count;} - Tracked(Tracked const&) noexcept {++count;} - Tracked& operator=(Tracked const&) = default; - ~Tracked() { --count; } + static int count; + Tracked() { ++count; } + Tracked(Tracked const&) noexcept { ++count; } + Tracked& operator=(Tracked const&) = default; + ~Tracked() { --count; } }; int Tracked::count = 0; @@ -1482,7 +1472,6 @@ int run_test() { //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_any_cast && !libcpp-no-exceptions // @@ -1546,7 +1535,6 @@ int run_test() //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_any_cast && !libcpp-no-exceptions // @@ -1681,6 +1669,7 @@ int run_test() //===----------------------------------------------------------------------===// + // // any::has_value() noexcept @@ -1694,7 +1683,6 @@ int run_test() namespace observers::has_value { int run_test() { - // noexcept test { std::any a; ASSERT_NOEXCEPT(a.has_value()); @@ -1748,7 +1736,8 @@ int run_test() //===----------------------------------------------------------------------===// -// XFAIL: libcpp-no-rtti + + // @@ -1787,34 +1776,6 @@ int run_test() } // namespace observers::type // -- END: test/std/utilities/any/any.class/any.observers/type.pass.cpp -// -- BEGIN: test/std/utilities/any/any.class/not_literal_type.pass.cpp -//===----------------------------------------------------------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// - - -// - -// [Note any is a not a literal type --end note] - -#include -#include - -#include "test_macros.h" - -namespace not_literal { -int run_test() { - static_assert(!std::is_literal_type::value, ""); - - return 0; -} -} // namespace not_literal -// -- END: test/std/utilities/any/any.class/not_literal_type.pass.cpp - // -- BEGIN: test/std/utilities/any/any.nonmembers/any.cast/any_cast_pointer.pass.cpp //===----------------------------------------------------------------------===// // @@ -1825,7 +1786,6 @@ int run_test() { //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_any_cast && !libcpp-no-exceptions // @@ -2011,7 +1971,6 @@ int run_test() { //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_any_cast && !libcpp-no-exceptions // @@ -2323,7 +2282,6 @@ int run_test() { //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_any_cast && !libcpp-no-exceptions // @@ -2466,7 +2424,6 @@ int run_test() { //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_any_cast && !libcpp-no-exceptions // @@ -3222,7 +3179,7 @@ int main() { ctor::copy::run_test(); ctor::default_::run_test(); - ctor::in_place::run_test(); + ctor::in_place_type::run_test(); ctor::move::run_test(); ctor::value::run_test(); @@ -3233,8 +3190,6 @@ int main() { observers::has_value::run_test(); observers::type::run_test(); - not_literal::run_test(); - nonmembers::cast::pointer::run_test(); nonmembers::cast::reference::run_test(); nonmembers::make_any::run_test(); diff --git a/tests/std/tests/P0220R1_optional/test.cpp b/tests/std/tests/P0220R1_optional/test.cpp index dab8e0b61..f3c38ba3d 100644 --- a/tests/std/tests/P0220R1_optional/test.cpp +++ b/tests/std/tests/P0220R1_optional/test.cpp @@ -15,26 +15,19 @@ // the MSVC-specific test cases. // // The LLVM sources are updated manually: -// 1. Navigate a bash prompt to `libcxx` in an LLVM monorepo. -// 2. Redirect the output of the bash loop: -// for f in $(find test/std/utilities/utility/utility.inplace test/std/utilities/optional -name '*.pass.cpp'); -// do echo "// -- BEGIN: $f"; -// sed -e 's/int main(int, char\*\*)/int run_test()/; s/FIXME/TODO/g' < $f; -// echo -e "// -- END: $f\n"; -// done +// 1. Navigate a bash prompt to `llvm-project/libcxx`. +// 2. Redirect the output of: +// ../../tools/scripts/transform_llvm.sh test/std/utilities/utility/utility.inplace test/std/utilities/optional // into a file. // 3. Replicate the namespace structure from here into that file, use its content to replace everything between the // "LLVM SOURCES BEGIN"/"END" delimiters, and ensure that `main` properly calls each of the `run_test` functions. -// 4. You'll need to fixup the specialization of std::hash in test/std/utilities/optional/optional.hash/hash.pass.cpp. +// 4. Fix the specialization of std::hash by closing/reopening namespaces and qualifying types. +// 5. Restore the TRANSITION-commented workarounds. +// 6. Restore the _HAS_CXX20 and _HAS_CXX23 guards. // // Yes, this is an awkward hand process; notably the required headers can change without notice. We should investigate // running the libc++ tests directly in all of our configurations so we needn't replicate this subset of files. -#define _HAS_DEPRECATED_IS_LITERAL_TYPE 1 -#define _HAS_DEPRECATED_RESULT_OF 1 -#define _SILENCE_CXX17_IS_LITERAL_TYPE_DEPRECATION_WARNING -#define _SILENCE_CXX17_RESULT_OF_DEPRECATION_WARNING -#define _SILENCE_CXX20_CISO646_REMOVED_WARNING #define _LIBCXX_IN_DEVCRT #include // Must precede any other libc++ headers #include @@ -51,6 +44,7 @@ //===----------------------------------------------------------------------===// + // // struct in_place_t { @@ -72,12 +66,10 @@ // template // inline constexpr in_place_index_t in_place_index{}; -#include #include #include - -#include "test_macros.h" -#include "type_id.h" +#include +#include namespace utility::in_place { template @@ -107,12 +99,12 @@ int run_test() { { using T1 = std::in_place_index_t<0>; using T2 = std::in_place_index_t<1>; - using T3 = std::in_place_index_t(-1)>; + using T3 = std::in_place_index_t(-1)>; static_assert(!std::is_same::value && !std::is_same::value); static_assert(!std::is_same::value); static_assert(check_tag(std::in_place_index<0>)); static_assert(check_tag(std::in_place_index<1>)); - static_assert(check_tag(std::in_place_index(-1)>)); + static_assert(check_tag(std::in_place_index(-1)>)); } return 0; @@ -129,11 +121,14 @@ int run_test() { // //===----------------------------------------------------------------------===// + + // optional #include #include + #if _HAS_CXX20 static_assert(!std::indirectly_readable >); static_assert(!std::indirectly_writable, int>); @@ -142,7 +137,7 @@ static_assert(!std::indirectly_movable, std::optional>); static_assert(!std::indirectly_movable_storable, std::optional>); static_assert(!std::indirectly_copyable, std::optional>); static_assert(!std::indirectly_copyable_storable, std::optional>); -#endif // _HAS_CXX20 +#endif // ^^^ _HAS_CXX20 ^^^ // -- END: test/std/utilities/optional/iterator_concept_conformance.compile.pass.cpp // -- BEGIN: test/std/utilities/optional/optional.bad_optional_access/default.pass.cpp @@ -155,7 +150,6 @@ static_assert(!std::indirectly_copyable_storable, std::option //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_optional_access // @@ -186,7 +180,7 @@ int run_test() // //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_optional_access + // @@ -210,6 +204,119 @@ int run_test() } // namespace bad_optional_access::derive // -- END: test/std/utilities/optional/optional.bad_optional_access/derive.pass.cpp +// -- BEGIN: test/std/utilities/optional/optional.comp_with_t/compare.three_way.pass.cpp +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + + + +// + +// [optional.comp.with.t], comparison with T + +// template +// requires (!is-derived-from-optional) && three_way_comparable_with +// constexpr compare_three_way_result_t +// operator<=>(const optional&, const U&); + +#include +#include +#include + +#include "test_comparisons.h" + +namespace comp_with_t::three_way { +#if _HAS_CXX20 +struct SomeInt { + int value_; + + constexpr explicit SomeInt(int value = 0) : value_(value) {} + + auto operator<=>(const SomeInt&) const = default; +}; + +template +concept HasSpaceship = requires(T t, U u) { t <=> u; }; + +// SFINAE tests. + +static_assert(std::three_way_comparable_with, std::optional>); +static_assert(HasSpaceship, std::optional>); + +static_assert(std::three_way_comparable_with, std::optional>); +static_assert(HasSpaceship, std::optional>); + +static_assert(!HasSpaceship, std::optional>); + +// Runtime and static tests. + +constexpr void test_custom_integral() { + { + SomeInt t{3}; + std::optional op{3}; + assert((t <=> op) == std::strong_ordering::equal); + assert(testOrder(t, op, std::strong_ordering::equal)); + } + { + SomeInt t{2}; + std::optional op{3}; + assert((t <=> op) == std::strong_ordering::less); + assert(testOrder(t, op, std::strong_ordering::less)); + } + { + SomeInt t{3}; + std::optional op{2}; + assert((t <=> op) == std::strong_ordering::greater); + assert(testOrder(t, op, std::strong_ordering::greater)); + } +} + +constexpr void test_int() { + { + int t{3}; + std::optional op{3}; + assert((t <=> op) == std::strong_ordering::equal); + assert(testOrder(t, op, std::strong_ordering::equal)); + } + { + int t{2}; + std::optional op{3}; + assert((t <=> op) == std::strong_ordering::less); + assert(testOrder(t, op, std::strong_ordering::less)); + } + { + int t{3}; + std::optional op{2}; + assert((t <=> op) == std::strong_ordering::greater); + assert(testOrder(t, op, std::strong_ordering::greater)); + } +} + +constexpr bool test() { + test_custom_integral(); + test_int(); + + return true; +} + +int run_test() { + assert(test()); + static_assert(test()); + return 0; +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv +int run_test() { + return 0; +} +#endif // ^^^ !_HAS_CXX20 ^^^ +} // namespace comp_with_t::three_way +// -- END: test/std/utilities/optional/optional.comp_with_t/compare.three_way.pass.cpp + // -- BEGIN: test/std/utilities/optional/optional.comp_with_t/equal.pass.cpp //===----------------------------------------------------------------------===// // @@ -219,6 +326,7 @@ int run_test() // //===----------------------------------------------------------------------===// + // // template constexpr bool operator==(const optional& x, const U& v); @@ -290,6 +398,7 @@ int run_test() { // //===----------------------------------------------------------------------===// + // // template constexpr bool operator>(const optional& x, const U& v); @@ -361,6 +470,7 @@ int run_test() { // //===----------------------------------------------------------------------===// + // // template constexpr bool operator>=(const optional& x, const U& v); @@ -434,6 +544,7 @@ int run_test() { // //===----------------------------------------------------------------------===// + // // template constexpr bool operator<=(const optional& x, const U& v); @@ -507,6 +618,7 @@ int run_test() { // //===----------------------------------------------------------------------===// + // // template constexpr bool operator<(const optional& x, const U& v); @@ -578,6 +690,7 @@ int run_test() { // //===----------------------------------------------------------------------===// + // // template constexpr bool operator!=(const optional& x, const U& v); @@ -650,6 +763,7 @@ int run_test() { //===----------------------------------------------------------------------===// + // // Test that provides all of the arithmetic, enum, and pointer @@ -662,7 +776,7 @@ int run_test() { #include "test_macros.h" -namespace enabled_hash { +namespace hash::enabled_hash { int run_test() { test_library_hash_specializations_available(); { @@ -671,7 +785,7 @@ int run_test() { return 0; } -} // namespace enabled_hash +} // namespace hash::enabled_hash // -- END: test/std/utilities/optional/optional.hash/enabled_hash.pass.cpp // -- BEGIN: test/std/utilities/optional/optional.hash/hash.pass.cpp @@ -683,6 +797,7 @@ int run_test() { // //===----------------------------------------------------------------------===// + // // template struct hash>; @@ -705,7 +820,7 @@ namespace std { template <> struct hash<::hash::B> { - size_t operator()(::hash::B const&) noexcept(false) { return 0; } + std::size_t operator()(::hash::B const&) noexcept(false) { return 0; } }; } @@ -751,7 +866,7 @@ int run_test() test_hash_enabled_for_type >(); test_hash_enabled_for_type >(); test_hash_enabled_for_type >(); -#endif // TRANSITION, DevCom-10107834 +#endif // ^^^ no workaround ^^^ test_hash_disabled_for_type>(); test_hash_disabled_for_type>(); @@ -759,7 +874,7 @@ int run_test() #ifndef __EDG__ // TRANSITION, DevCom-10107834 test_hash_enabled_for_type>(); test_hash_enabled_for_type>(); -#endif // TRANSITION, DevCom-10107834 +#endif // ^^^ no workaround ^^^ } return 0; @@ -776,8 +891,7 @@ int run_test() // //===----------------------------------------------------------------------===// -// Throwing bad_optional_access is supported starting in macosx10.13 -// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12}} && !no-exceptions + // @@ -790,7 +904,8 @@ int run_test() #include #include "test_macros.h" -namespace and_then { + +namespace monadic::and_then { #if _HAS_CXX23 struct LVal { constexpr std::optional operator()(int&) { return 1; } @@ -1039,7 +1154,7 @@ int run_test() { return 0; } #endif // ^^^ !_HAS_CXX23 ^^^ -} // namespace and_then +} // namespace monadic::and_then // -- END: test/std/utilities/optional/optional.monadic/and_then.pass.cpp // -- BEGIN: test/std/utilities/optional/optional.monadic/or_else.pass.cpp @@ -1051,6 +1166,7 @@ int run_test() { // //===----------------------------------------------------------------------===// + // // template constexpr optional or_else(F&&) &&; @@ -1061,7 +1177,7 @@ int run_test() { #include #include -namespace or_else { +namespace monadic::or_else { #if _HAS_CXX23 struct NonMovable { NonMovable() = default; @@ -1122,8 +1238,9 @@ int run_test() { return 0; } #endif // ^^^ !_HAS_CXX23 ^^^ -} // namespace or_else +} // namespace monadic::or_else // -- END: test/std/utilities/optional/optional.monadic/or_else.pass.cpp + // -- BEGIN: test/std/utilities/optional/optional.monadic/transform.pass.cpp //===----------------------------------------------------------------------===// // @@ -1133,8 +1250,7 @@ int run_test() { // //===----------------------------------------------------------------------===// -// Throwing bad_optional_access is supported starting in macosx10.13 -// XFAIL: use_system_cxx_lib && target={{.+}}-apple-macosx10.{{9|10|11|12}} && !no-exceptions + // @@ -1148,7 +1264,7 @@ int run_test() { #include #include -namespace transform { +namespace monadic::transform { #if _HAS_CXX23 struct LVal { constexpr int operator()(int&) { return 1; } @@ -1340,9 +1456,70 @@ int run_test() { return 0; } #endif // ^^^ !_HAS_CXX23 ^^^ -} // namespace transform +} // namespace monadic::transform // -- END: test/std/utilities/optional/optional.monadic/transform.pass.cpp +// -- BEGIN: test/std/utilities/optional/optional.nullops/compare.three_way.pass.cpp +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + + + +// + +// [optional.nullops], comparison with nullopt + +// template +// constexpr strong_ordering operator<=>(const optional&, nullopt_t) noexcept; + +#include +#include +#include + +#include "test_comparisons.h" + +namespace nullops::three_way { +#if _HAS_CXX20 +constexpr bool test() { + { + std::optional op; + assert((std::nullopt <=> op) == std::strong_ordering::equal); + assert(testOrder(std::nullopt, op, std::strong_ordering::equal)); + assert((op <=> std::nullopt) == std::strong_ordering::equal); + assert(testOrder(op, std::nullopt, std::strong_ordering::equal)); + } + { + std::optional op{1}; + assert((std::nullopt <=> op) == std::strong_ordering::less); + assert(testOrder(std::nullopt, op, std::strong_ordering::less)); + } + { + std::optional op{1}; + assert((op <=> std::nullopt) == std::strong_ordering::greater); + assert(testOrder(op, std::nullopt, std::strong_ordering::greater)); + } + + return true; +} + +int run_test() { + assert(test()); + static_assert(test()); + return 0; +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv +int run_test() { + return 0; +} +#endif // ^^^ !_HAS_CXX20 ^^^ +} // namespace nullops::three_way +// -- END: test/std/utilities/optional/optional.nullops/compare.three_way.pass.cpp + // -- BEGIN: test/std/utilities/optional/optional.nullops/equal.pass.cpp //===----------------------------------------------------------------------===// // @@ -1352,6 +1529,7 @@ int run_test() { // //===----------------------------------------------------------------------===// + // // template constexpr bool operator==(const optional& x, nullopt_t) noexcept; @@ -1398,6 +1576,7 @@ int run_test() // //===----------------------------------------------------------------------===// + // // template constexpr bool operator>(const optional& x, nullopt_t) noexcept; @@ -1444,6 +1623,7 @@ int run_test() // //===----------------------------------------------------------------------===// + // // template constexpr bool operator>=(const optional& x, nullopt_t) noexcept; @@ -1491,6 +1671,7 @@ int run_test() //===----------------------------------------------------------------------===// + // // template constexpr bool operator<=(const optional& x, nullopt_t) noexcept; @@ -1537,6 +1718,7 @@ int run_test() // //===----------------------------------------------------------------------===// + // // template constexpr bool operator<(const optional& x, nullopt_t) noexcept; @@ -1583,6 +1765,7 @@ int run_test() // //===----------------------------------------------------------------------===// + // // template constexpr bool operator!=(const optional& x, nullopt_t) noexcept; @@ -1629,6 +1812,7 @@ int run_test() // //===----------------------------------------------------------------------===// + // // struct nullopt_t{see below}; @@ -1649,8 +1833,8 @@ using std::nullopt; constexpr bool test() { - nullopt_t meow{nullopt}; - (void)meow; + nullopt_t foo{nullopt}; + (void)foo; return true; } @@ -1676,6 +1860,7 @@ int run_test() // //===----------------------------------------------------------------------===// + // // template optional& operator=(U&& v); @@ -1965,6 +2150,7 @@ int run_test() // //===----------------------------------------------------------------------===// + // // From LWG2451: @@ -2224,9 +2410,10 @@ int run_test() // //===----------------------------------------------------------------------===// + // -// optional& operator=(const optional& rhs); // constexpr in C++20 +// constexpr optional& operator=(const optional& rhs); #include #include @@ -2271,19 +2458,15 @@ int run_test() { { using O = optional; -#if TEST_STD_VER > 17 - LIBCPP_STATIC_ASSERT(assign_empty(O{42}), ""); - LIBCPP_STATIC_ASSERT(assign_value(O{42}), ""); -#endif + static_assert(assign_empty(O{42})); + static_assert(assign_value(O{42})); assert(assign_empty(O{42})); assert(assign_value(O{42})); } { using O = optional; -#if TEST_STD_VER > 17 - LIBCPP_STATIC_ASSERT(assign_empty(O{42}), ""); - LIBCPP_STATIC_ASSERT(assign_value(O{42}), ""); -#endif + static_assert(assign_empty(O{42})); + static_assert(assign_value(O{42})); assert(assign_empty(O{42})); assert(assign_value(O{42})); } @@ -2336,6 +2519,7 @@ int run_test() // //===----------------------------------------------------------------------===// + // // template T& optional::emplace(Args&&... args); @@ -2540,16 +2724,15 @@ void test_on_test_type() { } } -TEST_CONSTEXPR_CXX20 bool test_empty_emplace() -{ - optional opt; - auto &v = opt.emplace(42); - static_assert( std::is_same_v, "" ); - assert(*opt == 42); - assert( v == 42); - opt.emplace(); - assert(*opt == 0); - return true; +TEST_CONSTEXPR_CXX20 bool test_empty_emplace() { + optional opt; + auto& v = opt.emplace(42); + static_assert(std::is_same_v, ""); + assert(*opt == 42); + assert(v == 42); + opt.emplace(); + assert(*opt == 0); + return true; } int run_test() @@ -2591,6 +2774,9 @@ int run_test() { using T = ExplicitTrivialTestTypes::TestType; test_multi_arg(); +#if TEST_STD_VER > 17 + static_assert(test_multi_arg()); +#endif } { test_empty_emplace(); @@ -2634,6 +2820,7 @@ int run_test() // //===----------------------------------------------------------------------===// + // // template @@ -2782,11 +2969,12 @@ int run_test() // //===----------------------------------------------------------------------===// + // -// optional& operator=(optional&& rhs) +// constexpr optional& operator=(optional&& rhs) // noexcept(is_nothrow_move_assignable::value && -// is_nothrow_move_constructible::value); // constexpr in C++20 +// is_nothrow_move_constructible::value); #include #include @@ -2890,19 +3078,15 @@ int run_test() } { using O = optional; -#if TEST_STD_VER > 17 - LIBCPP_STATIC_ASSERT(assign_empty(O{42}), ""); - LIBCPP_STATIC_ASSERT(assign_value(O{42}), ""); -#endif + static_assert(assign_empty(O{42})); + static_assert(assign_value(O{42})); assert(assign_empty(O{42})); assert(assign_value(O{42})); } { using O = optional; -#if TEST_STD_VER > 17 - LIBCPP_STATIC_ASSERT(assign_empty(O{42}), ""); - LIBCPP_STATIC_ASSERT(assign_value(O{42}), ""); -#endif + static_assert(assign_empty(O{42})); + static_assert(assign_value(O{42})); assert(assign_empty(O{42})); assert(assign_value(O{42})); } @@ -2994,6 +3178,7 @@ int run_test() // //===----------------------------------------------------------------------===// + // // optional& operator=(nullopt_t) noexcept; @@ -3102,6 +3287,7 @@ int run_test() // //===----------------------------------------------------------------------===// + // // From LWG2451: @@ -3444,6 +3630,7 @@ int run_test() // //===----------------------------------------------------------------------===// + // // template @@ -3515,7 +3702,6 @@ public: friend bool operator==(const Z& x, const Z& y) {return x.i_ == y.i_;} }; - template constexpr bool test_all() { @@ -3570,7 +3756,6 @@ int run_test() //===----------------------------------------------------------------------===// // -// XFAIL: dylib-has-no-bad_optional_access && !libcpp-no-exceptions // @@ -3704,6 +3889,7 @@ int run_test() // //===----------------------------------------------------------------------===// + // // constexpr optional(const optional& rhs); @@ -3868,6 +4054,15 @@ int run_test() static_assert( *o2 == 4, "" ); } + // LWG3836 https://wg21.link/LWG3836 + // std::optional conversion constructor optional(const optional&) + // should take precedence over optional(U&&) with operator bool + { + std::optional o1(false); + std::optional o2(o1); + assert(!o2.value()); + } + return 0; } } // namespace ctor::copy @@ -3883,8 +4078,6 @@ int run_test() //===----------------------------------------------------------------------===// // -// Clang 5 will generate bad implicit deduction guides -// Specifically, for the copy constructor. // template @@ -3967,6 +4160,7 @@ int run_test() // //===----------------------------------------------------------------------===// + // // constexpr optional() noexcept; @@ -4053,6 +4247,7 @@ int run_test() // //===----------------------------------------------------------------------===// + // // constexpr optional(in_place_t); @@ -4098,6 +4293,7 @@ int run_test() { // //===----------------------------------------------------------------------===// + // // template @@ -4220,6 +4416,7 @@ int run_test() // //===----------------------------------------------------------------------===// + // // template @@ -4318,6 +4515,7 @@ int run_test() // //===----------------------------------------------------------------------===// + // // template @@ -4326,6 +4524,7 @@ int run_test() #include #include +#include #include #include @@ -4440,6 +4639,7 @@ int run_test() //===----------------------------------------------------------------------===// // + // // template @@ -4593,7 +4793,6 @@ int run_test() //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_optional_access && !libcpp-no-exceptions // @@ -4820,6 +5019,7 @@ int run_test() // //===----------------------------------------------------------------------===// + // // constexpr optional(nullopt_t) noexcept; @@ -4900,6 +5100,7 @@ int run_test() // //===----------------------------------------------------------------------===// + // // template @@ -4949,8 +5150,6 @@ public: friend constexpr bool operator==(const X& x, const X& y) {return x.i_ == y.i_;} }; -int count = 0; - struct Z { Z(int) { TEST_THROW(6); } @@ -5004,7 +5203,6 @@ int run_test() //===----------------------------------------------------------------------===// // -// XFAIL: dylib-has-no-bad_optional_access && !libcpp-no-exceptions // @@ -5159,7 +5357,6 @@ int run_test() //===----------------------------------------------------------------------===// // -// XFAIL: dylib-has-no-bad_optional_access && !libcpp-no-exceptions // @@ -5317,6 +5514,7 @@ int run_test() { // //===----------------------------------------------------------------------===// + // // ~optional(); @@ -5391,6 +5589,7 @@ int run_test() //===----------------------------------------------------------------------===// + // // void reset() noexcept; @@ -5415,20 +5614,19 @@ struct X bool X::dtor_called = false; -TEST_CONSTEXPR_CXX20 bool check_reset() -{ - { - optional opt; - static_assert(noexcept(opt.reset()) == true, ""); - opt.reset(); - assert(static_cast(opt) == false); - } - { - optional opt(3); - opt.reset(); - assert(static_cast(opt) == false); - } - return true; +TEST_CONSTEXPR_CXX20 bool check_reset() { + { + optional opt; + static_assert(noexcept(opt.reset()) == true, ""); + opt.reset(); + assert(static_cast(opt) == false); + } + { + optional opt(3); + opt.reset(); + assert(static_cast(opt) == false); + } + return true; } int run_test() @@ -5468,6 +5666,7 @@ int run_test() // //===----------------------------------------------------------------------===// + // // constexpr explicit optional::operator bool() const noexcept; @@ -5510,14 +5709,11 @@ int run_test() // //===----------------------------------------------------------------------===// + // // constexpr T& optional::operator*() &; -#ifdef _LIBCPP_DEBUG -#define _LIBCPP_ASSERT(x, m) ((x) ? (void)0 : std::exit(0)) -#endif - #include #include #include @@ -5568,7 +5764,7 @@ int run_test() } static_assert(test() == 7, ""); - return 0; + return 0; } } // namespace observe::deref // -- END: test/std/utilities/optional/optional.object/optional.object.observe/dereference.pass.cpp @@ -5582,14 +5778,11 @@ int run_test() // //===----------------------------------------------------------------------===// + // // constexpr const T& optional::operator*() const &; -#ifdef _LIBCPP_DEBUG -#define _LIBCPP_ASSERT(x, m) ((x) ? (void)0 : std::exit(0)) -#endif - #include #include #include @@ -5636,7 +5829,7 @@ int run_test() assert((*opt).test() == 2); } - return 0; + return 0; } } // namespace observe::deref_const // -- END: test/std/utilities/optional/optional.object/optional.object.observe/dereference_const.pass.cpp @@ -5650,14 +5843,11 @@ int run_test() // //===----------------------------------------------------------------------===// + // // constexpr T&& optional::operator*() const &&; -#ifdef _LIBCPP_DEBUG -#define _LIBCPP_ASSERT(x, m) ((x) ? (void)0 : std::exit(0)) -#endif - #include #include #include @@ -5704,7 +5894,7 @@ int run_test() assert((*std::move(opt)).test() == 2); } - return 0; + return 0; } } // namespace observe::deref_const_rvalue // -- END: test/std/utilities/optional/optional.object/optional.object.observe/dereference_const_rvalue.pass.cpp @@ -5718,14 +5908,11 @@ int run_test() // //===----------------------------------------------------------------------===// + // // constexpr T&& optional::operator*() &&; -#ifdef _LIBCPP_DEBUG -#define _LIBCPP_ASSERT(x, m) ((x) ? (void)0 : std::exit(0)) -#endif - #include #include #include @@ -5776,7 +5963,7 @@ int run_test() } static_assert(test() == 7, ""); - return 0; + return 0; } } // namespace observe::deref_rvalue // -- END: test/std/utilities/optional/optional.object/optional.object.observe/dereference_rvalue.pass.cpp @@ -5790,6 +5977,7 @@ int run_test() // //===----------------------------------------------------------------------===// + // // constexpr bool optional::has_value() const noexcept; @@ -5832,14 +6020,11 @@ int run_test() // //===----------------------------------------------------------------------===// + // // constexpr T* optional::operator->(); -#ifdef _LIBCPP_DEBUG -#define _LIBCPP_ASSERT(x, m) ((x) ? (void)0 : std::exit(0)) -#endif - #include #include #include @@ -5888,7 +6073,7 @@ int run_test() static_assert(test() == 3, ""); } - return 0; + return 0; } } // namespace observe::op_arrow // -- END: test/std/utilities/optional/optional.object/optional.object.observe/op_arrow.pass.cpp @@ -5902,14 +6087,11 @@ int run_test() // //===----------------------------------------------------------------------===// + // // constexpr const T* optional::operator->() const; -#ifdef _LIBCPP_DEBUG -#define _LIBCPP_ASSERT(x, m) ((x) ? (void)0 : std::exit(0)) -#endif - #include #include #include @@ -5962,7 +6144,7 @@ int run_test() static_assert(opt->test() == 1, ""); } - return 0; + return 0; } } // namespace observe::op_arrow_const // -- END: test/std/utilities/optional/optional.object/optional.object.observe/op_arrow_const.pass.cpp @@ -5977,7 +6159,6 @@ int run_test() //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_optional_access && !libcpp-no-exceptions // @@ -6058,7 +6239,6 @@ int run_test() //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_optional_access && !libcpp-no-exceptions // @@ -6130,7 +6310,6 @@ int run_test() //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_optional_access && !libcpp-no-exceptions // @@ -6201,6 +6380,7 @@ int run_test() // //===----------------------------------------------------------------------===// + // // template constexpr T optional::value_or(U&& v) &&; @@ -6280,6 +6460,7 @@ int run_test() // //===----------------------------------------------------------------------===// + // // template constexpr T optional::value_or(U&& v) const&; @@ -6364,9 +6545,8 @@ int run_test() // //===----------------------------------------------------------------------===// -// -// XFAIL: dylib-has-no-bad_optional_access && !libcpp-no-exceptions +// // constexpr T& optional::value() &&; @@ -6443,6 +6623,7 @@ int run_test() // //===----------------------------------------------------------------------===// + // // void swap(optional&) @@ -6776,6 +6957,7 @@ int run_test() //===----------------------------------------------------------------------===// + // // Make sure we properly generate special member functions for optional @@ -6845,6 +7027,7 @@ int run_test() { //===----------------------------------------------------------------------===// + // // The following special member functions should propagate the triviality of @@ -6947,6 +7130,7 @@ int run_test() { // //===----------------------------------------------------------------------===// + // // template @@ -6983,6 +7167,86 @@ int run_test() } // namespace types // -- END: test/std/utilities/optional/optional.object/types.pass.cpp +// -- BEGIN: test/std/utilities/optional/optional.relops/compare.three_way.pass.cpp +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + + + +// + +// [optional.relops], relational operators + +// template U> +// constexpr compare_three_way_result_t +// operator<=>(const optional&, const optional&); + +#include +#include +#include + +#include "test_comparisons.h" + +namespace relops::three_way { +#if _HAS_CXX20 +constexpr bool test() { + { + std::optional op1; + std::optional op2; + + assert((op1 <=> op2) == std::strong_ordering::equal); + assert(testOrder(op1, op2, std::strong_ordering::equal)); + } + { + std::optional op1{3}; + std::optional op2{3}; + assert((op1 <=> op1) == std::strong_ordering::equal); + assert(testOrder(op1, op1, std::strong_ordering::equal)); + assert((op1 <=> op2) == std::strong_ordering::equal); + assert(testOrder(op1, op2, std::strong_ordering::equal)); + assert((op2 <=> op1) == std::strong_ordering::equal); + assert(testOrder(op2, op1, std::strong_ordering::equal)); + } + { + std::optional op; + std::optional op1{2}; + std::optional op2{3}; + assert((op <=> op2) == std::strong_ordering::less); + assert(testOrder(op, op2, std::strong_ordering::less)); + assert((op1 <=> op2) == std::strong_ordering::less); + assert(testOrder(op1, op2, std::strong_ordering::less)); + } + { + std::optional op; + std::optional op1{3}; + std::optional op2{2}; + assert((op1 <=> op) == std::strong_ordering::greater); + assert(testOrder(op1, op, std::strong_ordering::greater)); + assert((op1 <=> op2) == std::strong_ordering::greater); + assert(testOrder(op1, op2, std::strong_ordering::greater)); + } + + return true; +} + +int run_test() { + assert(test()); + static_assert(test()); + return 0; +} +#else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv +int run_test() { + return 0; +} +#endif // ^^^ !_HAS_CXX20 ^^^ +} // namespace relops::three_way +// -- END: test/std/utilities/optional/optional.relops/compare.three_way.pass.cpp + // -- BEGIN: test/std/utilities/optional/optional.relops/equal.pass.cpp //===----------------------------------------------------------------------===// // @@ -6992,6 +7256,7 @@ int run_test() // //===----------------------------------------------------------------------===// + // // template constexpr bool operator==(const optional& x, const optional& y); @@ -7085,6 +7350,7 @@ int run_test() { // //===----------------------------------------------------------------------===// + // // template constexpr bool operator>= (const optional& x, const optional& y); @@ -7175,6 +7441,7 @@ int run_test() { // //===----------------------------------------------------------------------===// + // // template constexpr bool operator> (const optional& x, const optional& y); @@ -7263,6 +7530,7 @@ int run_test() { // //===----------------------------------------------------------------------===// + // // template constexpr bool operator<= (const optional& x, const optional& y); @@ -7353,6 +7621,7 @@ int run_test() { // //===----------------------------------------------------------------------===// + // // template constexpr bool operator< (const optional& x, const optional& y); @@ -7441,6 +7710,7 @@ int run_test() { // //===----------------------------------------------------------------------===// + // // template constexpr bool operator!=(const optional& x, const optional& y); @@ -7534,7 +7804,7 @@ int run_test() { // //===----------------------------------------------------------------------===// -// XFAIL: dylib-has-no-bad_optional_access && !libcpp-no-exceptions + // // @@ -7595,6 +7865,7 @@ int run_test() // //===----------------------------------------------------------------------===// + // // template @@ -7644,6 +7915,7 @@ int run_test() // //===----------------------------------------------------------------------===// + // // template @@ -7713,6 +7985,7 @@ int run_test() // //===----------------------------------------------------------------------===// + // // template void swap(optional& x, optional& y) @@ -8070,6 +8343,7 @@ int run_test() // //===----------------------------------------------------------------------===// + // // #include @@ -8078,7 +8352,7 @@ int run_test() #include "test_macros.h" -namespace init_list { +namespace optional_includes_initializer_list { int run_test() { using std::optional; @@ -8088,9 +8362,8 @@ int run_test() return 0; } -} // namespace init_list +} // namespace optional_includes_initializer_list // -- END: test/std/utilities/optional/optional.syn/optional_includes_initializer_list.pass.cpp - // LLVM SOURCES END // clang-format on @@ -8353,51 +8626,54 @@ int main() { bad_optional_access::default_::run_test(); bad_optional_access::derive::run_test(); + comp_with_t::three_way::run_test(); comp_with_t::equal::run_test(); - comp_with_t::not_equal::run_test(); - comp_with_t::less_than::run_test(); - comp_with_t::less_equal::run_test(); comp_with_t::greater_than::run_test(); comp_with_t::greater_equal::run_test(); + comp_with_t::less_equal::run_test(); + comp_with_t::less_than::run_test(); + comp_with_t::not_equal::run_test(); - enabled_hash::run_test(); + hash::enabled_hash::run_test(); hash::run_test(); - and_then::run_test(); - or_else::run_test(); - transform::run_test(); + monadic::and_then::run_test(); + monadic::or_else::run_test(); + monadic::transform::run_test(); + nullops::three_way::run_test(); nullops::equal::run_test(); - nullops::not_equal::run_test(); - nullops::less_than::run_test(); - nullops::less_equal::run_test(); nullops::greater_than::run_test(); nullops::greater_equal::run_test(); + nullops::less_equal::run_test(); + nullops::less_than::run_test(); + nullops::not_equal::run_test(); nullopt::run_test(); - assign::nullopt::run_test(); - assign::copy::run_test(); - assign::move::run_test(); assign::value::run_test(); - assign::optional_U::run_test(); + assign::const_optional_U::run_test(); + assign::copy::run_test(); assign::emplace::run_test(); assign::emplace_initializer_list::run_test(); + assign::move::run_test(); + assign::nullopt::run_test(); + assign::optional_U::run_test(); ctor::const_optional_U::run_test(); ctor::const_T::run_test(); + ctor::copy::run_test(); ctor::deduct::run_test(); ctor::default_::run_test(); ctor::clobber::run_test(); - ctor::explicit_optional_U::run_test(); ctor::explicit_const_optional_U::run_test(); - ctor::nullopt::run_test(); - ctor::copy::run_test(); - ctor::move::run_test(); - ctor::rvalue_T::run_test(); - ctor::in_place::run_test(); + ctor::explicit_optional_U::run_test(); ctor::initializer_list::run_test(); + ctor::in_place::run_test(); + ctor::move::run_test(); + ctor::nullopt::run_test(); ctor::optional_U::run_test(); + ctor::rvalue_T::run_test(); ctor::U::run_test(); dtor::run_test(); @@ -8405,22 +8681,19 @@ int main() { modifiers::reset::run_test(); observe::op_bool::run_test(); - observe::deref::run_test(); observe::deref_const::run_test(); observe::deref_const_rvalue::run_test(); observe::deref_rvalue::run_test(); - observe::has_value::run_test(); observe::op_arrow::run_test(); observe::op_arrow_const::run_test(); observe::value::run_test(); - observe::value_const::run_test(); - observe::value_rvalue::run_test(); observe::value_const_rvalue::run_test(); observe::value_or::run_test(); observe::value_or_const::run_test(); + observe::value_rvalue::run_test(); member_swap::run_test(); @@ -8430,18 +8703,21 @@ int main() { types::run_test(); + relops::three_way::run_test(); relops::equal::run_test(); - relops::not_equal::run_test(); - relops::less_than::run_test(); + relops::greater_equal::run_test(); relops::greater_than::run_test(); relops::less_equal::run_test(); - relops::greater_equal::run_test(); + relops::less_than::run_test(); + relops::not_equal::run_test(); nonmembers::make_optional::run_test(); nonmembers::make_optional_explicit::run_test(); nonmembers::make_optional_explicit_init_list::run_test(); nonmembers::swap_::run_test(); + optional_includes_initializer_list::run_test(); + msvc::lwg3836::run_test(); msvc::vso406124::run_test(); diff --git a/tests/std/tests/P0608R3_improved_variant_converting_constructor/env.lst b/tests/std/tests/P0608R3_improved_variant_converting_constructor/env.lst index 351a8293d..2de7aab29 100644 --- a/tests/std/tests/P0608R3_improved_variant_converting_constructor/env.lst +++ b/tests/std/tests/P0608R3_improved_variant_converting_constructor/env.lst @@ -1,4 +1,4 @@ # Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -RUNALL_INCLUDE ..\usual_20_matrix.lst +RUNALL_INCLUDE ..\usual_17_matrix.lst diff --git a/tests/std/tests/usual_17_matrix.lst b/tests/std/tests/usual_17_matrix.lst index 13bb00d66..d5ec4eda0 100644 --- a/tests/std/tests/usual_17_matrix.lst +++ b/tests/std/tests/usual_17_matrix.lst @@ -1,8 +1,6 @@ # Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -# When updating this file, also update tests\P0088R3_variant\env.lst to match - RUNALL_INCLUDE .\prefix.lst RUNALL_CROSSLIST * PM_CL="/w14640 /Zc:threadSafeInit-" diff --git a/tools/scripts/transform_llvm.sh b/tools/scripts/transform_llvm.sh new file mode 100644 index 000000000..ead3b5360 --- /dev/null +++ b/tools/scripts/transform_llvm.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +# This script is used to regenerate the P0220R1_any, P0220R1_optional, and P0088R3_variant tests. + +SED_SCRIPTS='s/int main\(int, char\*\*\)/int run_test()/;' +SED_SCRIPTS+='s/FI[X]ME/TODO/g;' +SED_SCRIPTS+='s@// (REQUIRES|UNSUPPORTED|XFAIL):.*$@@;' + +for f in $(find $* -name '*.pass.cpp' -not -name '*nothing_to_do*'); +do + echo "// -- BEGIN: $f"; + sed -E -e "$SED_SCRIPTS" < $f; + echo -e "// -- END: $f\n"; +done