Skip to content

Commit

Permalink
[libc++] Implement P0591R4 (Utility functions to implement uses-alloc…
Browse files Browse the repository at this point in the history
…ator construction)

Reviewed By: ldionne, #libc, huixie90

Spies: huixie90, libcxx-commits, mgorny

Differential Revision: https://reviews.llvm.org/D131898
  • Loading branch information
philnik777 committed Oct 6, 2022
1 parent 8e3e962 commit 79df8e1
Show file tree
Hide file tree
Showing 25 changed files with 948 additions and 22 deletions.
1 change: 1 addition & 0 deletions libcxx/docs/ReleaseNotes.rst
Expand Up @@ -41,6 +41,7 @@ Implemented Papers
- P2417R2 - A more constexpr bitset
- P2445R1 - ``std::forward_like``
- P2273R3 - Making ``std::unique_ptr`` constexpr
- P0591R4 - Utility functions to implement uses-allocator construction

Improvements and New Features
-----------------------------
Expand Down
1 change: 1 addition & 0 deletions libcxx/docs/Status/Cxx20.rst
Expand Up @@ -40,6 +40,7 @@ Paper Status

.. note::

.. [#note-P0591] P0591: The changes in [mem.poly.allocator.mem] are missing.
.. [#note-P0600] P0600: The missing bits in P0600 are in |sect|\ [mem.res.class] and |sect|\ [mem.poly.allocator.class].
.. [#note-P0645] P0645: The paper is implemented but still marked as an incomplete feature
(the feature-test macro is not set and the libary is only available when built with ``-fexperimental-library``).
Expand Down
4 changes: 2 additions & 2 deletions libcxx/docs/Status/Cxx20Issues.csv
Expand Up @@ -149,7 +149,7 @@
"`3169 <https://wg21.link/LWG3169>`__","``ranges``\ permutation generators discard useful information","Cologne","|Complete|","15.0","|ranges|"
"`3183 <https://wg21.link/LWG3183>`__","Normative permission to specialize Ranges variable templates","Cologne","|Nothing To Do|","","|ranges|"
"`3184 <https://wg21.link/LWG3184>`__","Inconsistencies in ``bind_front``\ wording","Cologne","|Complete|","13.0"
"`3185 <https://wg21.link/LWG3185>`__","Uses-allocator construction functions missing ``constexpr``\ and ``noexcept``\ ","Cologne","",""
"`3185 <https://wg21.link/LWG3185>`__","Uses-allocator construction functions missing ``constexpr``\ and ``noexcept``\ ","Cologne","|Complete|","16.0"
"`3186 <https://wg21.link/LWG3186>`__","``ranges``\ removal, partition, and ``partial_sort_copy``\ algorithms discard useful information","Cologne","|Complete|","15.0","|ranges|"
"`3187 <https://wg21.link/LWG3187>`__","`P0591R4 <https://wg21.link/p0591r4>`__ reverted DR 2586 fixes to ``scoped_allocator_adaptor::construct()``\ ","Cologne","",""
"`3191 <https://wg21.link/LWG3191>`__","``std::ranges::shuffle``\ synopsis does not match algorithm definition","Cologne","|Complete|","15.0","|ranges|"
Expand Down Expand Up @@ -243,7 +243,7 @@
"`3318 <https://wg21.link/LWG3318>`__","Clarify whether clocks can represent time before their epoch","Prague","","","|chrono|"
"`3319 <https://wg21.link/LWG3319>`__","Properly reference specification of IANA time zone database","Prague","","","|chrono|"
"`3320 <https://wg21.link/LWG3320>`__","``span::cbegin/cend``\ methods produce different results than ``std::[ranges::]cbegin/cend``\ ","Prague","|Complete|",""
"`3321 <https://wg21.link/LWG3321>`__","``uninitialized_construct_using_allocator``\ should use ``construct_at``\ ","Prague","",""
"`3321 <https://wg21.link/LWG3321>`__","``uninitialized_construct_using_allocator``\ should use ``construct_at``\ ","Prague","|Complete|","16.0"
"`3323 <https://wg21.link/LWG3323>`__","``*has-tuple-element*``\ helper concept needs ``convertible_to``\ ","Prague","","","|ranges|"
"`3324 <https://wg21.link/LWG3324>`__","Special-case ``std::strong/weak/partial_order``\ for pointers","Prague","|Complete|","14.0","|spaceship|"
"`3325 <https://wg21.link/LWG3325>`__","Constrain return type of transformation function for ``transform_view``\ ","Prague","|Complete|","15.0","|ranges|"
Expand Down
4 changes: 2 additions & 2 deletions libcxx/docs/Status/Cxx20Papers.csv
Expand Up @@ -55,8 +55,8 @@
"`P0357R3 <https://wg21.link/P0357R3>`__","LWG","reference_wrapper for incomplete types","San Diego","|Complete|","8.0"
"`P0482R6 <https://wg21.link/P0482R6>`__","CWG","char8_t: A type for UTF-8 characters and strings","San Diego","|Partial| [#note-P0482]_","16.0"
"`P0487R1 <https://wg21.link/P0487R1>`__","LWG","Fixing ``operator>>(basic_istream&, CharT*)``\ (LWG 2499)","San Diego","|Complete|","8.0"
"`P0591R4 <https://wg21.link/P0591R4>`__","LWG","Utility functions to implement uses-allocator construction","San Diego","* *",""
"`P0595R2 <https://wg21.link/P0595R2>`__","CWG","P0595R2 std::is_constant_evaluated()","San Diego","|Complete|","9.0"
"`P0591R4 <https://wg21.link/P0591R4>`__","LWG","Utility functions to implement uses-allocator construction","San Diego","|Partial| [#note-P0591]_",""
"`P0595R2 <https://wg21.link/P0595R2>`__","CWG","std::is_constant_evaluated()","San Diego","|Complete|","9.0"
"`P0602R4 <https://wg21.link/P0602R4>`__","LWG","variant and optional should propagate copy/move triviality","San Diego","|Complete|","8.0"
"`P0608R3 <https://wg21.link/P0608R3>`__","LWG","A sane variant converting constructor","San Diego","|Complete|","9.0"
"`P0655R1 <https://wg21.link/P0655R1>`__","LWG","visit<R>: Explicit Return Type for visit","San Diego","|Complete|","12.0"
Expand Down
2 changes: 1 addition & 1 deletion libcxx/docs/Status/ZipProjects.csv
Expand Up @@ -4,7 +4,7 @@ Section,Description,Dependencies,Assignee,Complete
| `[utility.syn] <https://wg21.link/utility.syn>`_, "[pair] basic_common_reference, common_type", None, Nikolas Klauser, |Complete|
| `[pairs.pair] <https://wg21.link/pairs.pair>`_, "`[pair] constructor, assignment and swap overloads <https://reviews.llvm.org/D131495>`_", None, Hui Xie, |Complete|
"| `[memory.syn] <https://wg21.link/memory.syn>`_
| `[allocator.uses.construction] <https://wg21.link/allocator.uses.construction>`_", "[pair] uses_allocator_construction_args overloads", None, Unassigned, |Not Started|
| `[allocator.uses.construction] <https://wg21.link/allocator.uses.construction>`_", "[pair] uses_allocator_construction_args overloads", None, Nikolas Klauser, |Complete|
| `[vector.bool] <https://wg21.link/vector.bool>`_, "[vector<bool>::reference] add const operator= overload", None, Hui Xie, |Not Started|
| `[iterator.concept.winc] <https://wg21.link/iterator.concept.winc>`_, "Update weakly_comparable", None, Hui Xie, |Not Started|
| `[range.zip] <https://wg21.link/ranges.syn>`_, "`zip_view <https://reviews.llvm.org/D122806>`_", "| `zip_view::iterator`
Expand Down
1 change: 1 addition & 0 deletions libcxx/include/CMakeLists.txt
Expand Up @@ -412,6 +412,7 @@ set(files
__memory/uninitialized_algorithms.h
__memory/unique_ptr.h
__memory/uses_allocator.h
__memory/uses_allocator_construction.h
__memory/voidify.h
__mutex_base
__node_handle
Expand Down
219 changes: 219 additions & 0 deletions libcxx/include/__memory/uses_allocator_construction.h
@@ -0,0 +1,219 @@
//===----------------------------------------------------------------------===//
//
// 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
//
//===----------------------------------------------------------------------===//

#ifndef _LIBCPP___MEMORY_USES_ALLOCATOR_CONSTRUCTION_H
#define _LIBCPP___MEMORY_USES_ALLOCATOR_CONSTRUCTION_H

#include <__config>
#include <__memory/construct_at.h>
#include <__memory/uses_allocator.h>
#include <__type_traits/enable_if.h>
#include <__type_traits/is_same.h>
#include <__utility/pair.h>
#include <tuple>

#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif

_LIBCPP_BEGIN_NAMESPACE_STD

#if _LIBCPP_STD_VER >= 17

template <class _Type>
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>
_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...>) {
return std::forward_as_tuple(std::forward<_Args>(__args)...);
} else if constexpr (uses_allocator_v<_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&>) {
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>
_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(
piecewise_construct,
std::apply(
[&__alloc](auto&&... __args1) {
return std::__uses_allocator_construction_args<typename _Pair::first_type>(
__alloc, std::forward<decltype(__args1)>(__args1)...);
},
std::forward<_Tuple1>(__x)),
std::apply(
[&__alloc](auto&&... __args2) {
return std::__uses_allocator_construction_args<typename _Pair::second_type>(
__alloc, std::forward<decltype(__args2)>(__args2)...);
},
std::forward<_Tuple2>(__y)));
}

template <class _Pair, class _Alloc, __enable_if_t<__is_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>
_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>(
__alloc,
piecewise_construct,
std::forward_as_tuple(std::forward<_Up>(__u)),
std::forward_as_tuple(std::forward<_Vp>(__v)));
}

# if _LIBCPP_STD_VER > 20
template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_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>
_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>
_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(std::get<0>(std::move(__pair))),
std::forward_as_tuple(std::get<1>(std::move(__pair))));
}

