[libcxx-commits] [libcxx] a96443e - [libc++] Implement P0401R6 (allocate_at_least)

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Sat Apr 9 07:04:03 PDT 2022


Author: Nikolas Klauser
Date: 2022-04-09T16:03:45+02:00
New Revision: a96443eddedc250188b5e5f2b74ae1cf2baf1472

URL: https://github.com/llvm/llvm-project/commit/a96443eddedc250188b5e5f2b74ae1cf2baf1472
DIFF: https://github.com/llvm/llvm-project/commit/a96443eddedc250188b5e5f2b74ae1cf2baf1472.diff

LOG: [libc++] Implement P0401R6 (allocate_at_least)

Reviewed By: ldionne, var-const, #libc

Spies: mgorny, libcxx-commits, arichardson

Differential Revision: https://reviews.llvm.org/D122877

Added: 
    libcxx/include/__memory/allocate_at_least.h
    libcxx/test/std/utilities/memory/allocator.traits/allocate_at_least.pass.cpp
    libcxx/test/std/utilities/memory/default.allocator/allocator.members/allocate_at_least.pass.cpp

Modified: 
    libcxx/docs/FeatureTestMacroTable.rst
    libcxx/docs/Status/Cxx2bPapers.csv
    libcxx/include/CMakeLists.txt
    libcxx/include/__memory/allocator.h
    libcxx/include/__split_buffer
    libcxx/include/memory
    libcxx/include/module.modulemap
    libcxx/include/string
    libcxx/include/vector
    libcxx/include/version
    libcxx/test/libcxx/private_headers.verify.cpp
    libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.pass.cpp
    libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp
    libcxx/utils/generate_feature_test_macro_components.py

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index e305a78e0c456..fa5dcb13b78b3 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -300,7 +300,7 @@ Status
     -------------------------------------------------------------------
     ``__cpp_lib_adaptor_iterator_pair_constructor``   ``202106L``
     ------------------------------------------------- -----------------
-    ``__cpp_lib_allocate_at_least``                   *unimplemented*
+    ``__cpp_lib_allocate_at_least``                   ``202106L``
     ------------------------------------------------- -----------------
     ``__cpp_lib_associative_heterogeneous_erasure``   *unimplemented*
     ------------------------------------------------- -----------------

diff  --git a/libcxx/docs/Status/Cxx2bPapers.csv b/libcxx/docs/Status/Cxx2bPapers.csv
index c0ef6882a5135..4aad9a6d66785 100644
--- a/libcxx/docs/Status/Cxx2bPapers.csv
+++ b/libcxx/docs/Status/Cxx2bPapers.csv
@@ -11,7 +11,7 @@
 "`P2212R2 <https://wg21.link/P2212R2>`__","LWG","Relax Requirements for time_point::clock","February 2021","",""
 "`P2259R1 <https://wg21.link/P2259R1>`__","LWG","Repairing input range adaptors and counted_iterator","February 2021","",""
 "","","","","",""
-"`P0401R6 <https://wg21.link/P0401R6>`__","LWG","Providing size feedback in the Allocator interface","June 2021","",""
+"`P0401R6 <https://wg21.link/P0401R6>`__","LWG","Providing size feedback in the Allocator interface","June 2021","|Complete|","15.0"
 "`P0448R4 <https://wg21.link/P0448R4>`__","LWG","A strstream replacement using span<charT> as buffer","June 2021","",""
 "`P1132R8 <https://wg21.link/P1132R8>`__","LWG","out_ptr - a scalable output pointer abstraction","June 2021","",""
 "`P1328R1 <https://wg21.link/P1328R1>`__","LWG","Making std::type_info::operator== constexpr","June 2021","",""

diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index ebe95c09eed29..27b9e6e7aea77 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -286,6 +286,7 @@ set(files
   __locale
   __mbstate_t.h
   __memory/addressof.h
+  __memory/allocate_at_least.h
   __memory/allocation_guard.h
   __memory/allocator.h
   __memory/allocator_arg_t.h

diff  --git a/libcxx/include/__memory/allocate_at_least.h b/libcxx/include/__memory/allocate_at_least.h
new file mode 100644
index 0000000000000..7ce588a25d1be
--- /dev/null
+++ b/libcxx/include/__memory/allocate_at_least.h
@@ -0,0 +1,61 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_ALLOCATE_AT_LEAST_H
+#define _LIBCPP___MEMORY_ALLOCATE_AT_LEAST_H
+
+#include <__config>
+#include <__memory/allocator_traits.h>
+#include <cstddef>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER > 20
+template <class _Pointer>
+struct allocation_result {
+  _Pointer ptr;
+  size_t count;
+};
+
+template <class _Alloc>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr
+allocation_result<typename allocator_traits<_Alloc>::pointer> allocate_at_least(_Alloc& __alloc, size_t __n) {
+  if constexpr (requires { __alloc.allocate_at_least(__n); }) {
+    return __alloc.allocate_at_least(__n);
+  } else {
+    return {__alloc.allocate(__n), __n};
+  }
+}
+
+template <class _Alloc>
+[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr
+auto __allocate_at_least(_Alloc& __alloc, size_t __n) {
+  return std::allocate_at_least(__alloc, __n);
+}
+#else
+template <class _Pointer>
+struct __allocation_result {
+  _Pointer ptr;
+  size_t count;
+};
+
+template <class _Alloc>
+_LIBCPP_NODISCARD _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR
+__allocation_result<typename allocator_traits<_Alloc>::pointer> __allocate_at_least(_Alloc& __alloc, size_t __n) {
+  return {__alloc.allocate(__n), __n};
+}
+
+#endif // _LIBCPP_STD_VER > 20
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___MEMORY_ALLOCATE_AT_LEAST_H

diff  --git a/libcxx/include/__memory/allocator.h b/libcxx/include/__memory/allocator.h
index 915ff7c3a516a..1945848674f22 100644
--- a/libcxx/include/__memory/allocator.h
+++ b/libcxx/include/__memory/allocator.h
@@ -11,6 +11,7 @@
 #define _LIBCPP___MEMORY_ALLOCATOR_H
 
 #include <__config>
+#include <__memory/allocate_at_least.h>
 #include <__memory/allocator_traits.h>
 #include <__utility/forward.h>
 #include <cstddef>
@@ -106,6 +107,13 @@ class _LIBCPP_TEMPLATE_VIS allocator
         }
     }
 
+#if _LIBCPP_STD_VER > 20
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr
+    allocation_result<_Tp*> allocate_at_least(size_t __n) {
+        return {allocate(__n), __n};
+    }
+#endif
+
     _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
     void deallocate(_Tp* __p, size_t __n) _NOEXCEPT {
         if (__libcpp_is_constant_evaluated()) {
@@ -188,6 +196,13 @@ class _LIBCPP_TEMPLATE_VIS allocator<const _Tp>
         }
     }
 
