Skip to content

Commit bcf172e

Browse files
authored
[libc++] LWG 3821 uses_allocator_construction_args should have overload for pair-like (#66939)
This change addresses LWG 3821 and LWG 3677. - make `std::pair`'s constructor no longer takes `subrange` - `uses_allocator_construction_args` constraint changes w.r.t to `pair-like` types - `uses_allocator_construction_args` constraints checks `is-pair-like<remove_cv_t<T>>`
1 parent a56071f commit bcf172e

File tree

9 files changed

+238
-59
lines changed

9 files changed

+238
-59
lines changed

libcxx/docs/Status/Cxx23Issues.csv

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@
198198
"`3629 <https://wg21.link/LWG3629>`__","``make_error_code`` and ``make_error_condition`` are customization points","November 2022","|Complete|","16.0",""
199199
"`3636 <https://wg21.link/LWG3636>`__","``formatter<T>::format`` should be ``const``-qualified","November 2022","|Complete|","16.0","|format|"
200200
"`3646 <https://wg21.link/LWG3646>`__","``std::ranges::view_interface::size`` returns a signed type","November 2022","|Complete|","16.0","|ranges|"
201-
"`3677 <https://wg21.link/LWG3677>`__","Is a cv-qualified ``pair`` specially handled in uses-allocator construction?", "November 2022","","",""
201+
"`3677 <https://wg21.link/LWG3677>`__","Is a cv-qualified ``pair`` specially handled in uses-allocator construction?", "November 2022","|Complete|","18.0",""
202202
"`3717 <https://wg21.link/LWG3717>`__","``common_view::end`` should improve ``random_access_range`` case", "November 2022","","","|ranges|"
203203
"`3732 <https://wg21.link/LWG3732>`__","``prepend_range`` and ``append_range`` can't be amortized constant time", "November 2022","|Nothing to do|","","|ranges|"
204204
"`3736 <https://wg21.link/LWG3736>`__","``move_iterator`` missing ``disable_sized_sentinel_for`` specialization", "November 2022","","","|ranges|"
@@ -262,7 +262,7 @@
262262
"`3742 <https://wg21.link/LWG3742>`__","``deque::prepend_range`` needs to permute","February 2023","","","|ranges|"
263263
"`3790 <https://wg21.link/LWG3790>`__","`P1467 <https://wg21.link/P1467>`__ accidentally changed ``nexttoward``'s signature","February 2023","","",""
264264
"`3819 <https://wg21.link/LWG3819>`__","``reference_meows_from_temporary`` should not use ``is_meowible``","February 2023","","",""
265-
"`3821 <https://wg21.link/LWG3821>`__","``uses_allocator_construction_args`` should have overload for ``pair-like``","February 2023","","",""
265+
"`3821 <https://wg21.link/LWG3821>`__","``uses_allocator_construction_args`` should have overload for ``pair-like``","February 2023","|Complete|","18.0.0",""
266266
"`3834 <https://wg21.link/LWG3834>`__","Missing ``constexpr`` for ``std::intmax_t`` math functions in ``<cinttypes>``","February 2023","","",""
267267
"`3839 <https://wg21.link/LWG3839>`__","``range_formatter``'s ``set_separator``, ``set_brackets``, and ``underlying`` functions should be ``noexcept``","February 2023","|Complete|","17.0","|format|"
268268
"`3841 <https://wg21.link/LWG3841>`__","``<version>`` should not be ""all freestanding""","February 2023","","",""

libcxx/include/__memory/uses_allocator_construction.h

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <__config>
1313
#include <__memory/construct_at.h>
1414
#include <__memory/uses_allocator.h>
15+
#include <__tuple/pair_like.h>
1516
#include <__type_traits/enable_if.h>
1617
#include <__type_traits/is_same.h>
1718
#include <__type_traits/remove_cv.h>
@@ -36,23 +37,27 @@ inline constexpr bool __is_std_pair = false;
3637
template <class _Type1, class _Type2>
3738
inline constexpr bool __is_std_pair<pair<_Type1, _Type2>> = true;
3839

39-
template <class _Type, class _Alloc, class... _Args, __enable_if_t<!__is_std_pair<_Type>, int> = 0>
40+
template <class _Tp>
41+
inline constexpr bool __is_cv_std_pair = __is_std_pair<remove_cv_t<_Tp>>;
42+
43+
template <class _Type, class _Alloc, class... _Args, __enable_if_t<!__is_cv_std_pair<_Type>, int> = 0>
4044
_LIBCPP_HIDE_FROM_ABI constexpr auto
4145
__uses_allocator_construction_args(const _Alloc& __alloc, _Args&&... __args) noexcept {
42-
if constexpr (!uses_allocator_v<_Type, _Alloc> && is_constructible_v<_Type, _Args...>) {
46+
if constexpr (!uses_allocator_v<remove_cv_t<_Type>, _Alloc> && is_constructible_v<_Type, _Args...>) {
4347
return std::forward_as_tuple(std::forward<_Args>(__args)...);
44-
} else if constexpr (uses_allocator_v<_Type, _Alloc> &&
48+
} else if constexpr (uses_allocator_v<remove_cv_t<_Type>, _Alloc> &&
4549
is_constructible_v<_Type, allocator_arg_t, const _Alloc&, _Args...>) {
4650
return tuple<allocator_arg_t, const _Alloc&, _Args&&...>(allocator_arg, __alloc, std::forward<_Args>(__args)...);
47-
} else if constexpr (uses_allocator_v<_Type, _Alloc> && is_constructible_v<_Type, _Args..., const _Alloc&>) {
51+
} else if constexpr (uses_allocator_v<remove_cv_t<_Type>, _Alloc> &&
52+
is_constructible_v<_Type, _Args..., const _Alloc&>) {
4853
return std::forward_as_tuple(std::forward<_Args>(__args)..., __alloc);
4954
} else {
5055
static_assert(
5156
sizeof(_Type) + 1 == 0, "If uses_allocator_v<Type> is true, the type has to be allocator-constructible");
5257
}
5358
}
5459

55-
template <class _Pair, class _Alloc, class _Tuple1, class _Tuple2, __enable_if_t<__is_std_pair<_Pair>, int> = 0>
60+
template <class _Pair, class _Alloc, class _Tuple1, class _Tuple2, __enable_if_t<__is_cv_std_pair<_Pair>, int> = 0>
5661
_LIBCPP_HIDE_FROM_ABI constexpr auto __uses_allocator_construction_args(
5762
const _Alloc& __alloc, piecewise_construct_t, _Tuple1&& __x, _Tuple2&& __y) noexcept {
5863
return std::make_tuple(
@@ -71,12 +76,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr auto __uses_allocator_construction_args(
7176
std::forward<_Tuple2>(__y)));
7277
}
7378

74-
template <class _Pair, class _Alloc, __enable_if_t<__is_std_pair<_Pair>, int> = 0>
79+
template <class _Pair, class _Alloc, __enable_if_t<__is_cv_std_pair<_Pair>, int> = 0>
7580
_LIBCPP_HIDE_FROM_ABI constexpr auto __uses_allocator_construction_args(const _Alloc& __alloc) noexcept {
7681
return std::__uses_allocator_construction_args<_Pair>(__alloc, piecewise_construct, tuple<>{}, tuple<>{});
7782
}
7883

79-
template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_std_pair<_Pair>, int> = 0>
84+
template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_cv_std_pair<_Pair>, int> = 0>
8085
_LIBCPP_HIDE_FROM_ABI constexpr auto
8186
__uses_allocator_construction_args(const _Alloc& __alloc, _Up&& __u, _Vp&& __v) noexcept {
8287
return std::__uses_allocator_construction_args<_Pair>(
@@ -87,22 +92,22 @@ __uses_allocator_construction_args(const _Alloc& __alloc, _Up&& __u, _Vp&& __v)
8792
}
8893

8994
# if _LIBCPP_STD_VER >= 23
90-
template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_std_pair<_Pair>, int> = 0>
95+
template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_cv_std_pair<_Pair>, int> = 0>
9196
_LIBCPP_HIDE_FROM_ABI constexpr auto
9297
__uses_allocator_construction_args(const _Alloc& __alloc, pair<_Up, _Vp>& __pair) noexcept {
9398
return std::__uses_allocator_construction_args<_Pair>(
9499
__alloc, piecewise_construct, std::forward_as_tuple(__pair.first), std::forward_as_tuple(__pair.second));
95100
}
96101
# endif
97102

98-
template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_std_pair<_Pair>, int> = 0>
103+
template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_cv_std_pair<_Pair>, int> = 0>
99104
_LIBCPP_HIDE_FROM_ABI constexpr auto
100105
__uses_allocator_construction_args(const _Alloc& __alloc, const pair<_Up, _Vp>& __pair) noexcept {
101106
return std::__uses_allocator_construction_args<_Pair>(
102107
__alloc, piecewise_construct, std::forward_as_tuple(__pair.first), std::forward_as_tuple(__pair.second));
103108
}
104109

105-
template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_std_pair<_Pair>, int> = 0>
110+
template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_cv_std_pair<_Pair>, int> = 0>
106111
_LIBCPP_HIDE_FROM_ABI constexpr auto
107112
__uses_allocator_construction_args(const _Alloc& __alloc, pair<_Up, _Vp>&& __pair) noexcept {
108113
return std::__uses_allocator_construction_args<_Pair>(
@@ -113,7 +118,7 @@ __uses_allocator_construction_args(const _Alloc& __alloc, pair<_Up, _Vp>&& __pai
113118
}
114119

115120
# if _LIBCPP_STD_VER >= 23
116-
template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_std_pair<_Pair>, int> = 0>
121+
template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_cv_std_pair<_Pair>, int> = 0>
117122
_LIBCPP_HIDE_FROM_ABI constexpr auto
118123
__uses_allocator_construction_args(const _Alloc& __alloc, const pair<_Up, _Vp>&& __pair) noexcept {
119124
return std::__uses_allocator_construction_args<_Pair>(
@@ -122,6 +127,20 @@ __uses_allocator_construction_args(const _Alloc& __alloc, const pair<_Up, _Vp>&&
122127
std::forward_as_tuple(std::get<0>(std::move(__pair))),
123128
std::forward_as_tuple(std::get<1>(std::move(__pair))));
124129
}
130+
131+
template < class _Pair,
132+
class _Alloc,
133+
__pair_like _PairLike,
134+
__enable_if_t<__is_cv_std_pair<_Pair> && !__is_specialization_of_subrange<remove_cvref_t<_PairLike>>::value,
135+
int> = 0>
136+
_LIBCPP_HIDE_FROM_ABI constexpr auto
137+
__uses_allocator_construction_args(const _Alloc& __alloc, _PairLike&& __p) noexcept {
138+
return std::__uses_allocator_construction_args<_Pair>(
139+
__alloc,
140+
piecewise_construct,
141+
std::forward_as_tuple(std::get<0>(std::forward<_PairLike>(__p))),
142+
std::forward_as_tuple(std::get<1>(std::forward<_PairLike>(__p))));
143+
}
125144
# endif
126145

127146
namespace __uses_allocator_detail {
@@ -139,23 +158,33 @@ template <class _Tp>
139158
inline constexpr bool __convertible_to_const_pair_ref =
140159
decltype(__uses_allocator_detail::__convertible_to_const_pair_ref_impl<_Tp>(0))::value;
141160

161+
# if _LIBCPP_STD_VER >= 23
162+
template <class _Tp, class _Up>
163+
inline constexpr bool __uses_allocator_constraints =
164+
__is_cv_std_pair<_Tp> &&
165+
(__is_specialization_of_subrange<remove_cvref_t<_Up>>::value ||
166+
(!__pair_like<_Up> && !__convertible_to_const_pair_ref<_Up>));
167+
# else
168+
template <class _Tp, class _Up>
169+
inline constexpr bool __uses_allocator_constraints = __is_cv_std_pair<_Tp> && !__convertible_to_const_pair_ref<_Up>;
170+
# endif
171+
142172
} // namespace __uses_allocator_detail
143173

144-
template <
145-
class _Pair,
146-
class _Alloc,
147-
class _Type,
148-
__enable_if_t<__is_std_pair<_Pair> && !__uses_allocator_detail::__convertible_to_const_pair_ref<_Type>, int> = 0>
174+
template < class _Pair,
175+
class _Alloc,
176+
class _Type,
177+
__enable_if_t<__uses_allocator_detail::__uses_allocator_constraints<_Pair, _Type>, int> = 0>
149178
_LIBCPP_HIDE_FROM_ABI constexpr auto
150179
__uses_allocator_construction_args(const _Alloc& __alloc, _Type&& __value) noexcept;
151180

152181
template <class _Type, class _Alloc, class... _Args>
153182
_LIBCPP_HIDE_FROM_ABI constexpr _Type __make_obj_using_allocator(const _Alloc& __alloc, _Args&&... __args);
154183

155-
template <class _Pair,
156-
class _Alloc,
157-
class _Type,
158-
__enable_if_t<__is_std_pair<_Pair> && !__uses_allocator_detail::__convertible_to_const_pair_ref<_Type>, int>>
184+
template < class _Pair,
185+
class _Alloc,
186+
class _Type,
187+
__enable_if_t< __uses_allocator_detail::__uses_allocator_constraints<_Pair, _Type>, int>>
159188
_LIBCPP_HIDE_FROM_ABI constexpr auto
160189
__uses_allocator_construction_args(const _Alloc& __alloc, _Type&& __value) noexcept {
161190
struct __pair_constructor {

libcxx/include/__utility/pair.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,8 @@ struct _LIBCPP_TEMPLATE_VIS pair
284284
}
285285

286286
template <__pair_like _PairLike>
287-
requires(is_constructible_v<first_type, decltype(std::get<0>(std::declval<_PairLike&&>()))> &&
287+
requires(!__is_specialization_of_subrange<remove_cvref_t<_PairLike>>::value &&
288+
is_constructible_v<first_type, decltype(std::get<0>(std::declval<_PairLike&&>()))> &&
288289
is_constructible_v<second_type, decltype(std::get<1>(std::declval<_PairLike&&>()))>)
289290
_LIBCPP_HIDE_FROM_ABI constexpr explicit(__pair_like_explicit_wknd<_PairLike>())
290291
pair(_PairLike&& __p)

libcxx/include/memory

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -867,6 +867,43 @@ template <class T> struct hash<shared_ptr<T> >;
867867
template <class T, class Alloc>
868868
inline constexpr bool uses_allocator_v = uses_allocator<T, Alloc>::value;
869869
870+
// [allocator.uses.construction], uses-allocator construction
871+
template<class T, class Alloc, class... Args>
872+
constexpr auto uses_allocator_construction_args(const Alloc& alloc, // since C++20
873+
Args&&... args) noexcept;
874+
template<class T, class Alloc, class Tuple1, class Tuple2>
875+
constexpr auto uses_allocator_construction_args(const Alloc& alloc, // since C++20
876+
piecewise_construct_t,
877+
Tuple1&& x, Tuple2&& y) noexcept;
878+
template<class T, class Alloc>
879+
constexpr auto uses_allocator_construction_args(const Alloc& alloc) noexcept; // since C++20
880+
template<class T, class Alloc, class U, class V>
881+
constexpr auto uses_allocator_construction_args(const Alloc& alloc, // since C++20
882+
U&& u, V&& v) noexcept;
883+
template<class T, class Alloc, class U, class V>
884+
constexpr auto uses_allocator_construction_args(const Alloc& alloc, // since C++23
885+
pair<U, V>& pr) noexcept;
886+
template<class T, class Alloc, class U, class V>
887+
constexpr auto uses_allocator_construction_args(const Alloc& alloc, // since C++20
888+
const pair<U, V>& pr) noexcept;
889+
template<class T, class Alloc, class U, class V>
890+
constexpr auto uses_allocator_construction_args(const Alloc& alloc, // since C++20
891+
pair<U, V>&& pr) noexcept;
892+
template<class T, class Alloc, class U, class V>
893+
constexpr auto uses_allocator_construction_args(const Alloc& alloc, // since C++23
894+
const pair<U, V>&& pr) noexcept;
895+
template<class T, class Alloc, pair-like P>
896+
constexpr auto uses_allocator_construction_args(const Alloc& alloc, // since C++20
897+
P&& p) noexcept;
898+
template<class T, class Alloc, class U>
899+
constexpr auto uses_allocator_construction_args(const Alloc& alloc, // since C++20
900+
U&& u) noexcept;
901+
template<class T, class Alloc, class... Args>
902+
constexpr T make_obj_using_allocator(const Alloc& alloc, Args&&... args); // since C++20
903+
template<class T, class Alloc, class... Args>
904+
constexpr T* uninitialized_construct_using_allocator(T* p, // since C++20
905+
const Alloc& alloc, Args&&... args);
906+
870907
// [ptr.align]
871908
void* align(size_t alignment, size_t size, void*& ptr, size_t& space);
872909

libcxx/test/std/depr/depr.numeric.imits.has.denorm/deprecated.verify.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,9 @@ void func() {
9191
std::numeric_limits<const volatile double>::has_denorm_loss; // expected-warning {{'has_denorm_loss' is deprecated}}
9292
std::numeric_limits<const volatile double>::denorm_min();
9393

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

9899
std::denorm_indeterminate; // expected-warning {{'denorm_indeterminate' is deprecated}}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
2+
//===----------------------------------------------------------------------===//
3+
//
4+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
// UNSUPPORTED: c++03, c++11, c++14, c++17
11+
12+
// template<different-from<subrange> PairLike>
13+
// requires pair-like-convertible-from<PairLike, const I&, const S&>
14+
// constexpr operator PairLike() const;
15+
16+
#include <cassert>
17+
#include <concepts>
18+
#include <ranges>
19+
20+
#include "test_macros.h"
21+
22+
static_assert(std::convertible_to<std::ranges::subrange<int*>, std::pair<int*, int*>>);
23+
static_assert(std::convertible_to<std::ranges::subrange<int*>, std::tuple<int*, int*>>);
24+
static_assert(!std::convertible_to<std::ranges::subrange<int*>, std::pair<long*, int*>>);
25+
static_assert(!std::convertible_to<std::ranges::subrange<int*>, std::pair<int*, long*>>);
26+
static_assert(!std::convertible_to<std::ranges::subrange<int*>, std::pair<long*, long*>>);
27+
static_assert(!std::convertible_to<std::ranges::subrange<int*>, std::array<int*, 2>>);
28+
29+
constexpr bool test() {
30+
// Check to std::pair
31+
{
32+
int data[] = {1, 2, 3, 4, 5};
33+
const std::ranges::subrange a(data);
34+
{
35+
std::pair<int*, int*> p(a);
36+
assert(p.first == data + 0);
37+
assert(p.second == data + 5);
38+
}
39+
{
40+
std::pair<int*, int*> p{a};
41+
assert(p.first == data + 0);
42+
assert(p.second == data + 5);
43+
}
44+
{
45+
std::pair<int*, int*> p = a;
46+
assert(p.first == data + 0);
47+
assert(p.second == data + 5);
48+
}
49+
}
50+
51+
// Check to std::tuple
52+
{
53+
int data[] = {1, 2, 3, 4, 5};
54+
const std::ranges::subrange a(data);
55+
{
56+
std::tuple<int*, int*> p(a);
57+
assert(std::get<0>(p) == data + 0);
58+
assert(std::get<1>(p) == data + 5);
59+
}
60+
{
61+
std::tuple<int*, int*> p{a};
62+
assert(std::get<0>(p) == data + 0);
63+
assert(std::get<1>(p) == data + 5);
64+
}
65+
{
66+
std::tuple<int*, int*> p = a;
67+
assert(std::get<0>(p) == data + 0);
68+
assert(std::get<1>(p) == data + 5);
69+
}
70+
}
71+
72+
return true;
73+
}
74+
75+
int main(int, char**) {
76+
test();
77+
static_assert(test());
78+
79+
return 0;
80+
}

0 commit comments

Comments
 (0)