# if _LIBCPP_STD_VER > 20
template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_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(std::get<0>(std::move(__pair))),
std::forward_as_tuple(std::get<1>(std::move(__pair))));
}
# endif

namespace __uses_allocator_detail {

template <class _Ap, class _Bp>
void __fun(const pair<_Ap, _Bp>&);

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;

} // 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>
_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>>
_LIBCPP_HIDE_FROM_ABI constexpr auto
__uses_allocator_construction_args(const _Alloc& __alloc, _Type&& __value) noexcept {
struct __pair_constructor {
using _PairMutable = remove_cv_t<_Pair>;

_LIBCPP_HIDE_FROM_ABI constexpr auto __do_construct(const _PairMutable& __pair) const {
return std::__make_obj_using_allocator<_PairMutable>(__alloc_, __pair);
}

_LIBCPP_HIDE_FROM_ABI constexpr auto __do_construct(_PairMutable&& __pair) const {
return std::__make_obj_using_allocator<_PairMutable>(__alloc_, std::move(__pair));
}

const _Alloc& __alloc_;
_Type& __value_;

_LIBCPP_HIDE_FROM_ABI constexpr operator _PairMutable() const {
return __do_construct(std::forward<_Type>(this->__value_));
}
};

return std::make_tuple(__pair_constructor{__alloc, __value});
}