+#if _LIBCPP_STD_VER > 20
+    [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr
+    allocation_result<const _Tp*> allocate_at_least(size_t __n) {
+        return {allocate(__n), __n};
+    }
+#endif
+
     _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
     void deallocate(const _Tp* __p, size_t __n) {
         if (__libcpp_is_constant_evaluated()) {

diff  --git a/libcxx/include/__split_buffer b/libcxx/include/__split_buffer
index 852f02a2c4d53..e484e70440c9a 100644
--- a/libcxx/include/__split_buffer
+++ b/libcxx/include/__split_buffer
@@ -319,7 +319,13 @@ template <class _Tp, class _Allocator>
 __split_buffer<_Tp, _Allocator>::__split_buffer(size_type __cap, size_type __start, __alloc_rr& __a)
     : __end_cap_(nullptr, __a)
 {
-    __first_ = __cap != 0 ? __alloc_traits::allocate(__alloc(), __cap) : nullptr;
+    if (__cap == 0) {
+        __first_ = nullptr;
+    } else {
+        auto __allocation = std::__allocate_at_least(__alloc(), __cap);
+        __first_ = __allocation.ptr;
+        __cap = __allocation.count;
+    }
     __begin_ = __end_ = __first_ + __start;
     __end_cap() = __first_ + __cap;
 }
@@ -385,10 +391,10 @@ __split_buffer<_Tp, _Allocator>::__split_buffer(__split_buffer&& __c, const __al
     }
     else
     {
-        size_type __cap = __c.size();
-        __first_ = __alloc_traits::allocate(__alloc(), __cap);
+        auto __allocation = std::__allocate_at_least(__alloc(), __c.size());
+        __first_ = __allocation.ptr;
         __begin_ = __end_ = __first_;
-        __end_cap() = __first_ + __cap;
+        __end_cap() = __first_ + __allocation.count;
         typedef move_iterator<iterator> _Ip;
         __construct_at_end(_Ip(__c.begin()), _Ip(__c.end()));
     }

diff  --git a/libcxx/include/memory b/libcxx/include/memory
index 8d577245999d4..7825b075820e3 100644
--- a/libcxx/include/memory
+++ b/libcxx/include/memory
@@ -98,6 +98,16 @@ struct allocator_traits
     static allocator_type select_on_container_copy_construction(const allocator_type& a); // constexpr in C++20
 };
 
+template<class Pointer>
+struct allocation_result {
+    Pointer ptr;
+    size_t count;
+}; // since C++23
+
+template<class Allocator>
+[[nodiscard]] constexpr allocation_result<typename allocator_traits<Allocator>::pointer>
+    allocate_at_least(Allocator& a, size_t n); // since C++23
+
 template <>
 class allocator<void> // removed in C++20
 {
@@ -827,6 +837,7 @@ void* align(size_t alignment, size_t size, void*& ptr, size_t& space);
 #include <__assert> // all public C++ headers provide the assertion handler
 #include <__config>
 #include <__memory/addressof.h>
+#include <__memory/allocate_at_least.h>
 #include <__memory/allocation_guard.h>
 #include <__memory/allocator.h>
 #include <__memory/allocator_arg_t.h>

diff  --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index ece960fdf2e67..18b094585b3ca 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -708,6 +708,7 @@ module std [system] {
 
     module __memory {
       module addressof                       { private header "__memory/addressof.h" }
+      module allocate_at_least               { private header "__memory/allocate_at_least.h" }
       module allocation_guard                { private header "__memory/allocation_guard.h" }
       module allocator                       { private header "__memory/allocator.h" }
       module allocator_arg_t                 { private header "__memory/allocator_arg_t.h" }

diff  --git a/libcxx/include/string b/libcxx/include/string
index 88b18be41cf66..daa6a68b230cb 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -528,6 +528,7 @@ basic_string<char32_t> operator "" s( const char32_t *str, size_t len ); // C++1
 #include <__format/enable_insertable.h>
 #include <__ios/fpos.h>
 #include <__iterator/wrap_iter.h>
+#include <__memory/allocate_at_least.h>
 #include <__utility/auto_cast.h>
 #include <__utility/move.h>
 #include <__utility/swap.h>
@@ -1623,11 +1624,11 @@ private:
                 else
                 {
                     allocator_type __a = __str.__alloc();
-                    pointer __p = __alloc_traits::allocate(__a, __str.__get_long_cap());
+                    auto __allocation = std::__allocate_at_least(__a, __str.__get_long_cap());
                     __clear_and_shrink();
                     __alloc() = _VSTD::move(__a);
-                    __set_long_pointer(__p);
-                    __set_long_cap(__str.__get_long_cap());
+                    __set_long_pointer(__allocation.ptr);
+                    __set_long_cap(__allocation.count);
                     __set_long_size(__str.size());
                 }
             }
@@ -1841,10 +1842,10 @@ void basic_string<_CharT, _Traits, _Allocator>::__init(const value_type* __s,
     }
     else
     {
-        size_type __cap = __recommend(__reserve);
-        __p = __alloc_traits::allocate(__alloc(), __cap+1);
+        auto __allocation = std::__allocate_at_least(__alloc(), __recommend(__reserve) + 1);
+        __p = __allocation.ptr;
         __set_long_pointer(__p);
-        __set_long_cap(__cap+1);
+        __set_long_cap(__allocation.count);
         __set_long_size(__sz);
     }
     traits_type::copy(_VSTD::__to_address(__p), __s, __sz);
@@ -1865,10 +1866,10 @@ basic_string<_CharT, _Traits, _Allocator>::__init(const value_type* __s, size_ty
     }
     else
     {
-        size_type __cap = __recommend(__sz);
-        __p = __alloc_traits::allocate(__alloc(), __cap+1);
+        auto __allocation = std::__allocate_at_least(__alloc(), __recommend(__sz) + 1);
+        __p = __allocation.ptr;
         __set_long_pointer(__p);
-        __set_long_cap(__cap+1);
+        __set_long_cap(__allocation.count);
         __set_long_size(__sz);
     }
     traits_type::copy(_VSTD::__to_address(__p), __s, __sz);
@@ -1940,10 +1941,10 @@ void basic_string<_CharT, _Traits, _Allocator>::__init_copy_ctor_external(
   } else {
     if (__sz > max_size())
       __throw_length_error();
-    size_t __cap = __recommend(__sz);
-    __p = __alloc_traits::allocate(__alloc(), __cap + 1);
+    auto __allocation = std::__allocate_at_least(__alloc(), __recommend(__sz) + 1);
+    __p = __allocation.ptr;
     __set_long_pointer(__p);
-    __set_long_cap(__cap + 1);
+    __set_long_cap(__allocation.count);
     __set_long_size(__sz);
   }
   traits_type::copy(_VSTD::__to_address(__p), __s, __sz + 1);
@@ -2004,10 +2005,10 @@ basic_string<_CharT, _Traits, _Allocator>::__init(size_type __n, value_type __c)
     }
     else
     {
-        size_type __cap = __recommend(__n);
-        __p = __alloc_traits::allocate(__alloc(), __cap+1);
+        auto __allocation = std::__allocate_at_least(__alloc(), __recommend(__n) + 1);
+        __p = __allocation.ptr;
         __set_long_pointer(__p);
-        __set_long_cap(__cap+1);
+        __set_long_cap(__allocation.count);
         __set_long_size(__n);
     }
     traits_type::assign(_VSTD::__to_address(__p), __n, __c);
@@ -2135,10 +2136,10 @@ basic_string<_CharT, _Traits, _Allocator>::__init(_ForwardIterator __first, _For
     }
     else
     {
-        size_type __cap = __recommend(__sz);
-        __p = __alloc_traits::allocate(__alloc(), __cap+1);
+        auto __allocation = std::__allocate_at_least(__alloc(), __recommend(__sz) + 1);
+        __p = __allocation.ptr;
         __set_long_pointer(__p);
-        __set_long_cap(__cap+1);
+        __set_long_cap(__allocation.count);
         __set_long_size(__sz);
     }
 
@@ -2230,7 +2231,8 @@ basic_string<_CharT, _Traits, _Allocator>::__grow_by_and_replace
     size_type __cap = __old_cap < __ms / 2 - __alignment ?
                           __recommend(_VSTD::max(__old_cap + __delta_cap, 2 * __old_cap)) :
                           __ms - 1;
-    pointer __p = __alloc_traits::allocate(__alloc(), __cap+1);
+    auto __allocation = std::__allocate_at_least(__alloc(), __cap + 1);
+    pointer __p = __allocation.ptr;
     __invalidate_all_iterators();
     if (__n_copy != 0)
         traits_type::copy(_VSTD::__to_address(__p),
@@ -2244,7 +2246,7 @@ basic_string<_CharT, _Traits, _Allocator>::__grow_by_and_replace
     if (__old_cap+1 != __min_cap)
         __alloc_traits::deallocate(__alloc(), __old_p, __old_cap+1);
     __set_long_pointer(__p);
-    __set_long_cap(__cap+1);
+    __set_long_cap(__allocation.count);
     __old_sz = __n_copy + __n_add + __sec_cp_sz;
     __set_long_size(__old_sz);
     traits_type::assign(__p[__old_sz], value_type());
@@ -2262,7 +2264,8 @@ basic_string<_CharT, _Traits, _Allocator>::__grow_by(size_type __old_cap, size_t
     size_type __cap = __old_cap < __ms / 2 - __alignment ?
                           __recommend(_VSTD::max(__old_cap + __delta_cap, 2 * __old_cap)) :
                           __ms - 1;
-    pointer __p = __alloc_traits::allocate(__alloc(), __cap+1);
+    auto __allocation = std::__allocate_at_least(__alloc(), __cap + 1);
+    pointer __p = __allocation.ptr;
     __invalidate_all_iterators();
     if (__n_copy != 0)
         traits_type::copy(_VSTD::__to_address(__p),
@@ -2275,7 +2278,7 @@ basic_string<_CharT, _Traits, _Allocator>::__grow_by(size_type __old_cap, size_t
     if (__old_cap+1 != __min_cap)
         __alloc_traits::deallocate(__alloc(), __old_p, __old_cap+1);
     __set_long_pointer(__p);
-    __set_long_cap(__cap+1);
+    __set_long_cap(__allocation.count);
 }
 
 // assign
@@ -3257,15 +3260,20 @@ basic_string<_CharT, _Traits, _Allocator>::__shrink_or_extend(size_type __target
     }
     else
     {
-        if (__target_capacity > __cap)
-            __new_data = __alloc_traits::allocate(__alloc(), __target_capacity+1);
+        if (__target_capacity > __cap) {
+            auto __allocation = std::__allocate_at_least(__alloc(), __target_capacity + 1);
+            __new_data = __allocation.ptr;
+            __target_capacity = __allocation.count - 1;
+        }
         else
         {
         #ifndef _LIBCPP_NO_EXCEPTIONS
             try
             {
         #endif // _LIBCPP_NO_EXCEPTIONS
-                __new_data = __alloc_traits::allocate(__alloc(), __target_capacity+1);
+                auto __allocation = std::__allocate_at_least(__alloc(), __target_capacity + 1);
+                __new_data = __allocation.ptr;
+                __target_capacity = __allocation.count - 1;
         #ifndef _LIBCPP_NO_EXCEPTIONS
             }
             catch (...)

diff  --git a/libcxx/include/vector b/libcxx/include/vector
index 3ad43ee2a9de6..e7d183c922dd1 100644
--- a/libcxx/include/vector
+++ b/libcxx/include/vector
@@ -287,6 +287,7 @@ erase_if(vector<T, Allocator>& c, Predicate pred);    // C++20
 #include <__functional/hash.h>
 #include <__iterator/iterator_traits.h>
 #include <__iterator/wrap_iter.h>
+#include <__memory/allocate_at_least.h>
 #include <__split_buffer>
 #include <__utility/forward.h>
 #include <__utility/move.h>
@@ -673,7 +674,25 @@ private:
 
     _LIBCPP_INLINE_VISIBILITY void __invalidate_all_iterators();
     _LIBCPP_INLINE_VISIBILITY void __invalidate_iterators_past(pointer __new_last);
-    void __vallocate(size_type __n);
+
+
+    //  Allocate space for __n objects
+    //  throws length_error if __n > max_size()
+    //  throws (probably bad_alloc) if memory run out
+    //  Precondition:  __begin_ == __end_ == __end_cap() == 0
+    //  Precondition:  __n > 0
+    //  Postcondition:  capacity() >= __n
+    //  Postcondition:  size() == 0
+    _LIBCPP_HIDE_FROM_ABI void __vallocate(size_type __n) {
+        if (__n > max_size())
+            __throw_length_error();
+        auto __allocation = std::__allocate_at_least(__alloc(), __n);
+        __begin_ = __allocation.ptr;
+        __end_ = __allocation.ptr;
+        __end_cap() = __begin_ + __allocation.count;
+        __annotate_new(0);
+    }
+
     void __vdeallocate() _NOEXCEPT;
     _LIBCPP_INLINE_VISIBILITY size_type __recommend(size_type __new_size) const;
     void __construct_at_end(size_type __n);
@@ -926,24 +945,6 @@ vector<_Tp, _Allocator>::__swap_out_circular_buffer(__split_buffer<value_type, a
     return __r;
 }
 
-//  Allocate space for __n objects
-//  throws length_error if __n > max_size()
-//  throws (probably bad_alloc) if memory run out
-//  Precondition:  __begin_ == __end_ == __end_cap() == 0
-//  Precondition:  __n > 0
-//  Postcondition:  capacity() == __n
-//  Postcondition:  size() == 0
-template <class _Tp, class _Allocator>
-void
-vector<_Tp, _Allocator>::__vallocate(size_type __n)
-{
-    if (__n > max_size())
-        this->__throw_length_error();
-    this->__begin_ = this->__end_ = __alloc_traits::allocate(this->__alloc(), __n);
-    this->__end_cap() = this->__begin_ + __n;
-    __annotate_new(0);
-}
-
 template <class _Tp, class _Allocator>
 void
 vector<_Tp, _Allocator>::__vdeallocate() _NOEXCEPT
@@ -2329,8 +2330,23 @@ private:
         _VSTD::__throw_out_of_range("vector");
     }
 
+    //  Allocate space for __n objects
+    //  throws length_error if __n > max_size()
+    //  throws (probably bad_alloc) if memory run out
+    //  Precondition:  __begin_ == __end_ == __cap() == 0
+    //  Precondition:  __n > 0
+    //  Postcondition:  capacity() >= __n
+    //  Postcondition:  size() == 0
     _LIBCPP_INLINE_VISIBILITY void __invalidate_all_iterators();
-    void __vallocate(size_type __n);
+    _LIBCPP_HIDE_FROM_ABI void __vallocate(size_type __n) {
+        if (__n > max_size())
+            __throw_length_error();
+        auto __allocation = std::__allocate_at_least(__alloc(), __external_cap_to_internal(__n));
+        __begin_ = __allocation.ptr;
+        __size_ = 0;
+        __cap() = __allocation.count;
+    }
+
     void __vdeallocate() _NOEXCEPT;
     _LIBCPP_INLINE_VISIBILITY
     static size_type __align_it(size_type __new_size) _NOEXCEPT
@@ -2416,25 +2432,6 @@ vector<bool, _Allocator>::__invalidate_all_iterators()
 {
 }
 
-//  Allocate space for __n objects
-//  throws length_error if __n > max_size()
-//  throws (probably bad_alloc) if memory run out
-//  Precondition:  __begin_ == __end_ == __cap() == 0
-//  Precondition:  __n > 0
-//  Postcondition:  capacity() == __n
-//  Postcondition:  size() == 0
-template <class _Allocator>
-void
-vector<bool, _Allocator>::__vallocate(size_type __n)
-{
-    if (__n > max_size())
-        this->__throw_length_error();
-    __n = __external_cap_to_internal(__n);
-    this->__begin_ = __storage_traits::allocate(this->__alloc(), __n);
-    this->__size_ = 0;
-    this->__cap() = __n;
-}
-
 template <class _Allocator>
 void
 vector<bool, _Allocator>::__vdeallocate() _NOEXCEPT

diff  --git a/libcxx/include/version b/libcxx/include/version
index 258adedffffa4..017d69225d0c3 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -374,7 +374,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 
 #if _LIBCPP_STD_VER > 20
 # define __cpp_lib_adaptor_iterator_pair_constructor    202106L
-// # define __cpp_lib_allocate_at_least                    202106L
+# define __cpp_lib_allocate_at_least                    202106L
 // # define __cpp_lib_associative_heterogeneous_erasure    202110L
 // # define __cpp_lib_bind_back                            202202L
 # define __cpp_lib_byteswap                             202110L

diff  --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp
index 06c5988ba0464..29b1b939bd3b8 100644
--- a/libcxx/test/libcxx/private_headers.verify.cpp
+++ b/libcxx/test/libcxx/private_headers.verify.cpp
@@ -317,6 +317,7 @@ END-SCRIPT
 #include <__locale> // expected-error@*:* {{use of private header from outside its module: '__locale'}}
 #include <__mbstate_t.h> // expected-error@*:* {{use of private header from outside its module: '__mbstate_t.h'}}
 #include <__memory/addressof.h> // expected-error@*:* {{use of private header from outside its module: '__memory/addressof.h'}}
+#include <__memory/allocate_at_least.h> // expected-error@*:* {{use of private header from outside its module: '__memory/allocate_at_least.h'}}
 #include <__memory/allocation_guard.h> // expected-error@*:* {{use of private header from outside its module: '__memory/allocation_guard.h'}}
 #include <__memory/allocator.h> // expected-error@*:* {{use of private header from outside its module: '__memory/allocator.h'}}
 #include <__memory/allocator_arg_t.h> // expected-error@*:* {{use of private header from outside its module: '__memory/allocator_arg_t.h'}}

diff  --git a/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.pass.cpp
index cd9d4fa7a09d2..930b15548e2e7 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.pass.cpp
@@ -422,17 +422,11 @@
 #   error "__cpp_lib_addressof_constexpr should have the value 201603L in c++2b"
 # endif
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_allocate_at_least
-#     error "__cpp_lib_allocate_at_least should be defined in c++2b"
-#   endif
-#   if __cpp_lib_allocate_at_least != 202106L
-#     error "__cpp_lib_allocate_at_least should have the value 202106L in c++2b"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_allocate_at_least
-#     error "__cpp_lib_allocate_at_least should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_allocate_at_least
+#   error "__cpp_lib_allocate_at_least should be defined in c++2b"
+# endif
+# if __cpp_lib_allocate_at_least != 202106L
+#   error "__cpp_lib_allocate_at_least should have the value 202106L in c++2b"
 # endif
 
 # ifndef __cpp_lib_allocator_traits_is_always_equal

diff  --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp
index 8a01b07713add..0e0bb0bea9725 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.pass.cpp
@@ -3614,17 +3614,11 @@
 #   error "__cpp_lib_addressof_constexpr should have the value 201603L in c++2b"
 # endif
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_allocate_at_least
-#     error "__cpp_lib_allocate_at_least should be defined in c++2b"
-#   endif
-#   if __cpp_lib_allocate_at_least != 202106L
-#     error "__cpp_lib_allocate_at_least should have the value 202106L in c++2b"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_allocate_at_least
-#     error "__cpp_lib_allocate_at_least should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_allocate_at_least
+#   error "__cpp_lib_allocate_at_least should be defined in c++2b"
+# endif
+# if __cpp_lib_allocate_at_least != 202106L
+#   error "__cpp_lib_allocate_at_least should have the value 202106L in c++2b"
 # endif
 
 # ifndef __cpp_lib_allocator_traits_is_always_equal

diff  --git a/libcxx/test/std/utilities/memory/allocator.traits/allocate_at_least.pass.cpp b/libcxx/test/std/utilities/memory/allocator.traits/allocate_at_least.pass.cpp
new file mode 100644
index 0000000000000..14a500e2d9d04
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/allocator.traits/allocate_at_least.pass.cpp
@@ -0,0 +1,66 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <memory>
+
+// template<class Allocator>
+// [[nodiscard]] constexpr allocation_result<typename allocator_traits<Allocator>::pointer>
+//   allocate_at_least(Allocator& a, size_t n);
+
+#include <cassert>
+#include <memory>
+
+// check that std::allocation_result exists and isn't restricted to pointers
+using AllocResult = std::allocation_result<int>;
+
+template <class T>
+struct no_allocate_at_least {
+  using value_type = T;
+  T t;
+
+  constexpr T* allocate(size_t) { return &t; }
+  constexpr void deallocate(T*, size_t) {}
+};
+
+template <class T>
+struct has_allocate_at_least {
+  using value_type = T;
+  T t1;
+  T t2;
+
+  constexpr T* allocate(size_t) { return &t1; }
+  constexpr void deallocate(T*, size_t) {}
+  constexpr std::allocation_result<T*> allocate_at_least(size_t) {
+    return {&t2, 2};
+  }
+};
+
+constexpr bool test() {
+  { // check that std::allocate_at_least forwards to allocator::allocate if no allocate_at_least exists
+    no_allocate_at_least<int> alloc;
+    std::same_as<std::allocation_result<int*>> decltype(auto) ret = std::allocate_at_least(alloc, 1);
+    assert(ret.count == 1);
+    assert(ret.ptr == &alloc.t);
+  }
+
+  { // check that std::allocate_at_least forwards to allocator::allocate_at_least if allocate_at_least exists
+    has_allocate_at_least<int> alloc;
+    std::same_as<std::allocation_result<int*>> decltype(auto) ret = std::allocate_at_least(alloc, 1);
+    assert(ret.count == 2);
+    assert(ret.ptr == &alloc.t2);
+  }
+
+  return true;
+}
+
+int main() {
+  test();
+  static_assert(test());
+}

diff  --git a/libcxx/test/std/utilities/memory/default.allocator/allocator.members/allocate_at_least.pass.cpp b/libcxx/test/std/utilities/memory/default.allocator/allocator.members/allocate_at_least.pass.cpp
new file mode 100644
index 0000000000000..a12c1222eddae
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/default.allocator/allocator.members/allocate_at_least.pass.cpp
@@ -0,0 +1,108 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <memory>
+
+// allocation_result<T*> allocate_at_least(size_t n)
+
+#include <cassert>
+#include <memory>
+
+#include "count_new.h"
+
+#ifdef TEST_HAS_NO_ALIGNED_ALLOCATION
+static const bool UsingAlignedNew = false;
+#else
+static const bool UsingAlignedNew = true;
+#endif
+
+#ifdef __STDCPP_DEFAULT_NEW_ALIGNMENT__
+static const size_t MaxAligned = __STDCPP_DEFAULT_NEW_ALIGNMENT__;
+#else
+static const size_t MaxAligned = std::alignment_of<std::max_align_t>::value;
+#endif
+
+static const size_t OverAligned = MaxAligned * 2;
+
+template <size_t Align>
+struct alignas(Align) AlignedType {
+  char data;
+  static int constructed;
+  AlignedType() { ++constructed; }
+  AlignedType(AlignedType const&) { ++constructed; }
+  ~AlignedType() { --constructed; }
+};
+template <size_t Align>
+int AlignedType<Align>::constructed = 0;
+
+
+template <size_t Align>
+void test_aligned() {
+  typedef AlignedType<Align> T;
+  T::constructed = 0;
+  globalMemCounter.reset();
+  std::allocator<T> a;
+  const bool IsOverAlignedType = Align > MaxAligned;
+  const bool ExpectAligned = IsOverAlignedType && UsingAlignedNew;
+  {
+    assert(globalMemCounter.checkOutstandingNewEq(0));
+    assert(T::constructed == 0);
+    globalMemCounter.last_new_size = 0;
+    globalMemCounter.last_new_align = 0;
+    std::same_as<std::allocation_result<T*>> decltype(auto) ap = a.allocate_at_least(3);
+    assert(ap.count >= 3);
+    DoNotOptimize(ap);
+    assert(globalMemCounter.checkOutstandingNewEq(1));
+    assert(globalMemCounter.checkNewCalledEq(1));
+    assert(globalMemCounter.checkAlignedNewCalledEq(ExpectAligned));
+    assert(globalMemCounter.checkLastNewSizeEq(3 * sizeof(T)));
+    assert(globalMemCounter.checkLastNewAlignEq(ExpectAligned ? Align : 0));
+    assert(T::constructed == 0);
+    globalMemCounter.last_delete_align = 0;
+    a.deallocate(ap.ptr, 3);
+    assert(globalMemCounter.checkOutstandingNewEq(0));
+    assert(globalMemCounter.checkDeleteCalledEq(1));
+    assert(globalMemCounter.checkAlignedDeleteCalledEq(ExpectAligned));
+    assert(globalMemCounter.checkLastDeleteAlignEq(ExpectAligned ? Align : 0));
+    assert(T::constructed == 0);
+  }
+}
+
+template <size_t Align>
+constexpr bool test_aligned_constexpr() {
+    typedef AlignedType<Align> T;
+    std::allocator<T> a;
+    std::same_as<std::allocation_result<T*>> decltype(auto) ap = a.allocate_at_least(3);
+    assert(ap.count >= 3);
+    a.deallocate(ap.ptr, 3);
+
+    return true;
+}
+
+int main(int, char**) {
+    test_aligned<1>();
+    test_aligned<2>();
+    test_aligned<4>();
+    test_aligned<8>();
+    test_aligned<16>();
+    test_aligned<MaxAligned>();
+    test_aligned<OverAligned>();
+    test_aligned<OverAligned * 2>();
+
+    static_assert(test_aligned_constexpr<1>());
+    static_assert(test_aligned_constexpr<2>());
+    static_assert(test_aligned_constexpr<4>());
+    static_assert(test_aligned_constexpr<8>());
+    static_assert(test_aligned_constexpr<16>());
+    static_assert(test_aligned_constexpr<MaxAligned>());
+    static_assert(test_aligned_constexpr<OverAligned>());
+    static_assert(test_aligned_constexpr<OverAligned * 2>());
+  return 0;
+}

diff  --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 61cee0dd2d332..9857fe4dc12fd 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -74,7 +74,6 @@ def add_version_header(tc):
     "name": "__cpp_lib_allocate_at_least",
     "values": { "c++2b": 202106 },
     "headers": ["memory"],
-    "unimplemented": True,
   }, {
     "name": "__cpp_lib_allocator_traits_is_always_equal",
     "values": { "c++17": 201411 },


        


More information about the libcxx-commits mailing list