Skip to content

Commit beecf2c

Browse files
phyBracketsldionne
andauthored
[libc++] Fix missing declarations of uses_allocator_construction_args (#67044)
We were not declaring `__uses_allocator_construction_args` helper functions, leading to several valid uses failing to compile. This patch solves the problem by moving these helper functions into a struct, which also reduces the amount of redundant SFINAE we need to perform since most overloads are checking for a cv-qualfied pair. Fixes #66714 Co-authored-by: Louis Dionne <[email protected]>
1 parent d10dc5a commit beecf2c

File tree

2 files changed

+179
-129
lines changed

2 files changed

+179
-129
lines changed

libcxx/include/__memory/uses_allocator_construction.h

Lines changed: 122 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -40,104 +40,8 @@ inline constexpr bool __is_std_pair<pair<_Type1, _Type2>> = true;
4040
template <class _Tp>
4141
inline constexpr bool __is_cv_std_pair = __is_std_pair<remove_cv_t<_Tp>>;
4242

43-
template <class _Type, class _Alloc, class... _Args, __enable_if_t<!__is_cv_std_pair<_Type>, int> = 0>
44-
_LIBCPP_HIDE_FROM_ABI constexpr auto
45-
__uses_allocator_construction_args(const _Alloc& __alloc, _Args&&... __args) noexcept {
46-
if constexpr (!uses_allocator_v<remove_cv_t<_Type>, _Alloc> && is_constructible_v<_Type, _Args...>) {
47-
return std::forward_as_tuple(std::forward<_Args>(__args)...);
48-
} else if constexpr (uses_allocator_v<remove_cv_t<_Type>, _Alloc> &&
49-
is_constructible_v<_Type, allocator_arg_t, const _Alloc&, _Args...>) {
50-
return tuple<allocator_arg_t, const _Alloc&, _Args&&...>(allocator_arg, __alloc, std::forward<_Args>(__args)...);
51-
} else if constexpr (uses_allocator_v<remove_cv_t<_Type>, _Alloc> &&
52-
is_constructible_v<_Type, _Args..., const _Alloc&>) {
53-
return std::forward_as_tuple(std::forward<_Args>(__args)..., __alloc);
54-
} else {
55-
static_assert(
56-
sizeof(_Type) + 1 == 0, "If uses_allocator_v<Type> is true, the type has to be allocator-constructible");
57-
}
58-
}
59-
60-
template <class _Pair, class _Alloc, class _Tuple1, class _Tuple2, __enable_if_t<__is_cv_std_pair<_Pair>, int> = 0>
61-
_LIBCPP_HIDE_FROM_ABI constexpr auto __uses_allocator_construction_args(
62-
const _Alloc& __alloc, piecewise_construct_t, _Tuple1&& __x, _Tuple2&& __y) noexcept {
63-
return std::make_tuple(
64-
piecewise_construct,
65-
std::apply(
66-
[&__alloc](auto&&... __args1) {
67-
return std::__uses_allocator_construction_args<typename _Pair::first_type>(
68-
__alloc, std::forward<decltype(__args1)>(__args1)...);
69-
},
70-
std::forward<_Tuple1>(__x)),
71-
std::apply(
72-
[&__alloc](auto&&... __args2) {
73-
return std::__uses_allocator_construction_args<typename _Pair::second_type>(
74-
__alloc, std::forward<decltype(__args2)>(__args2)...);
75-
},
76-
std::forward<_Tuple2>(__y)));
77-
}
78-
79-
template <class _Pair, class _Alloc, __enable_if_t<__is_cv_std_pair<_Pair>, int> = 0>
80-
_LIBCPP_HIDE_FROM_ABI constexpr auto __uses_allocator_construction_args(const _Alloc& __alloc) noexcept {
81-
return std::__uses_allocator_construction_args<_Pair>(__alloc, piecewise_construct, tuple<>{}, tuple<>{});
82-
}
83-
84-
template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_cv_std_pair<_Pair>, int> = 0>
85-
_LIBCPP_HIDE_FROM_ABI constexpr auto
86-
__uses_allocator_construction_args(const _Alloc& __alloc, _Up&& __u, _Vp&& __v) noexcept {
87-
return std::__uses_allocator_construction_args<_Pair>(
88-
__alloc,
89-
piecewise_construct,
90-
std::forward_as_tuple(std::forward<_Up>(__u)),
91-
std::forward_as_tuple(std::forward<_Vp>(__v)));
92-
}
93-
94-
# if _LIBCPP_STD_VER >= 23
95-
template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_cv_std_pair<_Pair>, int> = 0>
96-
_LIBCPP_HIDE_FROM_ABI constexpr auto
97-
__uses_allocator_construction_args(const _Alloc& __alloc, pair<_Up, _Vp>& __pair) noexcept {
98-
return std::__uses_allocator_construction_args<_Pair>(
99-
__alloc, piecewise_construct, std::forward_as_tuple(__pair.first), std::forward_as_tuple(__pair.second));
100-
}
101-
# endif
102-
103-
template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_cv_std_pair<_Pair>, int> = 0>
104-
_LIBCPP_HIDE_FROM_ABI constexpr auto
105-
__uses_allocator_construction_args(const _Alloc& __alloc, const pair<_Up, _Vp>& __pair) noexcept {
106-
return std::__uses_allocator_construction_args<_Pair>(
107-
__alloc, piecewise_construct, std::forward_as_tuple(__pair.first), std::forward_as_tuple(__pair.second));
108-
}
109-
110-
template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_cv_std_pair<_Pair>, int> = 0>
111-
_LIBCPP_HIDE_FROM_ABI constexpr auto
112-
__uses_allocator_construction_args(const _Alloc& __alloc, pair<_Up, _Vp>&& __pair) noexcept {
113-
return std::__uses_allocator_construction_args<_Pair>(
114-
__alloc,
115-
piecewise_construct,
116-
std::forward_as_tuple(std::get<0>(std::move(__pair))),
117-
std::forward_as_tuple(std::get<1>(std::move(__pair))));
118-
}
119-
120-
# if _LIBCPP_STD_VER >= 23
121-
template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_cv_std_pair<_Pair>, int> = 0>
122-
_LIBCPP_HIDE_FROM_ABI constexpr auto
123-
__uses_allocator_construction_args(const _Alloc& __alloc, const pair<_Up, _Vp>&& __pair) noexcept {
124-
return std::__uses_allocator_construction_args<_Pair>(
125-
__alloc,
126-
piecewise_construct,
127-
std::forward_as_tuple(std::get<0>(std::move(__pair))),
128-
std::forward_as_tuple(std::get<1>(std::move(__pair))));
129-
}
130-
131-
template <class _Pair, class _Alloc, __pair_like_no_subrange _PairLike, __enable_if_t<__is_cv_std_pair<_Pair>, int> = 0>
132-
_LIBCPP_HIDE_FROM_ABI constexpr auto
133-
__uses_allocator_construction_args(const _Alloc& __alloc, _PairLike&& __p) noexcept {
134-
return std::__uses_allocator_construction_args<_Pair>(
135-
__alloc,
136-
piecewise_construct,
137-
std::forward_as_tuple(std::get<0>(std::forward<_PairLike>(__p))),
138-
std::forward_as_tuple(std::get<1>(std::forward<_PairLike>(__p))));
139-
}
140-
# endif
43+
template <class _Tp, class = void>
44+
struct __uses_allocator_construction_args;
14145

14246
namespace __uses_allocator_detail {
14347

@@ -165,54 +69,143 @@ inline constexpr bool __uses_allocator_constraints = __is_cv_std_pair<_Tp> && !_
16569

16670
} // namespace __uses_allocator_detail
16771

168-
template < class _Pair,
169-
class _Alloc,
170-
class _Type,
171-
__enable_if_t<__uses_allocator_detail::__uses_allocator_constraints<_Pair, _Type>, int> = 0>
172-
_LIBCPP_HIDE_FROM_ABI constexpr auto
173-
__uses_allocator_construction_args(const _Alloc& __alloc, _Type&& __value) noexcept;
174-
17572
template <class _Type, class _Alloc, class... _Args>
17673
_LIBCPP_HIDE_FROM_ABI constexpr _Type __make_obj_using_allocator(const _Alloc& __alloc, _Args&&... __args);
17774

178-
template < class _Pair,
179-
class _Alloc,
180-
class _Type,
181-
__enable_if_t< __uses_allocator_detail::__uses_allocator_constraints<_Pair, _Type>, int>>
182-
_LIBCPP_HIDE_FROM_ABI constexpr auto
183-
__uses_allocator_construction_args(const _Alloc& __alloc, _Type&& __value) noexcept {
184-
struct __pair_constructor {
185-
using _PairMutable = remove_cv_t<_Pair>;
75+
template <class _Pair>
76+
struct __uses_allocator_construction_args<_Pair, __enable_if_t<__is_cv_std_pair<_Pair>>> {
77+
template <class _Alloc, class _Tuple1, class _Tuple2>
78+
static _LIBCPP_HIDE_FROM_ABI constexpr auto
79+
__apply(const _Alloc& __alloc, piecewise_construct_t, _Tuple1&& __x, _Tuple2&& __y) noexcept {
80+
return std::make_tuple(
81+
piecewise_construct,
82+
std::apply(
83+
[&__alloc](auto&&... __args1) {
84+
return __uses_allocator_construction_args<typename _Pair::first_type>::__apply(
85+
__alloc, std::forward<decltype(__args1)>(__args1)...);
86+
},
87+
std::forward<_Tuple1>(__x)),
88+
std::apply(
89+
[&__alloc](auto&&... __args2) {
90+
return __uses_allocator_construction_args<typename _Pair::second_type>::__apply(
91+
__alloc, std::forward<decltype(__args2)>(__args2)...);
92+
},
93+
std::forward<_Tuple2>(__y)));
94+
}
18695

187-
_LIBCPP_HIDDEN constexpr auto __do_construct(const _PairMutable& __pair) const {
188-
return std::__make_obj_using_allocator<_PairMutable>(__alloc_, __pair);
189-
}
96+
template <class _Alloc>
97+
static _LIBCPP_HIDE_FROM_ABI constexpr auto __apply(const _Alloc& __alloc) noexcept {
98+
return __uses_allocator_construction_args<_Pair>::__apply(__alloc, piecewise_construct, tuple<>{}, tuple<>{});
99+
}
190100

191-
_LIBCPP_HIDDEN constexpr auto __do_construct(_PairMutable&& __pair) const {
192-
return std::__make_obj_using_allocator<_PairMutable>(__alloc_, std::move(__pair));
193-
}
101+
template <class _Alloc, class _Up, class _Vp>
102+
static _LIBCPP_HIDE_FROM_ABI constexpr auto __apply(const _Alloc& __alloc, _Up&& __u, _Vp&& __v) noexcept {
103+
return __uses_allocator_construction_args<_Pair>::__apply(
104+
__alloc,
105+
piecewise_construct,
106+
std::forward_as_tuple(std::forward<_Up>(__u)),
107+
std::forward_as_tuple(std::forward<_Vp>(__v)));
108+
}
194109

195-
const _Alloc& __alloc_;
196-
_Type& __value_;
110+
# if _LIBCPP_STD_VER >= 23
111+
template <class _Alloc, class _Up, class _Vp>
112+
static _LIBCPP_HIDE_FROM_ABI constexpr auto __apply(const _Alloc& __alloc, pair<_Up, _Vp>& __pair) noexcept {
113+
return __uses_allocator_construction_args<_Pair>::__apply(
114+
__alloc, piecewise_construct, std::forward_as_tuple(__pair.first), std::forward_as_tuple(__pair.second));
115+
}
116+
# endif
197117

198-
_LIBCPP_HIDDEN constexpr operator _PairMutable() const { return __do_construct(std::forward<_Type>(__value_)); }
199-
};
118+
template <class _Alloc, class _Up, class _Vp>
119+
static _LIBCPP_HIDE_FROM_ABI constexpr auto __apply(const _Alloc& __alloc, const pair<_Up, _Vp>& __pair) noexcept {
120+
return __uses_allocator_construction_args<_Pair>::__apply(
121+
__alloc, piecewise_construct, std::forward_as_tuple(__pair.first), std::forward_as_tuple(__pair.second));
122+
}
200123

201-
return std::make_tuple(__pair_constructor{__alloc, __value});
202-
}
124+
template <class _Alloc, class _Up, class _Vp>
125+
static _LIBCPP_HIDE_FROM_ABI constexpr auto __apply(const _Alloc& __alloc, pair<_Up, _Vp>&& __pair) noexcept {
126+
return __uses_allocator_construction_args<_Pair>::__apply(
127+
__alloc,
128+
piecewise_construct,
129+
std::forward_as_tuple(std::get<0>(std::move(__pair))),
130+
std::forward_as_tuple(std::get<1>(std::move(__pair))));
131+
}
132+
133+
# if _LIBCPP_STD_VER >= 23
134+
template <class _Alloc, class _Up, class _Vp>
135+
static _LIBCPP_HIDE_FROM_ABI constexpr auto __apply(const _Alloc& __alloc, const pair<_Up, _Vp>&& __pair) noexcept {
136+
return __uses_allocator_construction_args<_Pair>::__apply(
137+
__alloc,
138+
piecewise_construct,
139+
std::forward_as_tuple(std::get<0>(std::move(__pair))),
140+
std::forward_as_tuple(std::get<1>(std::move(__pair))));
141+
}
142+
143+
template < class _Alloc, __pair_like_no_subrange _PairLike>
144+
static _LIBCPP_HIDE_FROM_ABI constexpr auto __apply(const _Alloc& __alloc, _PairLike&& __p) noexcept {
145+
return __uses_allocator_construction_args<_Pair>::__apply(
146+
__alloc,
147+
piecewise_construct,
148+
std::forward_as_tuple(std::get<0>(std::forward<_PairLike>(__p))),
149+
std::forward_as_tuple(std::get<1>(std::forward<_PairLike>(__p))));
150+
}
151+
# endif
152+
153+
template <class _Alloc,
154+
class _Type,
155+
__enable_if_t<__uses_allocator_detail::__uses_allocator_constraints<_Pair, _Type>, int> = 0>
156+
static _LIBCPP_HIDE_FROM_ABI constexpr auto __apply(const _Alloc& __alloc, _Type&& __value) noexcept {
157+
struct __pair_constructor {
158+
using _PairMutable = remove_cv_t<_Pair>;
159+
160+
_LIBCPP_HIDDEN constexpr auto __do_construct(const _PairMutable& __pair) const {
161+
return std::__make_obj_using_allocator<_PairMutable>(__alloc_, __pair);
162+
}
163+
164+
_LIBCPP_HIDDEN constexpr auto __do_construct(_PairMutable&& __pair) const {
165+
return std::__make_obj_using_allocator<_PairMutable>(__alloc_, std::move(__pair));
166+
}
167+
168+
const _Alloc& __alloc_;
169+
_Type& __value_;
170+
171+
_LIBCPP_HIDDEN constexpr operator _PairMutable() const { return __do_construct(std::forward<_Type>(__value_)); }
172+
};
173+
174+
return std::make_tuple(__pair_constructor{__alloc, __value});
175+
}
176+
};
177+
178+
template <class _Type>
179+
struct __uses_allocator_construction_args<_Type, __enable_if_t<!__is_cv_std_pair<_Type>>> {
180+
template <class _Alloc, class... _Args>
181+
static _LIBCPP_HIDE_FROM_ABI constexpr auto __apply(const _Alloc& __alloc, _Args&&... __args) noexcept {
182+
if constexpr (!uses_allocator_v<remove_cv_t<_Type>, _Alloc> && is_constructible_v<_Type, _Args...>) {
183+
return std::forward_as_tuple(std::forward<_Args>(__args)...);
184+
} else if constexpr (uses_allocator_v<remove_cv_t<_Type>, _Alloc> &&
185+
is_constructible_v<_Type, allocator_arg_t, const _Alloc&, _Args...>) {
186+
return tuple<allocator_arg_t, const _Alloc&, _Args&&...>(allocator_arg, __alloc, std::forward<_Args>(__args)...);
187+
} else if constexpr (uses_allocator_v<remove_cv_t<_Type>, _Alloc> &&
188+
is_constructible_v<_Type, _Args..., const _Alloc&>) {
189+
return std::forward_as_tuple(std::forward<_Args>(__args)..., __alloc);
190+
} else {
191+
static_assert(
192+
sizeof(_Type) + 1 == 0, "If uses_allocator_v<Type> is true, the type has to be allocator-constructible");
193+
}
194+
}
195+
};
203196

204197
template <class _Type, class _Alloc, class... _Args>
205198
_LIBCPP_HIDE_FROM_ABI constexpr _Type __make_obj_using_allocator(const _Alloc& __alloc, _Args&&... __args) {
206199
return std::make_from_tuple<_Type>(
207-
std::__uses_allocator_construction_args<_Type>(__alloc, std::forward<_Args>(__args)...));
200+
__uses_allocator_construction_args<_Type>::__apply(__alloc, std::forward<_Args>(__args)...));
208201
}
209202