template <class _Type, class _Alloc, class... _Args>
_LIBCPP_HIDE_FROM_ABI constexpr _Type __make_obj_using_allocator(const _Alloc& __alloc, _Args&&... __args) {
return std::make_from_tuple<_Type>(
std::__uses_allocator_construction_args<_Type>(__alloc, std::forward<_Args>(__args)...));
}

template <class _Type, class _Alloc, class... _Args>
_LIBCPP_HIDE_FROM_ABI constexpr _Type*
__uninitialized_construct_using_allocator(_Type* __ptr, const _Alloc& __alloc, _Args&&... __args) {
return std::apply(
[&__ptr](auto&&... __xs) { return std::__construct_at(__ptr, std::forward<decltype(__xs)>(__xs)...); },
std::__uses_allocator_construction_args<_Type>(__alloc, std::forward<_Args>(__args)...));
}

#endif // _LIBCPP_STD_VER >= 17

#if _LIBCPP_STD_VER >= 20

template <class _Type, class _Alloc, class... _Args>
_LIBCPP_HIDE_FROM_ABI constexpr auto uses_allocator_construction_args(const _Alloc& __alloc, _Args&&... __args) noexcept
-> decltype(std::__uses_allocator_construction_args<_Type>(__alloc, std::forward<_Args>(__args)...)) {
return /*--*/ std::__uses_allocator_construction_args<_Type>(__alloc, std::forward<_Args>(__args)...);
}

template <class _Type, class _Alloc, class... _Args>
_LIBCPP_HIDE_FROM_ABI constexpr auto make_obj_using_allocator(const _Alloc& __alloc, _Args&&... __args)
-> decltype(std::__make_obj_using_allocator<_Type>(__alloc, std::forward<_Args>(__args)...)) {
return /*--*/ std::__make_obj_using_allocator<_Type>(__alloc, std::forward<_Args>(__args)...);
}

template <class _Type, class _Alloc, class... _Args>
_LIBCPP_HIDE_FROM_ABI constexpr auto
uninitialized_construct_using_allocator(_Type* __ptr, const _Alloc& __alloc, _Args&&... __args)
-> decltype(std::__uninitialized_construct_using_allocator(__ptr, __alloc, std::forward<_Args>(__args)...)) {
return /*--*/ std::__uninitialized_construct_using_allocator(__ptr, __alloc, std::forward<_Args>(__args)...);
}

#endif // _LIBCPP_STD_VER >= 20

_LIBCPP_END_NAMESPACE_STD

#endif // _LIBCPP___MEMORY_USES_ALLOCATOR_CONSTRUCTION_H
1 change: 1 addition & 0 deletions libcxx/include/memory
Expand Up @@ -883,6 +883,7 @@ template<size_t N, class T>
#include <__memory/uninitialized_algorithms.h>
#include <__memory/unique_ptr.h>
#include <__memory/uses_allocator.h>
#include <__memory/uses_allocator_construction.h>
#include <version>

