Skip to content

[libc++] LWG 3821 uses_allocator_construction_args should have overload for pair-like #66939

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Oct 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions libcxx/docs/Status/Cxx23Issues.csv
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@
"`3629 <https://wg21.link/LWG3629>`__","``make_error_code`` and ``make_error_condition`` are customization points","November 2022","|Complete|","16.0",""
"`3636 <https://wg21.link/LWG3636>`__","``formatter<T>::format`` should be ``const``-qualified","November 2022","|Complete|","16.0","|format|"
"`3646 <https://wg21.link/LWG3646>`__","``std::ranges::view_interface::size`` returns a signed type","November 2022","|Complete|","16.0","|ranges|"
"`3677 <https://wg21.link/LWG3677>`__","Is a cv-qualified ``pair`` specially handled in uses-allocator construction?", "November 2022","","",""
"`3677 <https://wg21.link/LWG3677>`__","Is a cv-qualified ``pair`` specially handled in uses-allocator construction?", "November 2022","|Complete|","18.0",""
"`3717 <https://wg21.link/LWG3717>`__","``common_view::end`` should improve ``random_access_range`` case", "November 2022","","","|ranges|"
"`3732 <https://wg21.link/LWG3732>`__","``prepend_range`` and ``append_range`` can't be amortized constant time", "November 2022","|Nothing to do|","","|ranges|"
"`3736 <https://wg21.link/LWG3736>`__","``move_iterator`` missing ``disable_sized_sentinel_for`` specialization", "November 2022","","","|ranges|"
Expand Down Expand Up @@ -262,7 +262,7 @@
"`3742 <https://wg21.link/LWG3742>`__","``deque::prepend_range`` needs to permute","February 2023","","","|ranges|"
"`3790 <https://wg21.link/LWG3790>`__","`P1467 <https://wg21.link/P1467>`__ accidentally changed ``nexttoward``'s signature","February 2023","","",""
"`3819 <https://wg21.link/LWG3819>`__","``reference_meows_from_temporary`` should not use ``is_meowible``","February 2023","","",""
"`3821 <https://wg21.link/LWG3821>`__","``uses_allocator_construction_args`` should have overload for ``pair-like``","February 2023","","",""
"`3821 <https://wg21.link/LWG3821>`__","``uses_allocator_construction_args`` should have overload for ``pair-like``","February 2023","|Complete|","18.0.0",""
"`3834 <https://wg21.link/LWG3834>`__","Missing ``constexpr`` for ``std::intmax_t`` math functions in ``<cinttypes>``","February 2023","","",""
"`3839 <https://wg21.link/LWG3839>`__","``range_formatter``'s ``set_separator``, ``set_brackets``, and ``underlying`` functions should be ``noexcept``","February 2023","|Complete|","17.0","|format|"
"`3841 <https://wg21.link/LWG3841>`__","``<version>`` should not be ""all freestanding""","February 2023","","",""
Expand Down
69 changes: 49 additions & 20 deletions libcxx/include/__memory/uses_allocator_construction.h
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -36,23 +37,27 @@ inline constexpr bool __is_std_pair = false;
template <class _Type1, class _Type2>
inline constexpr bool __is_std_pair<pair<_Type1, _Type2>> = true;

template <class _Type, class _Alloc, class... _Args, __enable_if_t<!__is_std_pair<_Type>, int> = 0>
template <class _Tp>
inline constexpr bool __is_cv_std_pair = __is_std_pair<remove_cv_t<_Tp>>;

template <class _Type, class _Alloc, class... _Args, __enable_if_t<!__is_cv_std_pair<_Type>, 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<remove_cv_t<_Type>, _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<remove_cv_t<_Type>, _Alloc> &&
is_constructible_v<_Type, allocator_arg_t, const _Alloc&, _Args...>) {
return tuple<allocator_arg_t, const _Alloc&, _Args&&...>(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<remove_cv_t<_Type>, _Alloc> &&
is_constructible_v<_Type, _Args..., const _Alloc&>) {
return std::forward_as_tuple(std::forward<_Args>(__args)..., __alloc);
} else {
static_assert(
sizeof(_Type) + 1 == 0, "If uses_allocator_v<Type> is true, the type has to be allocator-constructible");
}
}

