From 87152f41a1a3c4f7a749bb9d9db408c75f9ab0a1 Mon Sep 17 00:00:00 2001 From: "Michael S. Rizkalla" Date: Thu, 18 Feb 2021 02:53:52 +0000 Subject: [PATCH] : P0608R3 Improving variant's converting constructor/assignment (#1629) Co-authored-by: Casey Carter Co-authored-by: Igor Zhukov <4289847+fsb4000@users.noreply.github.com> Co-authored-by: S. B. Tam <8998201+cpplearner@users.noreply.github.com> Co-authored-by: Stephan T. Lavavej --- stl/inc/variant | 30 +- stl/inc/yvals_core.h | 1 + tests/std/test.lst | 1 + tests/std/tests/P0088R3_variant/env.lst | 10 +- tests/std/tests/P0088R3_variant/test.cpp | 57 ++-- .../env.lst | 4 + .../test.cpp | 263 ++++++++++++++++++ 7 files changed, 338 insertions(+), 28 deletions(-) create mode 100644 tests/std/tests/P0608R3_improved_variant_converting_constructor/env.lst create mode 100644 tests/std/tests/P0608R3_improved_variant_converting_constructor/test.cpp diff --git a/stl/inc/variant b/stl/inc/variant index bccf5add82..339410bfd7 100644 --- a/stl/inc/variant +++ b/stl/inc/variant @@ -967,10 +967,25 @@ using _Variant_destroy_layer = conditional_t + +#if _HAS_CXX20 +// build Ti x[] = {std::forward(t)}; +template +auto _Construct_array(_TargetType(&&)[1]) -> _Meta_list, _TargetType>; + +template +using _Variant_type_resolver = decltype(_Construct_array<_Idx, _TargetType>({_STD declval<_InitializerType>()})); +#endif // _HAS_CXX20 + +template struct _Variant_init_single_overload { - using _FTy = _Meta_list, _Ty> (*)(_Ty); - operator _FTy(); +#if _HAS_CXX20 + template + auto operator()(_TargetType, _InitializerType&&) -> _Variant_type_resolver<_Idx, _TargetType, _InitializerType>; +#else // _HAS_CXX20 + template + auto operator()(_TargetType, _InitializerType&&) -> _Meta_list, _TargetType>; +#endif // _HAS_CXX20 }; template @@ -978,7 +993,9 @@ struct _Variant_init_overload_set_; template struct _Variant_init_overload_set_, _Types...> - : _Variant_init_single_overload<_Indices, _Types>... {}; + : _Variant_init_single_overload<_Indices, _Types>... { + using _Variant_init_single_overload<_Indices, _Types>::operator()...; +}; template using _Variant_init_overload_set = _Variant_init_overload_set_, _Types...>; @@ -987,11 +1004,12 @@ template struct _Variant_init_helper {}; // failure case (has no member "type") template -struct _Variant_init_helper{}(_STD declval<_Ty>()))>, _Ty, +struct _Variant_init_helper< + void_t{}(_STD declval<_Ty>(), _STD declval<_Ty>()))>, _Ty, _Types...> { // perform overload resolution to determine the unique alternative that should be initialized in // variant<_Types...> from an argument expression with type and value category _Ty - using type = decltype(_Variant_init_overload_set<_Types...>{}(_STD declval<_Ty>())); + using type = decltype(_Variant_init_overload_set<_Types...>{}(_STD declval<_Ty>(), _STD declval<_Ty>())); }; template // extract the type from _Variant_init_helper diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 2e8d22b23d..f0591e023b 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -159,6 +159,7 @@ // P0556R3 Integral Power-Of-2 Operations (renamed by P1956R1) // P0586R2 Integer Comparison Functions // P0595R2 is_constant_evaluated() +// P0608R3 Improving variant's Converting Constructor/Assignment // P0616R0 Using move() In // P0631R8 Math Constants // P0646R1 list/forward_list remove()/remove_if()/unique() Return size_type diff --git a/tests/std/test.lst b/tests/std/test.lst index 7b5e0b2210..a403bb4532 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -251,6 +251,7 @@ tests\P0556R3_bit_integral_power_of_two_operations tests\P0586R2_integer_comparison tests\P0595R2_is_constant_evaluated tests\P0607R0_inline_variables +tests\P0608R3_improved_variant_converting_constructor tests\P0616R0_using_move_in_numeric tests\P0631R8_numbers_math_constants tests\P0660R10_jthread_and_cv_any diff --git a/tests/std/tests/P0088R3_variant/env.lst b/tests/std/tests/P0088R3_variant/env.lst index 48e3b76c74..474ef952dd 100644 --- a/tests/std/tests/P0088R3_variant/env.lst +++ b/tests/std/tests/P0088R3_variant/env.lst @@ -10,7 +10,7 @@ RUNALL_CROSSLIST PM_CL="/w14640 /Zc:threadSafeInit-" RUNALL_CROSSLIST PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /Zc:noexceptTypes-" -PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=0 /std:c++17 /DCONSTEXPR_NOTHROW" +PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=0 /std:c++17 /DCONSTEXPR_NOTHROW /DTEST_PERMISSIVE" PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=1 /std:c++latest /permissive-" PM_CL="/EHsc /MD /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /Zc:char8_t- /Zc:preprocessor" PM_CL="/EHsc /MDd /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /Zc:wchar_t-" @@ -22,15 +22,15 @@ PM_CL="/EHsc /MT /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /analyze: PM_CL="/EHsc /MT /D_ITERATOR_DEBUG_LEVEL=1 /std:c++latest /permissive-" PM_CL="/EHsc /MTd /D_ITERATOR_DEBUG_LEVEL=0 /std:c++latest /permissive- /fp:strict" PM_CL="/EHsc /MTd /D_ITERATOR_DEBUG_LEVEL=1 /std:c++latest /permissive-" -PM_CL="/EHsc /MTd /D_ITERATOR_DEBUG_LEVEL=2 /std:c++latest /permissive /DCONSTEXPR_NOTHROW" +PM_CL="/EHsc /MTd /D_ITERATOR_DEBUG_LEVEL=2 /std:c++latest /permissive /DCONSTEXPR_NOTHROW /DTEST_PERMISSIVE" PM_CL="/EHsc /MTd /D_ITERATOR_DEBUG_LEVEL=2 /std:c++latest /permissive- /analyze:only /analyze:autolog-" PM_CL="/Za /EHsc /MD /std:c++latest /permissive-" PM_CL="/Za /EHsc /MDd /std:c++latest /permissive-" -PM_CL="/clr /MD /std:c++17 /DCONSTEXPR_NOTHROW" -PM_CL="/clr /MDd /std:c++17 /DCONSTEXPR_NOTHROW" +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 /MTd /std:c++latest /permissive-" PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MD /std:c++latest /permissive-" -PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MDd /std:c++17" +PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MDd /std:c++17 /DTEST_PERMISSIVE" PM_COMPILER="clang-cl" PM_CL="-fno-ms-compatibility -fno-delayed-template-parsing /EHsc /MTd /std:c++latest /permissive- /fp:strict" diff --git a/tests/std/tests/P0088R3_variant/test.cpp b/tests/std/tests/P0088R3_variant/test.cpp index 7a66a9f06e..87e482ffec 100644 --- a/tests/std/tests/P0088R3_variant/test.cpp +++ b/tests/std/tests/P0088R3_variant/test.cpp @@ -1768,7 +1768,8 @@ int run_test() { static_assert(!std::is_assignable, int>::value, ""); static_assert(!std::is_assignable, int>::value, ""); -#if 0 // TRANSITION, P0608 +#if _HAS_CXX20 +#ifndef __EDG__ // TRANSITION, DevCom-1337958 static_assert(std::is_assignable, int>::value == VariantAllowsNarrowingConversions, ""); static_assert(std::is_assignable, int>::value @@ -1778,13 +1779,16 @@ int run_test() static_assert(!std::is_assignable, int>::value, ""); static_assert(!std::is_assignable, decltype("meow")>::value, ""); +#endif // !__EDG__ static_assert(!std::is_assignable, decltype("meow")>::value, ""); static_assert(!std::is_assignable, decltype("meow")>::value, ""); - static_assert(!std::is_assignable, std::true_type>::value, ""); + 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 // TRANSITION, P0608 +#endif // !TEST_PERMISSIVE +#endif // _HAS_CXX20 return 0; } @@ -3048,7 +3052,8 @@ void test_T_assignment_sfinae() { using V = std::variant; static_assert(!std::is_assignable::value, "no matching operator="); } -#if 0 // TRANSITION, P0608 +#if _HAS_CXX20 +#ifndef __EDG__ // TRANSITION, DevCom-1337958 { using V = std::variant; static_assert(std::is_assignable::value == VariantAllowsNarrowingConversions, @@ -3063,10 +3068,11 @@ void test_T_assignment_sfinae() { }; static_assert(!std::is_assignable::value, "no boolean conversion in operator="); - static_assert(!std::is_assignable::value, + static_assert(std::is_assignable::value, "no converted to bool in operator="); } -#endif // TRANSITION, P0608 +#endif // !__EDG__ +#endif // _HAS_CXX20 { struct X {}; struct Y { @@ -3104,7 +3110,8 @@ void test_T_assignment_basic() { assert(v.index() == 1); assert(std::get<1>(v) == 43); } -#if 0 // TRANSITION, P0608 +#if _HAS_CXX20 +#ifndef __EDG__ // TRANSITION, DevCom-1337958 #ifndef TEST_VARIANT_ALLOWS_NARROWING_CONVERSIONS { std::variant v; @@ -3116,19 +3123,22 @@ void test_T_assignment_basic() { assert(std::get<0>(v) == 43); } #endif +#endif // !__EDG__ { std::variant v = true; v = "bar"; assert(v.index() == 0); assert(std::get<0>(v) == "bar"); } +#ifndef TEST_PERMISSIVE { std::variant> v; v = nullptr; assert(v.index() == 1); assert(std::get<1>(v) == nullptr); } -#endif // TRANSITION, P0608 +#endif // !TEST_PERMISSIVE +#endif // _HAS_CXX20 { std::variant v = 42; v = false; @@ -3266,7 +3276,8 @@ int run_test() { static_assert(!std::is_constructible, int>::value, ""); static_assert(!std::is_constructible, int>::value, ""); -#if 0 // TRANSITION, P0608 +#if _HAS_CXX20 +#ifndef __EDG__ // TRANSITION, DevCom-1337958 static_assert(std::is_constructible, int>::value == VariantAllowsNarrowingConversions, ""); static_assert(std::is_constructible, int>::value @@ -3278,11 +3289,13 @@ int run_test() static_assert(!std::is_constructible, decltype("meow")>::value, ""); static_assert(!std::is_constructible, decltype("meow")>::value, ""); static_assert(!std::is_constructible, decltype("meow")>::value, ""); - - static_assert(!std::is_constructible, std::true_type>::value, ""); +#endif // !__EDG__ + 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 // TRANSITION, P0608 +#endif // !TEST_PERMISSIVE +#endif // _HAS_CXX20 return 0; } @@ -4567,7 +4580,8 @@ void test_T_ctor_sfinae() { static_assert(!std::is_constructible::value, "no matching constructor"); } -#if 0 // TRANSITION, P0608 +#if _HAS_CXX20 +#ifndef __EDG__ // TRANSITION, DevCom-1337958 { using V = std::variant; static_assert(std::is_constructible::value == VariantAllowsNarrowingConversions, @@ -4582,10 +4596,11 @@ void test_T_ctor_sfinae() { }; static_assert(!std::is_constructible::value, "no boolean conversion in constructor"); - static_assert(!std::is_constructible::value, + static_assert(std::is_constructible::value, "no converted to bool in constructor"); } -#endif // TRANSITION, P0608 +#endif // !__EDG__ +#endif // _HAS_CXX20 { struct X {}; struct Y { @@ -4629,7 +4644,8 @@ void test_T_ctor_basic() { static_assert(v.index() == 1, ""); static_assert(std::get<1>(v) == 42, ""); } -#if 0 // TRANSITION, P0608 +#if _HAS_CXX20 +#ifndef __EDG__ // TRANSITION, DevCom-1337958 #ifndef TEST_VARIANT_ALLOWS_NARROWING_CONVERSIONS { constexpr std::variant v(42); @@ -4637,17 +4653,20 @@ void test_T_ctor_basic() { static_assert(std::get<1>(v) == 42, ""); } #endif +#endif // !__EDG__ { std::variant v = "meow"; assert(v.index() == 0); assert(std::get<0>(v) == "meow"); } +#ifndef TEST_PERMISSIVE { std::variant> v = nullptr; assert(v.index() == 1); assert(std::get<1>(v) == nullptr); } -#endif // TRANSITION, P0608 +#endif // !TEST_PERMISSIVE +#endif // _HAS_CXX20 { std::variant v = true; assert(v.index() == 0); @@ -4681,6 +4700,7 @@ void test_T_ctor_basic() { #endif } +#if !_HAS_CXX20 // Narrowing check occurs with P0608R3 struct BoomOnAnything { template constexpr BoomOnAnything(T) { static_assert(!std::is_same::value, ""); } @@ -4692,6 +4712,7 @@ void test_no_narrowing_check_for_class_types() { assert(v.index() == 0); assert(std::get<0>(v) == 42); } +#endif // Narrowing check occurs with P0608R3 struct Bar {}; struct Baz {}; @@ -4708,7 +4729,9 @@ 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(); return 0; } diff --git a/tests/std/tests/P0608R3_improved_variant_converting_constructor/env.lst b/tests/std/tests/P0608R3_improved_variant_converting_constructor/env.lst new file mode 100644 index 0000000000..642f530ffa --- /dev/null +++ b/tests/std/tests/P0608R3_improved_variant_converting_constructor/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_latest_matrix.lst diff --git a/tests/std/tests/P0608R3_improved_variant_converting_constructor/test.cpp b/tests/std/tests/P0608R3_improved_variant_converting_constructor/test.cpp new file mode 100644 index 0000000000..2d407f7d1e --- /dev/null +++ b/tests/std/tests/P0608R3_improved_variant_converting_constructor/test.cpp @@ -0,0 +1,263 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// Also tests for P1957R2: Converting from T* to bool should be considered narrowing + +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +struct double_double { + double_double(double x) : x_(x) {} + + double x_; +}; +struct convertible_bool { + convertible_bool(bool x) : x_(x) {} + ~convertible_bool() = default; + + operator bool() const noexcept { + return x_; + } + + bool x_; +}; +struct default_struct {}; + +void assert_P0608R3() { + // P0608R3 examples + static_assert(is_constructible_v, const char*>); + static_assert(is_constructible_v, string>); + static_assert(is_constructible_v>, char16_t>); + static_assert(is_constructible_v>, double&>); + static_assert(is_constructible_v, char>); +#ifndef __EDG__ // TRANSITION, DevCom-1337958 + static_assert(is_constructible_v, int>); + static_assert(is_constructible_v, int>); + static_assert(is_constructible_v, int>); + static_assert(is_constructible_v, long long>, int>); +#endif // !__EDG__ + static_assert(is_constructible_v, char>); +#ifndef __EDG__ // TRANSITION, DevCom-1337958 + static_assert(!is_constructible_v, int>); + static_assert(!is_constructible_v>, int>); +#endif // !__EDG__ + static_assert(!is_constructible_v, int>); + + static_assert(is_assignable_v, const char*>); + static_assert(is_assignable_v, string>); + static_assert(is_assignable_v>, char16_t>); + static_assert(is_assignable_v>, double&>); + static_assert(is_assignable_v, char>); +#ifndef __EDG__ // TRANSITION, DevCom-1337958 + static_assert(is_assignable_v, int>); + static_assert(is_assignable_v, int>); + static_assert(is_assignable_v, int>); + static_assert(is_assignable_v, long long>, int>); +#endif // !__EDG__ + static_assert(is_assignable_v, char>); +#ifndef __EDG__ // TRANSITION, DevCom-1337958 + static_assert(!is_assignable_v, int>); + static_assert(!is_assignable_v>, int>); +#endif // !__EDG__ + static_assert(!is_assignable_v, int>); +} + +void assert_P1957R2() { + // P1957R2 examples + static_assert(is_constructible_v, bool>); + static_assert(is_constructible_v, bitset<4>::reference>); + static_assert(is_constructible_v, bitset<4>::reference>); + + static_assert(is_assignable_v, bool>); + static_assert(is_assignable_v, bitset<4>::reference>); + static_assert(is_assignable_v, bitset<4>::reference>); +} + +void assert_more_examples() { + // More examples + static_assert(is_constructible_v, double>); + static_assert(is_constructible_v>, optional, int>, int>); + static_assert(is_constructible_v>, optional>, int>); + static_assert(is_constructible_v, optional, float>, int>); + static_assert(is_constructible_v, convertible_bool>); + static_assert(is_constructible_v, convertible_bool>); + static_assert(is_constructible_v, bool>); + static_assert(is_constructible_v, convertible_bool>); + static_assert(is_constructible_v, bool>); + static_assert(is_constructible_v, bool>); +#ifndef __EDG__ // TRANSITION, DevCom-1337958 +#ifdef __clang__ // TRANSITION, DevCom-1338628 + static_assert(is_constructible_v, int>); +#endif // __clang__ + static_assert(!is_constructible_v, unsigned int>); + static_assert(!is_constructible_v, int>); +#endif // !__EDG__ + static_assert(!is_constructible_v, int>); + + static_assert(is_assignable_v, double>); + static_assert(is_assignable_v>, optional, int>, int>); + static_assert(is_assignable_v>, optional>, int>); + static_assert(is_assignable_v, optional, float>, int>); + static_assert(is_assignable_v, convertible_bool>); + static_assert(is_assignable_v, convertible_bool>); + static_assert(is_assignable_v, bool>); + static_assert(is_assignable_v, convertible_bool>); + static_assert(is_assignable_v, bool>); + static_assert(is_assignable_v, bool>); +#ifndef __EDG__ // TRANSITION, DevCom-1337958 +#ifdef __clang__ // TRANSITION, DevCom-1338628 + static_assert(is_assignable_v, int>); +#endif // __clang__ + static_assert(!is_assignable_v, unsigned int>); + static_assert(!is_assignable_v, int>); +#endif // !__EDG__ + static_assert(!is_assignable_v, int>); +} + +void test_variant_constructor_P0608R3() { + // P0608R3 runtime checks + variant a = "abc"; // string + assert(a.index() == 0); + assert(get<0>(a) == "abc"); + + variant> b = u'\u2043'; // optional + assert(b.index() == 1); + assert(get>(b) == u'\u2043'); + + double c_data = 3.14; + variant> c = c_data; // reference_wrapper + assert(c.index() == 1); + assert(get<1>(c) == c_data); + + variant d; + assert(d.index() == 0); + d = 0; // int + assert(d.index() == 1); + + variant e; + assert(e.index() == 0); +#ifndef __EDG__ // TRANSITION, DevCom-1337958 + e = 0; // long + assert(e.index() == 1); +#endif // !__EDG__ + + variant f = 'a'; // int + assert(f.index() == 1); + assert(get(f) == 97); + +#ifndef __EDG__ // TRANSITION, DevCom-1337958 + variant g = 0; // long + assert(g.index() == 1); + + variant h = 0; // long + assert(h.index() == 1); + + variant, long long> i = 0; // long long + assert(i.index() == 2); +#endif // !__EDG__ + + variant j = 'a'; // int + assert(j.index() == 1); + assert(get(j) == 97); +} + +void test_variant_constructor_P1957R2() { + bitset<4> a_bitset("0101"); + bool a_data = a_bitset[2]; + variant a = a_data; // bool + assert(a.index() == 0); + assert(get<0>(a)); + + bitset<4> b_bitset("0101"); + variant b = b_bitset[2]; // bool + variant b2 = b_bitset[1]; // bool + assert(b.index() == 0); + assert(get<0>(b)); + assert(b2.index() == 0); + assert(!get<0>(b2)); +} + +void test_variant_constructor_more_examples() { + variant> a = true; // bool + assert(a.index() == 3); + + variant b = convertible_bool{true}; // bool + assert(b.index() == 0); + assert(get<0>(b)); + + variant c = false; // bool + assert(c.index() == 2); + + variant d = convertible_bool{true}; // convertible_bool + assert(d.index() == 2); + + variant e = bool{}; // bool + assert(e.index() == 1); + assert(!get<1>(e)); + + variant f = convertible_bool{false}; // bool + assert(f.index() == 1); + assert(!get<1>(f)); + + variant g = true_type{}; // bool + assert(g.index() == 0); + assert(get<0>(g)); +} + +void test_assignment_operator() { + variant a; // string + assert(a.index() == 0); + assert(get(a) == ""); + a = 3; // int + assert(a.index() == 2); + assert(get(a) == 3); + a = true; // bool + assert(a.index() == 1); + assert(get(a) == true); + +#ifndef __EDG__ // TRANSITION, DevCom-1337958 + bool b_data = true; + variant b = b_data; // bool + assert(b.index() == 0); + assert(get<0>(b) == b_data); + b = 12; // int + assert(b.index() == 1); + assert(get<1>(b) == 12); + b = 12.5; // double_double + assert(b.index() == 2); + assert(get<2>(b).x_ == 12.5); +#endif // !__EDG__ + +#ifdef __clang__ // TRANSITION, DevCom-1338628 + variant c; + assert(c.index() == 0); + c = false; // bool + assert(c.index() == 1); + assert(get<1>(c) == false); + c = 5.12; // double_double + assert(c.index() == 2); + assert(get<2>(c).x_ == 5.12); + double_double c_data{1.2}; + c = static_cast(&c_data); // void* + assert(c.index() == 0); + assert(static_cast(get<0>(c))->x_ == 1.2); +#endif // __clang__ +} + +int main() { + assert_P0608R3(); + assert_P1957R2(); + assert_more_examples(); + test_variant_constructor_P0608R3(); + test_variant_constructor_P1957R2(); + test_variant_constructor_more_examples(); + test_assignment_operator(); +}