From 97dd487ab09609bd4ffcff1f1474d773d051e70c Mon Sep 17 00:00:00 2001 From: Hui Date: Wed, 20 Sep 2023 19:41:00 +0100 Subject: [PATCH 1/5] [libc++] LWG 3821 uses_allocator_construction_args should have overload for pair-like --- libcxx/docs/Status/Cxx23Issues.csv | 2 +- .../__memory/uses_allocator_construction.h | 62 ++++++++++++++----- libcxx/include/__utility/pair.h | 3 +- libcxx/include/memory | 37 +++++++++++ .../uses_allocator_construction_args.pass.cpp | 38 ++++++++++-- 5 files changed, 119 insertions(+), 23 deletions(-) diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv index 67ef35ec39b2a..6ff2a5801d72d 100644 --- a/libcxx/docs/Status/Cxx23Issues.csv +++ b/libcxx/docs/Status/Cxx23Issues.csv @@ -262,7 +262,7 @@ "`3742 `__","``deque::prepend_range`` needs to permute","February 2023","","","|ranges|" "`3790 `__","`P1467 `__ accidentally changed ``nexttoward``'s signature","February 2023","","","" "`3819 `__","``reference_meows_from_temporary`` should not use ``is_meowible``","February 2023","","","" -"`3821 `__","``uses_allocator_construction_args`` should have overload for ``pair-like``","February 2023","","","" +"`3821 `__","``uses_allocator_construction_args`` should have overload for ``pair-like``","February 2023","|Complete|","18.0.0","" "`3834 `__","Missing ``constexpr`` for ``std::intmax_t`` math functions in ````","February 2023","","","" "`3839 `__","``range_formatter``'s ``set_separator``, ``set_brackets``, and ``underlying`` functions should be ``noexcept``","February 2023","|Complete|","17.0","|format|" "`3841 `__","```` should not be ""all freestanding""","February 2023","","","" diff --git a/libcxx/include/__memory/uses_allocator_construction.h b/libcxx/include/__memory/uses_allocator_construction.h index a2e4f6e26f4b3..e4ac89e60379c 100644 --- a/libcxx/include/__memory/uses_allocator_construction.h +++ b/libcxx/include/__memory/uses_allocator_construction.h @@ -12,6 +12,7 @@ #include <__config> #include <__memory/construct_at.h> #include <__memory/uses_allocator.h> +#include <__tuple/pair_like.h> #include <__type_traits/enable_if.h> #include <__type_traits/is_same.h> #include <__type_traits/remove_cv.h> @@ -36,7 +37,10 @@ inline constexpr bool __is_std_pair = false; template inline constexpr bool __is_std_pair> = true; -template , int> = 0> +template +inline constexpr bool __is_cv_std_pair = __is_std_pair>; + +template , int> = 0> _LIBCPP_HIDE_FROM_ABI constexpr auto __uses_allocator_construction_args(const _Alloc& __alloc, _Args&&... __args) noexcept { if constexpr (!uses_allocator_v<_Type, _Alloc> && is_constructible_v<_Type, _Args...>) { @@ -52,7 +56,7 @@ __uses_allocator_construction_args(const _Alloc& __alloc, _Args&&... __args) noe } } -template , int> = 0> +template , int> = 0> _LIBCPP_HIDE_FROM_ABI constexpr auto __uses_allocator_construction_args( const _Alloc& __alloc, piecewise_construct_t, _Tuple1&& __x, _Tuple2&& __y) noexcept { return std::make_tuple( @@ -71,12 +75,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr auto __uses_allocator_construction_args( std::forward<_Tuple2>(__y))); } -template , int> = 0> +template , int> = 0> _LIBCPP_HIDE_FROM_ABI constexpr auto __uses_allocator_construction_args(const _Alloc& __alloc) noexcept { return std::__uses_allocator_construction_args<_Pair>(__alloc, piecewise_construct, tuple<>{}, tuple<>{}); } -template , int> = 0> +template , int> = 0> _LIBCPP_HIDE_FROM_ABI constexpr auto __uses_allocator_construction_args(const _Alloc& __alloc, _Up&& __u, _Vp&& __v) noexcept { return std::__uses_allocator_construction_args<_Pair>( @@ -87,7 +91,7 @@ __uses_allocator_construction_args(const _Alloc& __alloc, _Up&& __u, _Vp&& __v) } # if _LIBCPP_STD_VER >= 23 -template , int> = 0> +template , int> = 0> _LIBCPP_HIDE_FROM_ABI constexpr auto __uses_allocator_construction_args(const _Alloc& __alloc, pair<_Up, _Vp>& __pair) noexcept { return std::__uses_allocator_construction_args<_Pair>( @@ -95,14 +99,14 @@ __uses_allocator_construction_args(const _Alloc& __alloc, pair<_Up, _Vp>& __pair } # endif -template , int> = 0> +template , int> = 0> _LIBCPP_HIDE_FROM_ABI constexpr auto __uses_allocator_construction_args(const _Alloc& __alloc, const pair<_Up, _Vp>& __pair) noexcept { return std::__uses_allocator_construction_args<_Pair>( __alloc, piecewise_construct, std::forward_as_tuple(__pair.first), std::forward_as_tuple(__pair.second)); } -template , int> = 0> +template , int> = 0> _LIBCPP_HIDE_FROM_ABI constexpr auto __uses_allocator_construction_args(const _Alloc& __alloc, pair<_Up, _Vp>&& __pair) noexcept { return std::__uses_allocator_construction_args<_Pair>( @@ -113,7 +117,7 @@ __uses_allocator_construction_args(const _Alloc& __alloc, pair<_Up, _Vp>&& __pai } # if _LIBCPP_STD_VER >= 23 -template , int> = 0> +template , int> = 0> _LIBCPP_HIDE_FROM_ABI constexpr auto __uses_allocator_construction_args(const _Alloc& __alloc, const pair<_Up, _Vp>&& __pair) noexcept { return std::__uses_allocator_construction_args<_Pair>( @@ -122,6 +126,20 @@ __uses_allocator_construction_args(const _Alloc& __alloc, const pair<_Up, _Vp>&& std::forward_as_tuple(std::get<0>(std::move(__pair))), std::forward_as_tuple(std::get<1>(std::move(__pair)))); } + +template < class _Pair, + class _Alloc, + __pair_like _PairLike, + __enable_if_t<__is_cv_std_pair<_Pair> && !__is_specialization_of_subrange>::value, + int> = 0> +_LIBCPP_HIDE_FROM_ABI constexpr auto +__uses_allocator_construction_args(const _Alloc& __alloc, _PairLike&& __p) noexcept { + return std::__uses_allocator_construction_args<_Pair>( + __alloc, + piecewise_construct, + std::forward_as_tuple(std::get<0>(std::forward<_PairLike>(__p))), + std::forward_as_tuple(std::get<1>(std::forward<_PairLike>(__p)))); +} # endif namespace __uses_allocator_detail { @@ -139,23 +157,33 @@ template inline constexpr bool __convertible_to_const_pair_ref = decltype(__uses_allocator_detail::__convertible_to_const_pair_ref_impl<_Tp>(0))::value; +# if _LIBCPP_STD_VER >= 23 +template +inline constexpr bool __uses_allocator_constraints = + __is_cv_std_pair<_Tp> && + (__is_specialization_of_subrange>::value || + (!__pair_like<_Up> && !__convertible_to_const_pair_ref<_Up>)); +# else +template +inline constexpr bool __uses_allocator_constraints = __is_cv_std_pair<_Tp> && !__convertible_to_const_pair_ref<_Up>; +# endif + } // namespace __uses_allocator_detail -template < - class _Pair, - class _Alloc, - class _Type, - __enable_if_t<__is_std_pair<_Pair> && !__uses_allocator_detail::__convertible_to_const_pair_ref<_Type>, int> = 0> +template < class _Pair, + class _Alloc, + class _Type, + __enable_if_t<__uses_allocator_detail::__uses_allocator_constraints<_Pair, _Type>, int> = 0> _LIBCPP_HIDE_FROM_ABI constexpr auto __uses_allocator_construction_args(const _Alloc& __alloc, _Type&& __value) noexcept; template _LIBCPP_HIDE_FROM_ABI constexpr _Type __make_obj_using_allocator(const _Alloc& __alloc, _Args&&... __args); -template && !__uses_allocator_detail::__convertible_to_const_pair_ref<_Type>, int>> +template < class _Pair, + class _Alloc, + class _Type, + __enable_if_t< __uses_allocator_detail::__uses_allocator_constraints<_Pair, _Type>, int>> _LIBCPP_HIDE_FROM_ABI constexpr auto __uses_allocator_construction_args(const _Alloc& __alloc, _Type&& __value) noexcept { struct __pair_constructor { diff --git a/libcxx/include/__utility/pair.h b/libcxx/include/__utility/pair.h index 62dac6dd1da3f..535344eb1e2d6 100644 --- a/libcxx/include/__utility/pair.h +++ b/libcxx/include/__utility/pair.h @@ -284,7 +284,8 @@ struct _LIBCPP_TEMPLATE_VIS pair } template <__pair_like _PairLike> - requires(is_constructible_v(std::declval<_PairLike&&>()))> && + requires(!__is_specialization_of_subrange>::value && + is_constructible_v(std::declval<_PairLike&&>()))> && is_constructible_v(std::declval<_PairLike&&>()))>) _LIBCPP_HIDE_FROM_ABI constexpr explicit(__pair_like_explicit_wknd<_PairLike>()) pair(_PairLike&& __p) diff --git a/libcxx/include/memory b/libcxx/include/memory index cd6bcc7eaa35d..f8f7a62951f15 100644 --- a/libcxx/include/memory +++ b/libcxx/include/memory @@ -867,6 +867,43 @@ template struct hash >; template inline constexpr bool uses_allocator_v = uses_allocator::value; +// [allocator.uses.construction], uses-allocator construction +template + constexpr auto uses_allocator_construction_args(const Alloc& alloc, // freestanding, since C++20 + Args&&... args) noexcept; +template + constexpr auto uses_allocator_construction_args(const Alloc& alloc, // freestanding, since C++20 + piecewise_construct_t, + Tuple1&& x, Tuple2&& y) noexcept; +template + constexpr auto uses_allocator_construction_args(const Alloc& alloc) noexcept; // freestanding, since C++20 +template + constexpr auto uses_allocator_construction_args(const Alloc& alloc, // freestanding, since C++20 + U&& u, V&& v) noexcept; +template + constexpr auto uses_allocator_construction_args(const Alloc& alloc, // freestanding, since C++23 + pair& pr) noexcept; +template + constexpr auto uses_allocator_construction_args(const Alloc& alloc, // freestanding, since C++20 + const pair& pr) noexcept; +template + constexpr auto uses_allocator_construction_args(const Alloc& alloc, // freestanding, since C++20 + pair&& pr) noexcept; +template + constexpr auto uses_allocator_construction_args(const Alloc& alloc, // freestanding, since C++23 + const pair&& pr) noexcept; +template + constexpr auto uses_allocator_construction_args(const Alloc& alloc, // freestanding, since C++20 + P&& p) noexcept; +template + constexpr auto uses_allocator_construction_args(const Alloc& alloc, // freestanding, since C++20 + U&& u) noexcept; +template + constexpr T make_obj_using_allocator(const Alloc& alloc, Args&&... args); // freestanding, since C++20 +template + constexpr T* uninitialized_construct_using_allocator(T* p, // freestanding, since C++20 + const Alloc& alloc, Args&&... args); + // [ptr.align] void* align(size_t alignment, size_t size, void*& ptr, size_t& space); diff --git a/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/uses_allocator_construction_args.pass.cpp b/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/uses_allocator_construction_args.pass.cpp index 739278e74ddab..0ad5e12e8e1c1 100644 --- a/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/uses_allocator_construction_args.pass.cpp +++ b/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/uses_allocator_construction_args.pass.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -132,19 +133,48 @@ constexpr bool test() { } #endif { - ConvertibleToPair ctp {}; - auto ret = test_uses_allocator_construction_args>(a, ctp); + ConvertibleToPair ctp{}; + auto ret = test_uses_allocator_construction_args>(a, ctp); std::pair v = std::get<0>(ret); assert(std::get<0>(v) == 1); assert(std::get<1>(v) == 2); } { - ConvertibleToPair ctp {}; - auto ret = test_uses_allocator_construction_args>(a, std::move(ctp)); + ConvertibleToPair ctp{}; + auto ret = test_uses_allocator_construction_args>(a, std::move(ctp)); std::pair v = std::get<0>(ret); assert(std::get<0>(v) == 1); assert(std::get<1>(v) == 2); } +#if TEST_STD_VER >= 23 + // LWG 3821 + // uses_allocator_construction_args should have overload for pair-like + { + // pair-like with explicit ctr should work + struct Foo { + int i = 5; + }; + struct Bar { + int i; + constexpr explicit Bar(Foo foo) : i(foo.i) {} + }; + + std::tuple pair_like; + auto ret = test_uses_allocator_construction_args>(a, pair_like); + auto pair = std::make_from_tuple>(std::move(ret)); + assert(pair.first.i == 5); + assert(pair.second.i == 5); + } + { + // subrange should work + int i = 5; + std::ranges::subrange r{&i, &i}; + auto ret = std::__uses_allocator_construction_args>(a, r); + auto pair = std::make_from_tuple>(std::move(ret)); + assert(pair.first == &i); + assert(pair.second == &i); + } +#endif return true; } From 2c33d8a5e800c26856e2527e9382869edb36c388 Mon Sep 17 00:00:00 2001 From: Hui Date: Thu, 21 Sep 2023 22:32:28 +0100 Subject: [PATCH 2/5] remove freestanding --- libcxx/include/memory | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/libcxx/include/memory b/libcxx/include/memory index f8f7a62951f15..24ba82f43ddd3 100644 --- a/libcxx/include/memory +++ b/libcxx/include/memory @@ -869,39 +869,39 @@ template // [allocator.uses.construction], uses-allocator construction template - constexpr auto uses_allocator_construction_args(const Alloc& alloc, // freestanding, since C++20 + constexpr auto uses_allocator_construction_args(const Alloc& alloc, // since C++20 Args&&... args) noexcept; template - constexpr auto uses_allocator_construction_args(const Alloc& alloc, // freestanding, since C++20 + constexpr auto uses_allocator_construction_args(const Alloc& alloc, // since C++20 piecewise_construct_t, Tuple1&& x, Tuple2&& y) noexcept; template - constexpr auto uses_allocator_construction_args(const Alloc& alloc) noexcept; // freestanding, since C++20 + constexpr auto uses_allocator_construction_args(const Alloc& alloc) noexcept; // since C++20 template - constexpr auto uses_allocator_construction_args(const Alloc& alloc, // freestanding, since C++20 + constexpr auto uses_allocator_construction_args(const Alloc& alloc, // since C++20 U&& u, V&& v) noexcept; template - constexpr auto uses_allocator_construction_args(const Alloc& alloc, // freestanding, since C++23 + constexpr auto uses_allocator_construction_args(const Alloc& alloc, // since C++23 pair& pr) noexcept; template - constexpr auto uses_allocator_construction_args(const Alloc& alloc, // freestanding, since C++20 + constexpr auto uses_allocator_construction_args(const Alloc& alloc, // since C++20 const pair& pr) noexcept; template - constexpr auto uses_allocator_construction_args(const Alloc& alloc, // freestanding, since C++20 + constexpr auto uses_allocator_construction_args(const Alloc& alloc, // since C++20 pair&& pr) noexcept; template - constexpr auto uses_allocator_construction_args(const Alloc& alloc, // freestanding, since C++23 + constexpr auto uses_allocator_construction_args(const Alloc& alloc, // since C++23 const pair&& pr) noexcept; template - constexpr auto uses_allocator_construction_args(const Alloc& alloc, // freestanding, since C++20 + constexpr auto uses_allocator_construction_args(const Alloc& alloc, // since C++20 P&& p) noexcept; template - constexpr auto uses_allocator_construction_args(const Alloc& alloc, // freestanding, since C++20 + constexpr auto uses_allocator_construction_args(const Alloc& alloc, // since C++20 U&& u) noexcept; template - constexpr T make_obj_using_allocator(const Alloc& alloc, Args&&... args); // freestanding, since C++20 + constexpr T make_obj_using_allocator(const Alloc& alloc, Args&&... args); // since C++20 template - constexpr T* uninitialized_construct_using_allocator(T* p, // freestanding, since C++20 + constexpr T* uninitialized_construct_using_allocator(T* p, // since C++20 const Alloc& alloc, Args&&... args); // [ptr.align] From 576f50a7764756db29a98e380e3381a5a9a17cfc Mon Sep 17 00:00:00 2001 From: Hui Date: Sun, 1 Oct 2023 22:59:18 +0100 Subject: [PATCH 3/5] address feedback --- libcxx/docs/Status/Cxx23Issues.csv | 2 +- .../__memory/uses_allocator_construction.h | 7 +- .../operator.pair_like.pass.cpp | 80 +++++++++++++++++++ .../uses_allocator_construction_args.pass.cpp | 46 +++++++---- .../pairs/pairs.pair/ctor.pair_like.pass.cpp | 24 ++---- 5 files changed, 121 insertions(+), 38 deletions(-) create mode 100644 libcxx/test/std/ranges/range.utility/range.subrange/operator.pair_like.pass.cpp diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv index 6ff2a5801d72d..8be1ff588cf3b 100644 --- a/libcxx/docs/Status/Cxx23Issues.csv +++ b/libcxx/docs/Status/Cxx23Issues.csv @@ -198,7 +198,7 @@ "`3629 `__","``make_error_code`` and ``make_error_condition`` are customization points","November 2022","|Complete|","16.0","" "`3636 `__","``formatter::format`` should be ``const``-qualified","November 2022","|Complete|","16.0","|format|" "`3646 `__","``std::ranges::view_interface::size`` returns a signed type","November 2022","|Complete|","16.0","|ranges|" -"`3677 `__","Is a cv-qualified ``pair`` specially handled in uses-allocator construction?", "November 2022","","","" +"`3677 `__","Is a cv-qualified ``pair`` specially handled in uses-allocator construction?", "November 2022","|Complete|","18.0","" "`3717 `__","``common_view::end`` should improve ``random_access_range`` case", "November 2022","","","|ranges|" "`3732 `__","``prepend_range`` and ``append_range`` can't be amortized constant time", "November 2022","|Nothing to do|","","|ranges|" "`3736 `__","``move_iterator`` missing ``disable_sized_sentinel_for`` specialization", "November 2022","","","|ranges|" diff --git a/libcxx/include/__memory/uses_allocator_construction.h b/libcxx/include/__memory/uses_allocator_construction.h index e4ac89e60379c..71ae5bcd32331 100644 --- a/libcxx/include/__memory/uses_allocator_construction.h +++ b/libcxx/include/__memory/uses_allocator_construction.h @@ -43,12 +43,13 @@ inline constexpr bool __is_cv_std_pair = __is_std_pair>; template , int> = 0> _LIBCPP_HIDE_FROM_ABI constexpr auto __uses_allocator_construction_args(const _Alloc& __alloc, _Args&&... __args) noexcept { - if constexpr (!uses_allocator_v<_Type, _Alloc> && is_constructible_v<_Type, _Args...>) { + if constexpr (!uses_allocator_v, _Alloc> && is_constructible_v<_Type, _Args...>) { return std::forward_as_tuple(std::forward<_Args>(__args)...); - } else if constexpr (uses_allocator_v<_Type, _Alloc> && + } else if constexpr (uses_allocator_v, _Alloc> && is_constructible_v<_Type, allocator_arg_t, const _Alloc&, _Args...>) { return tuple(allocator_arg, __alloc, std::forward<_Args>(__args)...); - } else if constexpr (uses_allocator_v<_Type, _Alloc> && is_constructible_v<_Type, _Args..., const _Alloc&>) { + } else if constexpr (uses_allocator_v, _Alloc> && + is_constructible_v<_Type, _Args..., const _Alloc&>) { return std::forward_as_tuple(std::forward<_Args>(__args)..., __alloc); } else { static_assert( diff --git a/libcxx/test/std/ranges/range.utility/range.subrange/operator.pair_like.pass.cpp b/libcxx/test/std/ranges/range.utility/range.subrange/operator.pair_like.pass.cpp new file mode 100644 index 0000000000000..1d0dfd05b3f7e --- /dev/null +++ b/libcxx/test/std/ranges/range.utility/range.subrange/operator.pair_like.pass.cpp @@ -0,0 +1,80 @@ + +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +// template PairLike> +// requires pair-like-convertible-from +// constexpr operator PairLike() const; + +#include +#include +#include + +#include "test_macros.h" + +static_assert(std::convertible_to, std::pair>); +static_assert(std::convertible_to, std::tuple>); +static_assert(!std::convertible_to, std::pair>); +static_assert(!std::convertible_to, std::pair>); +static_assert(!std::convertible_to, std::pair>); +static_assert(!std::convertible_to, std::array>); + +constexpr bool test() { + // Check to std::pair + { + int data[] = {1, 2, 3, 4, 5}; + const std::ranges::subrange a(data); + { + std::pair p(a); + assert(p.first == data + 0); + assert(p.second == data + 5); + } + { + std::pair p{a}; + assert(p.first == data + 0); + assert(p.second == data + 5); + } + { + std::pair p = a; + assert(p.first == data + 0); + assert(p.second == data + 5); + } + } + + // Check to std::tuple + { + int data[] = {1, 2, 3, 4, 5}; + const std::ranges::subrange a(data); + { + std::tuple p(a); + assert(std::get<0>(p) == data + 0); + assert(std::get<1>(p) == data + 5); + } + { + std::tuple p{a}; + assert(std::get<0>(p) == data + 0); + assert(std::get<1>(p) == data + 5); + } + { + std::tuple p = a; + assert(std::get<0>(p) == data + 0); + assert(std::get<1>(p) == data + 5); + } + } + + return true; +} + +int main(int, char**) { + test(); + static_assert(test()); + + return 0; +} diff --git a/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/uses_allocator_construction_args.pass.cpp b/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/uses_allocator_construction_args.pass.cpp index 0ad5e12e8e1c1..ab3c039497dd9 100644 --- a/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/uses_allocator_construction_args.pass.cpp +++ b/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/uses_allocator_construction_args.pass.cpp @@ -29,25 +29,27 @@ constexpr decltype(auto) test_uses_allocator_construction_args(Args&&... args) { return std::uses_allocator_construction_args(std::forward(args)...); } -constexpr bool test() { +template