-
Notifications
You must be signed in to change notification settings - Fork 15.4k
[libc++] Partially implement LWG3187 for polymorphic_allocator
#171090
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
Open
frederick-vs-ja
wants to merge
1
commit into
llvm:main
Choose a base branch
from
frederick-vs-ja:lwg-3187-polymorphic_allocator
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
[libc++] Partially implement LWG3187 for polymorphic_allocator
#171090
frederick-vs-ja
wants to merge
1
commit into
llvm:main
from
frederick-vs-ja:lwg-3187-polymorphic_allocator
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Member
|
@llvm/pr-subscribers-libcxx Author: A. Jiang (frederick-vs-ja) ChangesApplying it (adding missing `const``) to pre-C++20 dispatching mechanisms.
Towards #100256. Full diff: https://github.com/llvm/llvm-project/pull/171090.diff 5 Files Affected:
diff --git a/libcxx/include/__memory/allocator_arg_t.h b/libcxx/include/__memory/allocator_arg_t.h
index 31a73fc4557ef..a3dac879f45b6 100644
--- a/libcxx/include/__memory/allocator_arg_t.h
+++ b/libcxx/include/__memory/allocator_arg_t.h
@@ -14,6 +14,7 @@
#include <__memory/uses_allocator.h>
#include <__type_traits/integral_constant.h>
#include <__type_traits/is_constructible.h>
+#include <__type_traits/remove_cv.h>
#include <__type_traits/remove_cvref.h>
#include <__utility/forward.h>
@@ -40,34 +41,15 @@ constexpr allocator_arg_t allocator_arg = allocator_arg_t();
template <class _Tp, class _Alloc, class... _Args>
struct __uses_alloc_ctor_imp {
using _RawAlloc _LIBCPP_NODEBUG = __remove_cvref_t<_Alloc>;
- static const bool __ua = uses_allocator<_Tp, _RawAlloc>::value;
- static const bool __ic = is_constructible<_Tp, allocator_arg_t, _Alloc, _Args...>::value;
- static const int value = __ua ? 2 - __ic : 0;
+ static constexpr bool __ua = uses_allocator<__remove_cv_t<_Tp>, _RawAlloc>::value;
+ static constexpr bool __ic_head = is_constructible<_Tp, allocator_arg_t, const _RawAlloc&, _Args...>::value;
+ static constexpr bool __ic_tail = is_constructible<_Tp, _Args..., const _RawAlloc&>::value;
+ static constexpr int value = __ua ? (__ic_head ? 1 : __ic_tail ? 2 : -1) : 0;
};
template <class _Tp, class _Alloc, class... _Args>
struct __uses_alloc_ctor : integral_constant<int, __uses_alloc_ctor_imp<_Tp, _Alloc, _Args...>::value> {};
-template <class _Tp, class _Allocator, class... _Args>
-inline _LIBCPP_HIDE_FROM_ABI void
-__user_alloc_construct_impl(integral_constant<int, 0>, _Tp* __storage, const _Allocator&, _Args&&... __args) {
- new (__storage) _Tp(std::forward<_Args>(__args)...);
-}
-
-// FIXME: This should have a version which takes a non-const alloc.
-template <class _Tp, class _Allocator, class... _Args>
-inline _LIBCPP_HIDE_FROM_ABI void
-__user_alloc_construct_impl(integral_constant<int, 1>, _Tp* __storage, const _Allocator& __a, _Args&&... __args) {
- new (__storage) _Tp(allocator_arg, __a, std::forward<_Args>(__args)...);
-}
-
-// FIXME: This should have a version which takes a non-const alloc.
-template <class _Tp, class _Allocator, class... _Args>
-inline _LIBCPP_HIDE_FROM_ABI void
-__user_alloc_construct_impl(integral_constant<int, 2>, _Tp* __storage, const _Allocator& __a, _Args&&... __args) {
- new (__storage) _Tp(std::forward<_Args>(__args)..., __a);
-}
-
#endif // _LIBCPP_CXX03_LANG
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__memory/uses_allocator_construction.h b/libcxx/include/__memory/uses_allocator_construction.h
index 6733f5cf6fc35..ff729015b6668 100644
--- a/libcxx/include/__memory/uses_allocator_construction.h
+++ b/libcxx/include/__memory/uses_allocator_construction.h
@@ -10,10 +10,12 @@
#define _LIBCPP___MEMORY_USES_ALLOCATOR_CONSTRUCTION_H
#include <__config>
+#include <__functional/reference_wrapper.h>
#include <__memory/construct_at.h>
#include <__memory/uses_allocator.h>
#include <__tuple/tuple_like_no_subrange.h>
#include <__type_traits/enable_if.h>
+#include <__type_traits/is_constructible.h>
#include <__type_traits/remove_cv.h>
#include <__utility/declval.h>
#include <__utility/pair.h>
@@ -34,23 +36,27 @@ _LIBCPP_BEGIN_NAMESPACE_STD
template <class _Tp>
inline constexpr bool __is_cv_std_pair = __is_pair_v<remove_cv_t<_Tp>>;
-template <class _Tp, class = void>
+template <class _Tp, bool _IsCvStdPair = __is_cv_std_pair<_Tp>>
struct __uses_allocator_construction_args;
+# if _LIBCPP_STD_VER >= 20
+template <class _Tp>
+using __uses_allocator_construction_args_recursive = __uses_allocator_construction_args<_Tp>;
+# else
+template <class _Tp>
+using __uses_allocator_construction_args_recursive = __uses_allocator_construction_args<_Tp, false>;
+# endif
+
namespace __uses_allocator_detail {
template <class _Ap, class _Bp>
void __fun(const pair<_Ap, _Bp>&);
+template <class, class = void>
+inline constexpr bool __convertible_to_const_pair_ref = false;
template <class _Tp>
-decltype(__uses_allocator_detail::__fun(std::declval<_Tp>()), true_type()) __convertible_to_const_pair_ref_impl(int);
-
-template <class>
-false_type __convertible_to_const_pair_ref_impl(...);
-
-template <class _Tp>
-inline constexpr bool __convertible_to_const_pair_ref =
- decltype(__uses_allocator_detail::__convertible_to_const_pair_ref_impl<_Tp>(0))::value;
+inline constexpr bool
+ __convertible_to_const_pair_ref<_Tp, decltype(__uses_allocator_detail::__fun(std::declval<_Tp>()))> = true;
# if _LIBCPP_STD_VER >= 23
template <class _Tp, class _Up>
@@ -67,21 +73,25 @@ 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>
-struct __uses_allocator_construction_args<_Pair, __enable_if_t<__is_cv_std_pair<_Pair>>> {
+struct __uses_allocator_construction_args<_Pair, true> {
template <class _Alloc, class _Tuple1, class _Tuple2>
static _LIBCPP_HIDE_FROM_ABI constexpr auto
__apply(const _Alloc& __alloc, piecewise_construct_t, _Tuple1&& __x, _Tuple2&& __y) noexcept {
return std::make_tuple(
+# if _LIBCPP_STD_VER >= 20
piecewise_construct,
+# else
+ std::ref(piecewise_construct),
+# endif
std::apply(
[&__alloc](auto&&... __args1) {
- return __uses_allocator_construction_args<typename _Pair::first_type>::__apply(
+ return __uses_allocator_construction_args_recursive<typename _Pair::first_type>::__apply(
__alloc, std::forward<decltype(__args1)>(__args1)...);
},
std::forward<_Tuple1>(__x)),
std::apply(
[&__alloc](auto&&... __args2) {
- return __uses_allocator_construction_args<typename _Pair::second_type>::__apply(
+ return __uses_allocator_construction_args_recursive<typename _Pair::second_type>::__apply(
__alloc, std::forward<decltype(__args2)>(__args2)...);
},
std::forward<_Tuple2>(__y)));
@@ -170,7 +180,7 @@ struct __uses_allocator_construction_args<_Pair, __enable_if_t<__is_cv_std_pair<
};
template <class _Type>
-struct __uses_allocator_construction_args<_Type, __enable_if_t<!__is_cv_std_pair<_Type>>> {
+struct __uses_allocator_construction_args<_Type, false> {
template <class _Alloc, class... _Args>
static _LIBCPP_HIDE_FROM_ABI constexpr auto __apply(const _Alloc& __alloc, _Args&&... __args) noexcept {
if constexpr (!uses_allocator_v<remove_cv_t<_Type>, _Alloc> && is_constructible_v<_Type, _Args...>) {
diff --git a/libcxx/include/__memory_resource/polymorphic_allocator.h b/libcxx/include/__memory_resource/polymorphic_allocator.h
index 9a351199b5b16..c3910ca70297a 100644
--- a/libcxx/include/__memory_resource/polymorphic_allocator.h
+++ b/libcxx/include/__memory_resource/polymorphic_allocator.h
@@ -14,9 +14,12 @@
#include <__cstddef/byte.h>
#include <__cstddef/max_align_t.h>
#include <__fwd/pair.h>
+#include <__memory/construct_at.h>
+#include <__memory/uses_allocator_construction.h>
#include <__memory_resource/memory_resource.h>
#include <__new/exceptions.h>
#include <__new/placement_new_delete.h>
+#include <__type_traits/remove_cv.h>
#include <__utility/exception_guard.h>
#include <__utility/piecewise_construct.h>
#include <limits>
@@ -35,6 +38,16 @@ _LIBCPP_BEGIN_NAMESPACE_STD
namespace pmr {
+template <class _Type, class _Alloc, class... _Args>
+_LIBCPP_HIDE_FROM_ABI _Type*
+__uninitialized_construct_using_allocator_nocv(_Type* __ptr, const _Alloc& __alloc, _Args&&... __args) {
+ return std::apply(
+ [__ptr_nocv = const_cast<remove_cv_t<_Type>*>(__ptr)](auto&&... __xs) {
+ return std::__construct_at(__ptr_nocv, std::forward<decltype(__xs)>(__xs)...);
+ },
+ __uses_allocator_construction_args<_Type>::__apply(__alloc, std::forward<_Args>(__args)...));
+}
+
// [mem.poly.allocator.class]
template <class _ValueType
@@ -122,24 +135,14 @@ class _LIBCPP_AVAILABILITY_PMR polymorphic_allocator {
template <class _Tp, class... _Ts>
_LIBCPP_HIDE_FROM_ABI void construct(_Tp* __p, _Ts&&... __args) {
- std::__user_alloc_construct_impl(
- typename __uses_alloc_ctor<_Tp, polymorphic_allocator&, _Ts...>::type(),
- __p,
- *this,
- std::forward<_Ts>(__args)...);
+ std::pmr::__uninitialized_construct_using_allocator_nocv(__p, *this, std::forward<_Ts>(__args)...);
}
template <class _T1, class _T2, class... _Args1, class... _Args2>
_LIBCPP_HIDE_FROM_ABI void
construct(pair<_T1, _T2>* __p, piecewise_construct_t, tuple<_Args1...> __x, tuple<_Args2...> __y) {
- ::new ((void*)__p) pair<_T1, _T2>(
- piecewise_construct,
- __transform_tuple(typename __uses_alloc_ctor< _T1, polymorphic_allocator&, _Args1... >::type(),
- std::move(__x),
- make_index_sequence<sizeof...(_Args1)>()),
- __transform_tuple(typename __uses_alloc_ctor< _T2, polymorphic_allocator&, _Args2... >::type(),
- std::move(__y),
- make_index_sequence<sizeof...(_Args2)>()));
+ std::pmr::__uninitialized_construct_using_allocator_nocv(
+ __p, *this, piecewise_construct, tuple<_Args1&&...>(std::move(__x)), tuple<_Args2&&...>(std::move(__y)));
}
template <class _T1, class _T2>
@@ -193,26 +196,6 @@ class _LIBCPP_AVAILABILITY_PMR polymorphic_allocator {
# endif
private:
- template <class... _Args, size_t... _Is>
- _LIBCPP_HIDE_FROM_ABI tuple<_Args&&...>
- __transform_tuple(integral_constant<int, 0>, tuple<_Args...>&& __t, index_sequence<_Is...>) {
- return std::forward_as_tuple(std::get<_Is>(std::move(__t))...);
- }
-
- template <class... _Args, size_t... _Is>
- _LIBCPP_HIDE_FROM_ABI tuple<allocator_arg_t const&, polymorphic_allocator&, _Args&&...>
- __transform_tuple(integral_constant<int, 1>, tuple<_Args...>&& __t, index_sequence<_Is...>) {
- using _Tup = tuple<allocator_arg_t const&, polymorphic_allocator&, _Args&&...>;
- return _Tup(allocator_arg, *this, std::get<_Is>(std::move(__t))...);
- }
-
- template <class... _Args, size_t... _Is>
- _LIBCPP_HIDE_FROM_ABI tuple<_Args&&..., polymorphic_allocator&>
- __transform_tuple(integral_constant<int, 2>, tuple<_Args...>&& __t, index_sequence<_Is...>) {
- using _Tup = tuple<_Args&&..., polymorphic_allocator&>;
- return _Tup(std::get<_Is>(std::move(__t))..., *this);
- }
-
_LIBCPP_HIDE_FROM_ABI size_t __max_size() const noexcept {
return numeric_limits<size_t>::max() / sizeof(value_type);
}
diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 670b90fc7b3b9..7c3b1908c3f39 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -373,6 +373,11 @@ public:
static_assert(!is_reference<_Hp>::value, "Attempted to default construct a reference element in a tuple");
}
+ template <class _Alloc, class... _Args>
+ _LIBCPP_HIDE_FROM_ABI __tuple_leaf(integral_constant<int, -1>, const _Alloc&, _Args&&...) {
+ static_assert(false, "If uses_allocator_v<T, A> is true, T has to be allocator-constructible");
+ }
+
template <class _Alloc>
_LIBCPP_HIDE_FROM_ABI constexpr __tuple_leaf(integral_constant<int, 0>, const _Alloc&) : __value_() {
static_assert(!is_reference<_Hp>::value, "Attempted to default construct a reference element in a tuple");
@@ -447,6 +452,11 @@ public:
_LIBCPP_HIDE_FROM_ABI constexpr __tuple_leaf() noexcept(is_nothrow_default_constructible<_Hp>::value) {}
+ template <class _Alloc, class... _Args>
+ _LIBCPP_HIDE_FROM_ABI __tuple_leaf(integral_constant<int, -1>, const _Alloc&, _Args&&...) {
+ static_assert(false, "If uses_allocator_v<T, A> is true, T has to be allocator-constructible");
+ }
+
template <class _Alloc>
_LIBCPP_HIDE_FROM_ABI constexpr __tuple_leaf(integral_constant<int, 0>, const _Alloc&) {}
diff --git a/libcxx/test/std/utilities/utility/mem.res/mem.poly.allocator.class/mem.poly.allocator.mem/construct_piecewise_pair_evil.pass.cpp b/libcxx/test/std/utilities/utility/mem.res/mem.poly.allocator.class/mem.poly.allocator.mem/construct_piecewise_pair_evil.pass.cpp
index b77734c28e12d..11eff1dc509e7 100644
--- a/libcxx/test/std/utilities/utility/mem.res/mem.poly.allocator.class/mem.poly.allocator.mem/construct_piecewise_pair_evil.pass.cpp
+++ b/libcxx/test/std/utilities/utility/mem.res/mem.poly.allocator.class/mem.poly.allocator.mem/construct_piecewise_pair_evil.pass.cpp
@@ -30,10 +30,10 @@ template <class T>
struct EvilAlloc {
explicit EvilAlloc() : inner_(std::pmr::null_memory_resource()) {}
- EvilAlloc(std::pmr::polymorphic_allocator<T>& a) : inner_(a) {}
+ EvilAlloc(std::pmr::polymorphic_allocator<T>&) = delete;
EvilAlloc(std::pmr::polymorphic_allocator<T>&& a) : inner_(a) {}
- EvilAlloc(std::pmr::polymorphic_allocator<T> const& a) = delete;
- EvilAlloc(std::pmr::polymorphic_allocator<T> const&& a) = delete;
+ EvilAlloc(std::pmr::polymorphic_allocator<T> const& a) : inner_(a) {}
+ EvilAlloc(std::pmr::polymorphic_allocator<T> const&&) = delete;
using value_type = T;
template <class U>
|
4660f66 to
271efc4
Compare
frederick-vs-ja
commented
Dec 8, 2025
271efc4 to
f93a11b
Compare
Applying it (adding missing `const`) to pre-C++20 dispatching mechanism. - Also implement LWG3677 for `tuple`, by modifications to `__memory/allocator_arg_t.h`. - Also partially implement LWG4312 for `polymorphic_allocator`, by reusing relevant C++20 mechanism changes around `allocator_arg_t`. - Also add fallback dispatching and overloads to emit better error messages. Making `std.memory.uses_allocator_construction` export `std.functional.reference_wrapper` to support Clang module builds.
f93a11b to
3775e8f
Compare
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Applying it (adding missing
const) to pre-C++20 dispatching mechanism.tuple, by modifications to__memory/allocator_arg_t.h.polymorphic_allocator, by reusing relevant C++20 mechanism changes aroundallocator_arg_t.Need to make
std.memory.uses_allocator_constructionexportstd.functional.reference_wrapperto support Clang module builds.Towards #100256.