210203
template <class _Type, class _Alloc, class... _Args>
211204
_LIBCPP_HIDE_FROM_ABI constexpr _Type*
212205
__uninitialized_construct_using_allocator(_Type* __ptr, const _Alloc& __alloc, _Args&&... __args) {
213206
return std::apply(
214207
[&__ptr](auto&&... __xs) { return std::__construct_at(__ptr, std::forward<decltype(__xs)>(__xs)...); },
215-
std::__uses_allocator_construction_args<_Type>(__alloc, std::forward<_Args>(__args)...));
208+
__uses_allocator_construction_args<_Type>::__apply(__alloc, std::forward<_Args>(__args)...));
216209
}
217210

218211
#endif // _LIBCPP_STD_VER >= 17
@@ -221,8 +214,8 @@ __uninitialized_construct_using_allocator(_Type* __ptr, const _Alloc& __alloc, _
221214

222215
template <class _Type, class _Alloc, class... _Args>
223216
_LIBCPP_HIDE_FROM_ABI constexpr auto uses_allocator_construction_args(const _Alloc& __alloc, _Args&&... __args) noexcept
224-
-> decltype(std::__uses_allocator_construction_args<_Type>(__alloc, std::forward<_Args>(__args)...)) {
225-
return /*--*/ std::__uses_allocator_construction_args<_Type>(__alloc, std::forward<_Args>(__args)...);
217+
-> decltype(__uses_allocator_construction_args<_Type>::__apply(__alloc, std::forward<_Args>(__args)...)) {
218+
return /*--*/ __uses_allocator_construction_args<_Type>::__apply(__alloc, std::forward<_Args>(__args)...);
226219
}
227220

228221
template <class _Type, class _Alloc, class... _Args>

libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/uses_allocator_construction_args.pass.cpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,63 @@ constexpr void testOne() {
121121
assert(std::get<2>(std::get<1>(ret)) == 3);
122122
assert(std::get<0>(std::get<2>(ret)) == 4);
123123
}
124+
{
125+
// Tests for ensuring forward declarations of uses_allocator_construction_args
126+
// See https://github.com/llvm/llvm-project/issues/66714.
127+
{
128+
using NestedPairType = std::pair<int, std::pair<int, UsesAllocArgT>>;
129+
std::same_as<std::tuple<
130+
std::piecewise_construct_t,
131+
std::tuple<>,
132+
std::tuple<std::piecewise_construct_t, std::tuple<>, std::tuple<std::allocator_arg_t, const Alloc&>>>> auto
133+
ret = test_uses_allocator_construction_args<NestedPairType>(a);
134+
(void)ret;
135+
}
136+
{
137+
using NestedPairType = std::pair<int, std::pair<UsesAllocArgT, int>>;
138+
std::same_as<std::tuple<
139+
std::piecewise_construct_t,
140+
std::tuple<>,
141+
std::tuple<std::piecewise_construct_t, std::tuple<std::allocator_arg_t, const Alloc&>, std::tuple<>>>> auto
142+
ret = test_uses_allocator_construction_args<NestedPairType>(a);
143+
(void)ret;
144+
}
145+
{
146+
using PairType = std::pair<int, int>;
147+
int up = 1;
148+
int vp = 2;
149+
150+
std::same_as<std::tuple<std::piecewise_construct_t, std::tuple<int&&>, std::tuple<int&&>>> auto ret =
151+
test_uses_allocator_construction_args<PairType>(a, std::move(up), std::move(vp));
152+
(void)ret;
153+
}
154+
{
155+
using PairType = const std::pair<int, int>;
156+
PairType p(1, 2);
157+
158+
std::same_as<std::tuple<std::piecewise_construct_t, std::tuple<const int&>, std::tuple<const int&>>> auto ret =
159+
test_uses_allocator_construction_args<PairType>(a, p);
160+
(void)ret;
161+
}
162+
{
163+
using PairType = std::pair<int, int>;
164+
PairType p(1, 2);
165+
166+
std::same_as<std::tuple<std::piecewise_construct_t, std::tuple<int&&>, std::tuple<int&&>>> auto ret =
167+
test_uses_allocator_construction_args<PairType>(a, std::move(p));
168+
(void)ret;
169+
}
170+
#if TEST_STD_VER >= 23
171+
{
172+
using PairType = const std::pair<int, int>;
173+
PairType p(1, 2);
174+
175+
std::same_as<std::tuple<std::piecewise_construct_t, std::tuple<const int&&>, std::tuple<const int&&>>> auto ret =
176+
test_uses_allocator_construction_args<PairType>(a, std::move(p));
177+
(void)ret;
178+
}
179+
#endif
180+
}
124181
#if TEST_STD_VER >= 23
125182
{
126183
std::pair p{3, 4};

0 commit comments

Comments
 (0)