From 31a9a4b83720af79110941824abe28fc6ff42355 Mon Sep 17 00:00:00 2001 From: yronglin Date: Fri, 22 Mar 2024 20:45:17 +0800 Subject: [PATCH] [libc++] Implement LWG3528 (`make_from_tuple` can perform (the equivalent of) a C-style cast) (#85263) Implement [LWG3528](https://wg21.link/LWG3528). Based on LWG3528(https://wg21.link/LWG3528) and http://eel.is/c++draft/description#structure.requirements-9, the standard allows to impose requirements, we constraint `std::make_from_tuple` to make `std::make_from_tuple` SFINAE friendly and also avoid worse diagnostic messages. We still keep the constraints of `std::__make_from_tuple_impl` so that `std::__make_from_tuple_impl` will have the same advantages when used alone. --------- Signed-off-by: yronglin --- libcxx/docs/Status/Cxx23Issues.csv | 2 +- libcxx/include/tuple | 28 +++++- .../tuple.apply/make_from_tuple.pass.cpp | 88 +++++++++++++++++++ 3 files changed, 116 insertions(+), 2 deletions(-) diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv index 8de265f4d1b6..ebdc4a745c9f 100644 --- a/libcxx/docs/Status/Cxx23Issues.csv +++ b/libcxx/docs/Status/Cxx23Issues.csv @@ -77,7 +77,7 @@ `3523 `__,"``iota_view::sentinel`` is not always ``iota_view``'s sentinel","June 2021","","","|ranges|" `3526 `__,"Return types of ``uses_allocator_construction_args`` unspecified","June 2021","","" `3527 `__,"``uses_allocator_construction_args`` handles rvalue pairs of rvalue references incorrectly","June 2021","","" -`3528 `__,"``make_from_tuple`` can perform (the equivalent of) a C-style cast","June 2021","","" +`3528 `__,"``make_from_tuple`` can perform (the equivalent of) a C-style cast","June 2021","|Complete|","19.0" `3529 `__,"``priority_queue(first, last)`` should construct ``c`` with ``(first, last)``","June 2021","|Complete|","14.0" `3530 `__,"``BUILTIN-PTR-MEOW`` should not opt the type out of syntactic checks","June 2021","","" `3532 `__,"``split_view::inner-iterator::operator++(int)`` should depend on ``Base``","June 2021","","","|ranges|" diff --git a/libcxx/include/tuple b/libcxx/include/tuple index f78db061b844..a9f0d680fe0e 100644 --- a/libcxx/include/tuple +++ b/libcxx/include/tuple @@ -1377,15 +1377,41 @@ inline _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) apply(_Fn&& __f, _Tuple&& std::forward<_Tuple>(__t), typename __make_tuple_indices>>::type{})) +#if _LIBCPP_STD_VER >= 20 template inline _LIBCPP_HIDE_FROM_ABI constexpr _Tp __make_from_tuple_impl(_Tuple&& __t, __tuple_indices<_Idx...>) + noexcept(noexcept(_Tp(std::get<_Idx>(std::forward<_Tuple>(__t))...))) + requires is_constructible_v<_Tp, decltype(std::get<_Idx>(std::forward<_Tuple>(__t)))...> { + return _Tp(std::get<_Idx>(std::forward<_Tuple>(__t))...); +} +#else +template +inline _LIBCPP_HIDE_FROM_ABI constexpr _Tp __make_from_tuple_impl(_Tuple&& __t, __tuple_indices<_Idx...>, + enable_if_t(std::forward<_Tuple>(__t)))...>> * = nullptr) _LIBCPP_NOEXCEPT_RETURN(_Tp(std::get<_Idx>(std::forward<_Tuple>(__t))...)) +#endif // _LIBCPP_STD_VER >= 20 +template >>::type, class = void> +inline constexpr bool __can_make_from_tuple = false; + +template +inline constexpr bool __can_make_from_tuple<_Tp, _Tuple, __tuple_indices<_Idx...>, + enable_if_t(std::declval<_Tuple>()))...>>> = true; + +// Based on LWG3528(https://wg21.link/LWG3528) and http://eel.is/c++draft/description#structure.requirements-9, +// the standard allows to impose requirements, we constraint std::make_from_tuple to make std::make_from_tuple +// SFINAE friendly and also avoid worse diagnostic messages. We still keep the constraints of std::__make_from_tuple_impl +// so that std::__make_from_tuple_impl will have the same advantages when used alone. +#if _LIBCPP_STD_VER >= 20 template + requires __can_make_from_tuple<_Tp, _Tuple> // strengthen +#else +template >> // strengthen +#endif // _LIBCPP_STD_VER >= 20 inline _LIBCPP_HIDE_FROM_ABI constexpr _Tp make_from_tuple(_Tuple&& __t) _LIBCPP_NOEXCEPT_RETURN(std::__make_from_tuple_impl<_Tp>( std::forward<_Tuple>(__t), typename __make_tuple_indices>>::type{})) - # undef _LIBCPP_NOEXCEPT_RETURN # endif // _LIBCPP_STD_VER >= 17 diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.apply/make_from_tuple.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.apply/make_from_tuple.pass.cpp index e3a21149c21e..d7374351afa8 100644 --- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.apply/make_from_tuple.pass.cpp +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.apply/make_from_tuple.pass.cpp @@ -195,6 +195,94 @@ void test_noexcept() { } } +namespace LWG3528 { +template +auto test_make_from_tuple(T&&, Tuple&& t) -> decltype(std::make_from_tuple(t), uint8_t()) { + return 0; +} +template +uint32_t test_make_from_tuple(...) { + return 0; +} + +template +static constexpr bool can_make_from_tuple = + std::is_same_v(T{}, Tuple{})), uint8_t>; + +template +auto test_make_from_tuple_impl(T&&, Tuple&& t) + -> decltype(std::__make_from_tuple_impl( + t, typename std::__make_tuple_indices< std::tuple_size_v>>::type{}), + uint8_t()) { + return 0; +} +template +uint32_t test_make_from_tuple_impl(...) { + return 0; +} + +template +static constexpr bool can_make_from_tuple_impl = + std::is_same_v(T{}, Tuple{})), uint8_t>; + +struct A { + int a; +}; +struct B : public A {}; + +struct C { + C(const B&) {} +}; + +enum class D { + ONE, + TWO, +}; + +// Test std::make_from_tuple constraints. + +// reinterpret_cast +static_assert(!can_make_from_tuple>); +static_assert(can_make_from_tuple>); + +// const_cast +static_assert(!can_make_from_tuple>); +static_assert(!can_make_from_tuple>); +static_assert(can_make_from_tuple>); +static_assert(can_make_from_tuple>); +static_assert(can_make_from_tuple>); +static_assert(can_make_from_tuple>); + +// static_cast +static_assert(!can_make_from_tuple>); +static_assert(!can_make_from_tuple>); +static_assert(can_make_from_tuple>); +static_assert(can_make_from_tuple>); +static_assert(can_make_from_tuple>); + +// Test std::__make_from_tuple_impl constraints. + +// reinterpret_cast +static_assert(!can_make_from_tuple_impl>); +static_assert(can_make_from_tuple_impl>); + +// const_cast +static_assert(!can_make_from_tuple_impl>); +static_assert(!can_make_from_tuple_impl>); +static_assert(can_make_from_tuple_impl>); +static_assert(can_make_from_tuple_impl>); +static_assert(can_make_from_tuple_impl>); +static_assert(can_make_from_tuple_impl>); + +// static_cast +static_assert(!can_make_from_tuple_impl>); +static_assert(!can_make_from_tuple_impl>); +static_assert(can_make_from_tuple_impl>); +static_assert(can_make_from_tuple_impl>); +static_assert(can_make_from_tuple_impl>); + +} // namespace LWG3528 + int main(int, char**) { test_constexpr_construction();