// standard-mandated includes
Expand Down
1 change: 1 addition & 0 deletions libcxx/include/module.modulemap.in
Expand Up @@ -878,6 +878,7 @@ module std [system] {
module uninitialized_algorithms { private header "__memory/uninitialized_algorithms.h" }
module unique_ptr { private header "__memory/unique_ptr.h" }
module uses_allocator { private header "__memory/uses_allocator.h" }
module uses_allocator_construction { private header "__memory/uses_allocator_construction.h" }
module voidify { private header "__memory/voidify.h" }
}
}
Expand Down
14 changes: 14 additions & 0 deletions libcxx/include/scoped_allocator
Expand Up @@ -112,6 +112,7 @@ template <class OuterA1, class OuterA2, class... InnerAllocs>
#include <__assert> // all public C++ headers provide the assertion handler
#include <__config>
#include <__memory/allocator_traits.h>
#include <__memory/uses_allocator_construction.h>
#include <__type_traits/common_type.h>
#include <__type_traits/enable_if.h>
#include <__type_traits/integral_constant.h>
Expand Down Expand Up @@ -523,6 +524,18 @@ public:
size_type max_size() const
{return allocator_traits<outer_allocator_type>::max_size(outer_allocator());}

#if _LIBCPP_STD_VER >= 20
template <class _Type, class... _Args>
_LIBCPP_HIDE_FROM_ABI void construct(_Type* __ptr, _Args&&... __args) {
using _OM = __outermost<outer_allocator_type>;
std::apply(
[__ptr, this](auto&&... __newargs) {
allocator_traits<typename _OM::type>::construct(
_OM()(outer_allocator()), __ptr, std::forward<decltype(__newargs)>(__newargs)...);
},
std::uses_allocator_construction_args<_Type>(inner_allocator(), std::forward<_Args>(__args)...));
}
#else
template <class _Tp, class... _Args>
_LIBCPP_INLINE_VISIBILITY
void construct(_Tp* __p, _Args&& ...__args)
Expand Down Expand Up @@ -577,6 +590,7 @@ public:
_VSTD::forward_as_tuple(_VSTD::forward<_Up>(__x.first)),
_VSTD::forward_as_tuple(_VSTD::forward<_Vp>(__x.second)));
}
#endif

template <class _Tp>
_LIBCPP_INLINE_VISIBILITY
Expand Down
1 change: 1 addition & 0 deletions libcxx/test/libcxx/private_headers.verify.cpp
Expand Up @@ -443,6 +443,7 @@ END-SCRIPT
#include <__memory/uninitialized_algorithms.h> // expected-error@*:* {{use of private header from outside its module: '__memory/uninitialized_algorithms.h'}}
#include <__memory/unique_ptr.h> // expected-error@*:* {{use of private header from outside its module: '__memory/unique_ptr.h'}}
#include <__memory/uses_allocator.h> // expected-error@*:* {{use of private header from outside its module: '__memory/uses_allocator.h'}}
#include <__memory/uses_allocator_construction.h> // expected-error@*:* {{use of private header from outside its module: '__memory/uses_allocator_construction.h'}}
#include <__memory/voidify.h> // expected-error@*:* {{use of private header from outside its module: '__memory/voidify.h'}}
#include <__mutex_base> // expected-error@*:* {{use of private header from outside its module: '__mutex_base'}}
#include <__node_handle> // expected-error@*:* {{use of private header from outside its module: '__node_handle'}}
Expand Down
1 change: 1 addition & 0 deletions libcxx/test/libcxx/transitive_includes/cxx2b.csv
Expand Up @@ -479,6 +479,7 @@ memory iosfwd
memory limits
memory new
memory stdexcept
memory tuple
memory type_traits
memory typeinfo
memory version
Expand Down
Expand Up @@ -115,10 +115,10 @@ struct G
{
static bool constructed;

typedef std::allocator<G> allocator_type;
typedef std::scoped_allocator_adaptor<std::allocator<G>> allocator_type;

G(std::allocator_arg_t, allocator_type&&) { assert(false); }
G(allocator_type&) { constructed = true; }
G(const allocator_type&) { constructed = true; }
};

bool G::constructed = false;
Expand Down Expand Up @@ -202,7 +202,7 @@ int main(int, char**)
// Test that is_constructible uses an lvalue ref so the correct constructor
// is picked.
{
std::scoped_allocator_adaptor<G::allocator_type> sa;
G::allocator_type sa;
G* ptr = sa.allocate(1);
sa.construct(ptr);
assert(G::constructed);
Expand Down

0 comments on commit 79df8e1

Please sign in to comment.