template <class _Pair, class _Alloc, class _Tuple1, class _Tuple2, __enable_if_t<__is_std_pair<_Pair>, int> = 0>
template <class _Pair, class _Alloc, class _Tuple1, class _Tuple2, __enable_if_t<__is_cv_std_pair<_Pair>, 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(
Expand All @@ -71,12 +76,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr auto __uses_allocator_construction_args(
std::forward<_Tuple2>(__y)));
}

template <class _Pair, class _Alloc, __enable_if_t<__is_std_pair<_Pair>, int> = 0>
template <class _Pair, class _Alloc, __enable_if_t<__is_cv_std_pair<_Pair>, 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 <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_std_pair<_Pair>, int> = 0>
template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_cv_std_pair<_Pair>, 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>(
Expand All @@ -87,22 +92,22 @@ __uses_allocator_construction_args(const _Alloc& __alloc, _Up&& __u, _Vp&& __v)
}

# if _LIBCPP_STD_VER >= 23
template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_std_pair<_Pair>, int> = 0>
template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_cv_std_pair<_Pair>, 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>(
__alloc, piecewise_construct, std::forward_as_tuple(__pair.first), std::forward_as_tuple(__pair.second));
}
# endif

template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_std_pair<_Pair>, int> = 0>
template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_cv_std_pair<_Pair>, 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 <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_std_pair<_Pair>, int> = 0>
template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_cv_std_pair<_Pair>, 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>(
Expand All @@ -113,7 +118,7 @@ __uses_allocator_construction_args(const _Alloc& __alloc, pair<_Up, _Vp>&& __pai
}

# if _LIBCPP_STD_VER >= 23
template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_std_pair<_Pair>, int> = 0>
template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_cv_std_pair<_Pair>, 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>(
Expand All @@ -122,6 +127,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<remove_cvref_t<_PairLike>>::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 {
Expand All @@ -139,23 +158,33 @@ template <class _Tp>
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 <class _Tp, class _Up>
inline constexpr bool __uses_allocator_constraints =
__is_cv_std_pair<_Tp> &&
(__is_specialization_of_subrange<remove_cvref_t<_Up>>::value ||
(!__pair_like<_Up> && !__convertible_to_const_pair_ref<_Up>));
# else
template <class _Tp, class _Up>
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 <class _Type, class _Alloc, class... _Args>
_LIBCPP_HIDE_FROM_ABI constexpr _Type __make_obj_using_allocator(const _Alloc& __alloc, _Args&&... __args);

template <class _Pair,
class _Alloc,
class _Type,
__enable_if_t<__is_std_pair<_Pair> && !__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 {
Expand Down
3 changes: 2 additions & 1 deletion libcxx/include/__utility/pair.h
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,8 @@ struct _LIBCPP_TEMPLATE_VIS pair
}

template <__pair_like _PairLike>
requires(is_constructible_v<first_type, decltype(std::get<0>(std::declval<_PairLike&&>()))> &&
requires(!__is_specialization_of_subrange<remove_cvref_t<_PairLike>>::value &&
is_constructible_v<first_type, decltype(std::get<0>(std::declval<_PairLike&&>()))> &&
is_constructible_v<second_type, decltype(std::get<1>(std::declval<_PairLike&&>()))>)
_LIBCPP_HIDE_FROM_ABI constexpr explicit(__pair_like_explicit_wknd<_PairLike>())
pair(_PairLike&& __p)
Expand Down
37 changes: 37 additions & 0 deletions libcxx/include/memory
Original file line number Diff line number Diff line change
Expand Up @@ -867,6 +867,43 @@ template <class T> struct hash<shared_ptr<T> >;
template <class T, class Alloc>
inline constexpr bool uses_allocator_v = uses_allocator<T, Alloc>::value;

// [allocator.uses.construction], uses-allocator construction
template<class T, class Alloc, class... Args>
constexpr auto uses_allocator_construction_args(const Alloc& alloc, // since C++20
Args&&... args) noexcept;
template<class T, class Alloc, class Tuple1, class Tuple2>
constexpr auto uses_allocator_construction_args(const Alloc& alloc, // since C++20
piecewise_construct_t,
Tuple1&& x, Tuple2&& y) noexcept;
template<class T, class Alloc>
constexpr auto uses_allocator_construction_args(const Alloc& alloc) noexcept; // since C++20
template<class T, class Alloc, class U, class V>
constexpr auto uses_allocator_construction_args(const Alloc& alloc, // since C++20
U&& u, V&& v) noexcept;
template<class T, class Alloc, class U, class V>
constexpr auto uses_allocator_construction_args(const Alloc& alloc, // since C++23
pair<U, V>& pr) noexcept;
template<class T, class Alloc, class U, class V>
constexpr auto uses_allocator_construction_args(const Alloc& alloc, // since C++20
const pair<U, V>& pr) noexcept;
template<class T, class Alloc, class U, class V>
constexpr auto uses_allocator_construction_args(const Alloc& alloc, // since C++20
pair<U, V>&& pr) noexcept;
template<class T, class Alloc, class U, class V>
constexpr auto uses_allocator_construction_args(const Alloc& alloc, // since C++23
const pair<U, V>&& pr) noexcept;
template<class T, class Alloc, pair-like P>
constexpr auto uses_allocator_construction_args(const Alloc& alloc, // since C++20
P&& p) noexcept;
template<class T, class Alloc, class U>
constexpr auto uses_allocator_construction_args(const Alloc& alloc, // since C++20
U&& u) noexcept;
template<class T, class Alloc, class... Args>
constexpr T make_obj_using_allocator(const Alloc& alloc, Args&&... args); // since C++20
template<class T, class Alloc, class... Args>
constexpr T* uninitialized_construct_using_allocator(T* p, // since C++20
const Alloc& alloc, Args&&... args);

// [ptr.align]
void* align(size_t alignment, size_t size, void*& ptr, size_t& space);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,9 @@ void func() {
std::numeric_limits<const volatile double>::has_denorm_loss; // expected-warning {{'has_denorm_loss' is deprecated}}
std::numeric_limits<const volatile double>::denorm_min();

std::numeric_limits<const volatile long double>::has_denorm; // expected-warning {{'has_denorm' is deprecated}}
std::numeric_limits<const volatile long double>::has_denorm_loss; // expected-warning {{'has_denorm_loss' is deprecated}}
std::numeric_limits<const volatile long double>::has_denorm; // expected-warning {{'has_denorm' is deprecated}}
std::numeric_limits<
const volatile long double>::has_denorm_loss; // expected-warning {{'has_denorm_loss' is deprecated}}
std::numeric_limits<const volatile long double>::denorm_min();

std::denorm_indeterminate; // expected-warning {{'denorm_indeterminate' is deprecated}}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<different-from<subrange> PairLike>
// requires pair-like-convertible-from<PairLike, const I&, const S&>
// constexpr operator PairLike() const;

#include <cassert>
#include <concepts>
#include <ranges>

#include "test_macros.h"

static_assert(std::convertible_to<std::ranges::subrange<int*>, std::pair<int*, int*>>);
static_assert(std::convertible_to<std::ranges::subrange<int*>, std::tuple<int*, int*>>);
static_assert(!std::convertible_to<std::ranges::subrange<int*>, std::pair<long*, int*>>);
static_assert(!std::convertible_to<std::ranges::subrange<int*>, std::pair<int*, long*>>);
static_assert(!std::convertible_to<std::ranges::subrange<int*>, std::pair<long*, long*>>);
static_assert(!std::convertible_to<std::ranges::subrange<int*>, std::array<int*, 2>>);

constexpr bool test() {
// Check to std::pair
{
int data[] = {1, 2, 3, 4, 5};
const std::ranges::subrange a(data);
{
std::pair<int*, int*> p(a);
assert(p.first == data + 0);
assert(p.second == data + 5);
}
{
std::pair<int*, int*> p{a};
assert(p.first == data + 0);
assert(p.second == data + 5);
}
{
std::pair<int*, int*> 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<int*, int*> p(a);
assert(std::get<0>(p) == data + 0);
assert(std::get<1>(p) == data + 5);
}
{
std::tuple<int*, int*> p{a};
assert(std::get<0>(p) == data + 0);
assert(std::get<1>(p) == data + 5);
}
{
std::tuple<int*, int*> 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;
}
Loading