[libcxx-commits] [libcxx] 0724f8b - [libc++] Implement C++20's P0784 (More constexpr containers)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Tue Sep 22 08:20:45 PDT 2020


Author: Louis Dionne
Date: 2020-09-22T11:20:33-04:00
New Revision: 0724f8bf47f8cb073d41e2750d45d5b05e66bf0b

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

LOG: [libc++] Implement C++20's P0784 (More constexpr containers)

This commit adds std::construct_at, and marks various members of
std::allocator_traits and std::allocator as constexpr. It also adds
tests and turns the existing tests into hybrid constexpr/runtime tests.

Thanks to Richard Smith for initial work on this, and to Michael Park
for D69803, D69132 and D69134, which are superseded by this patch.

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

Added: 
    libcxx/test/std/utilities/memory/default.allocator/allocator.dtor.pass.cpp
    libcxx/test/std/utilities/memory/default.allocator/allocator.members/allocate.constexpr.size.verify.cpp
    libcxx/test/std/utilities/memory/specialized.algorithms/specialized.construct/construct_at.pass.cpp

Modified: 
    libcxx/docs/FeatureTestMacroTable.rst
    libcxx/include/memory
    libcxx/include/new
    libcxx/include/version
    libcxx/test/std/containers/sequences/vector/vector.cons/copy.move_only.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/test/std/utilities/memory/allocator.traits/allocator.traits.members/allocate.pass.cpp
    libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/allocate.verify.cpp
    libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/allocate_hint.pass.cpp
    libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/construct.pass.cpp
    libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/deallocate.pass.cpp
    libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/destroy.pass.cpp
    libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/max_size.pass.cpp
    libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/select_on_container_copy_construction.pass.cpp
    libcxx/test/std/utilities/memory/default.allocator/allocator.globals/eq.pass.cpp
    libcxx/test/std/utilities/memory/default.allocator/allocator.members/allocate.pass.cpp
    libcxx/test/std/utilities/memory/default.allocator/allocator.members/allocate.size.pass.cpp
    libcxx/test/std/utilities/memory/default.allocator/allocator_types.pass.cpp
    libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy.pass.cpp
    libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_at.pass.cpp
    libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_n.pass.cpp
    libcxx/utils/generate_feature_test_macro_components.py
    libcxx/www/cxx2a_status.html

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 66162d73727f..0f17a3a1bd54 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -192,6 +192,8 @@ Status
     ------------------------------------------------- -----------------
     ``__cpp_lib_concepts``                            *unimplemented*
     ------------------------------------------------- -----------------
+    ``__cpp_lib_constexpr_dynamic_alloc``             ``201907L``
+    ------------------------------------------------- -----------------
     ``__cpp_lib_constexpr_misc``                      *unimplemented*
     ------------------------------------------------- -----------------
     ``__cpp_lib_constexpr_swap_algorithms``           *unimplemented*

diff  --git a/libcxx/include/memory b/libcxx/include/memory
index 0ce7d092a2e1..add0cc3e73dd 100644
--- a/libcxx/include/memory
+++ b/libcxx/include/memory
@@ -83,21 +83,19 @@ struct allocator_traits
     template <class T> using rebind_alloc  = Alloc::rebind<T>::other | Alloc<T, Args...>;
     template <class T> using rebind_traits = allocator_traits<rebind_alloc<T>>;
 
-    static pointer allocate(allocator_type& a, size_type n);                          // [[nodiscard]] in C++20
-    static pointer allocate(allocator_type& a, size_type n, const_void_pointer hint); // [[nodiscard]] in C++20
+    static pointer allocate(allocator_type& a, size_type n);                          // constexpr and [[nodiscard]] in C++20
+    static pointer allocate(allocator_type& a, size_type n, const_void_pointer hint); // constexpr and [[nodiscard]] in C++20
 
-    static void deallocate(allocator_type& a, pointer p, size_type n) noexcept;
+    static void deallocate(allocator_type& a, pointer p, size_type n) noexcept; // constexpr in C++20
 
     template <class T, class... Args>
-        static void construct(allocator_type& a, T* p, Args&&... args);
+    static void construct(allocator_type& a, T* p, Args&&... args); // constexpr in C++20
 
     template <class T>
-        static void destroy(allocator_type& a, T* p);
+    static void destroy(allocator_type& a, T* p); // constexpr in C++20
 
-    static size_type max_size(const allocator_type& a); // noexcept in C++14
-
-    static allocator_type
-        select_on_container_copy_construction(const allocator_type& a);
+    static size_type max_size(const allocator_type& a); // noexcept in C++14, constexpr in C++20
+    static allocator_type select_on_container_copy_construction(const allocator_type& a); // constexpr in C++20
 };
 
 template <>
@@ -135,12 +133,12 @@ public:
     constexpr allocator(const allocator&) noexcept;      // constexpr in C++20
     template <class U>
       constexpr allocator(const allocator<U>&) noexcept; // constexpr in C++20
-    ~allocator();
+    ~allocator();                                        // constexpr in C++20
     pointer address(reference x) const noexcept;             // deprecated in C++17, removed in C++20
     const_pointer address(const_reference x) const noexcept; // deprecated in C++17, removed in C++20
     T* allocate(size_t n, const void* hint);          // deprecated in C++17, removed in C++20
-    T* allocate(size_t n);
-    void deallocate(T* p, size_t n) noexcept;
+    T* allocate(size_t n);                              // constexpr in C++20
+    void deallocate(T* p, size_t n) noexcept;           // constexpr in C++20
     size_type max_size() const noexcept;              // deprecated in C++17, removed in C++20
     template<class U, class... Args>
         void construct(U* p, Args&&... args);         // deprecated in C++17, removed in C++20
@@ -149,10 +147,10 @@ public:
 };
 
 template <class T, class U>
-bool operator==(const allocator<T>&, const allocator<U>&) noexcept;
+bool operator==(const allocator<T>&, const allocator<U>&) noexcept; // constexpr in C++20
 
 template <class T, class U>
-bool operator!=(const allocator<T>&, const allocator<U>&) noexcept;
+bool operator!=(const allocator<T>&, const allocator<U>&) noexcept; // constexpr in C++20
 
 template <class OutputIterator, class T>
 class raw_storage_iterator
@@ -191,14 +189,17 @@ template <class ForwardIterator, class Size, class T>
 ForwardIterator
 uninitialized_fill_n(ForwardIterator first, Size n, const T& x);
 
+template <class T, class ...Args>
+constexpr T* construct_at(T* location, Args&& ...args); // since C++20
+
 template <class T>
-void destroy_at(T* location);
+void destroy_at(T* location); // constexpr in C++20
 
 template <class ForwardIterator>
- void destroy(ForwardIterator first, ForwardIterator last);
+void destroy(ForwardIterator first, ForwardIterator last); // constexpr in C++20
 
 template <class ForwardIterator, class Size>
- ForwardIterator destroy_n(ForwardIterator first, Size n);
+ForwardIterator destroy_n(ForwardIterator first, Size n); // constexpr in C++20
 
 template <class InputIterator, class ForwardIterator>
  ForwardIterator uninitialized_move(InputIterator first, InputIterator last, ForwardIterator result);
@@ -886,6 +887,39 @@ struct __rebind_pointer {
 #endif
 };
 
+// construct_at
+
+#if _LIBCPP_STD_VER > 17
+
+template<class _Tp>
+_LIBCPP_CONSTEXPR_AFTER_CXX17 void* __voidify(_Tp& __ptr) noexcept {
+    return const_cast<void*>(static_cast<const volatile void*>(_VSTD::addressof(__ptr)));
+}
+
+template<class _Tp, class ..._Args, class = decltype(
+    ::new (_VSTD::declval<void*>()) _Tp(_VSTD::declval<_Args>()...)
+)>
+_LIBCPP_INLINE_VISIBILITY
+constexpr _Tp* construct_at(_Tp* __location, _Args&& ...__args) {
+    _LIBCPP_ASSERT(__location, "null pointer given to construct_at");
+    return ::new (_VSTD::__voidify(*__location)) _Tp(_VSTD::forward<_Args>(__args)...);
+}
+
+#endif
+
+// destroy_at
+
+#if _LIBCPP_STD_VER > 14
+
+template <class _Tp>
+inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
+void destroy_at(_Tp* __loc) {
+    _LIBCPP_ASSERT(__loc, "null pointer given to destroy_at");
+    __loc->~_Tp();
+}
+
+#endif
+
 // allocator_traits
 
 template <class _Tp, class = void>
@@ -1390,34 +1424,34 @@ struct _LIBCPP_TEMPLATE_VIS allocator_traits
         {typedef allocator_traits<typename rebind_alloc<_Tp>::other> other;};
 #endif  // _LIBCPP_CXX03_LANG
 
-    _LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_INLINE_VISIBILITY
+    _LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
     static pointer allocate(allocator_type& __a, size_type __n)
         {return __a.allocate(__n);}
-    _LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_INLINE_VISIBILITY
+    _LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
     static pointer allocate(allocator_type& __a, size_type __n, const_void_pointer __hint)
         {return __allocate(__a, __n, __hint,
             __has_allocate_hint<allocator_type, size_type, const_void_pointer>());}
 
-    _LIBCPP_INLINE_VISIBILITY
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
     static void deallocate(allocator_type& __a, pointer __p, size_type __n) _NOEXCEPT
         {__a.deallocate(__p, __n);}
 
     template <class _Tp, class... _Args>
-        _LIBCPP_INLINE_VISIBILITY
+        _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
         static void construct(allocator_type& __a, _Tp* __p, _Args&&... __args)
             {__construct(__has_construct<allocator_type, _Tp*, _Args...>(),
                          __a, __p, _VSTD::forward<_Args>(__args)...);}
 
     template <class _Tp>
-        _LIBCPP_INLINE_VISIBILITY
+        _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
         static void destroy(allocator_type& __a, _Tp* __p)
             {__destroy(__has_destroy<allocator_type, _Tp*>(), __a, __p);}
 
-    _LIBCPP_INLINE_VISIBILITY
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
     static size_type max_size(const allocator_type& __a) _NOEXCEPT
         {return __max_size(__has_max_size<const allocator_type>(), __a);}
 
-    _LIBCPP_INLINE_VISIBILITY
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
     static allocator_type
         select_on_container_copy_construction(const allocator_type& __a)
             {return __select_on_container_copy_construction(
@@ -1536,7 +1570,7 @@ struct _LIBCPP_TEMPLATE_VIS allocator_traits
 
 private:
 
-    _LIBCPP_INLINE_VISIBILITY
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
     static pointer __allocate(allocator_type& __a, size_type __n,
         const_void_pointer __hint, true_type)
         {
@@ -1544,13 +1578,13 @@ private:
             return __a.allocate(__n, __hint);
             _LIBCPP_SUPPRESS_DEPRECATED_POP
         }
-    _LIBCPP_INLINE_VISIBILITY
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
     static pointer __allocate(allocator_type& __a, size_type __n,
         const_void_pointer, false_type)
         {return __a.allocate(__n);}
 
     template <class _Tp, class... _Args>
-        _LIBCPP_INLINE_VISIBILITY
+        _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
         static void __construct(true_type, allocator_type& __a, _Tp* __p, _Args&&... __args)
             {
                 _LIBCPP_SUPPRESS_DEPRECATED_PUSH
@@ -1559,14 +1593,18 @@ private:
             }
 
     template <class _Tp, class... _Args>
-        _LIBCPP_INLINE_VISIBILITY
+        _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
         static void __construct(false_type, allocator_type&, _Tp* __p, _Args&&... __args)
             {
+#if _LIBCPP_STD_VER > 17
+                _VSTD::construct_at(__p, _VSTD::forward<_Args>(__args)...);
+#else
                 ::new ((void*)__p) _Tp(_VSTD::forward<_Args>(__args)...);
+#endif
             }
 
     template <class _Tp>
-        _LIBCPP_INLINE_VISIBILITY
+        _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
         static void __destroy(true_type, allocator_type& __a, _Tp* __p)
             {
                 _LIBCPP_SUPPRESS_DEPRECATED_PUSH
@@ -1574,13 +1612,17 @@ private:
                 _LIBCPP_SUPPRESS_DEPRECATED_POP
             }
     template <class _Tp>
-        _LIBCPP_INLINE_VISIBILITY
+        _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
         static void __destroy(false_type, allocator_type&, _Tp* __p)
             {
+#if _LIBCPP_STD_VER > 17
+                _VSTD::destroy_at(__p);
+#else
                 __p->~_Tp();
+#endif
             }
 
-    _LIBCPP_INLINE_VISIBILITY
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
     static size_type __max_size(true_type, const allocator_type& __a) _NOEXCEPT
             {
                 _LIBCPP_SUPPRESS_DEPRECATED_PUSH
@@ -1588,15 +1630,15 @@ private:
                 _LIBCPP_SUPPRESS_DEPRECATED_POP
             }
 
-    _LIBCPP_INLINE_VISIBILITY
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
     static size_type __max_size(false_type, const allocator_type&) _NOEXCEPT
             {return numeric_limits<size_type>::max() / sizeof(value_type);}
 
-    _LIBCPP_INLINE_VISIBILITY
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
     static allocator_type
         __select_on_container_copy_construction(true_type, const allocator_type& __a)
             {return __a.select_on_container_copy_construction();}
-    _LIBCPP_INLINE_VISIBILITY
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
     static allocator_type
         __select_on_container_copy_construction(false_type, const allocator_type& __a)
             {return __a;}
@@ -1650,10 +1692,10 @@ public:
         {return _VSTD::addressof(__x);}
 #endif
 
-    _LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_INLINE_VISIBILITY _Tp* allocate(size_t __n)
+    _LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
+    _Tp* allocate(size_t __n)
         {
-        // TODO(mpark): Replace with `allocator_traits<allocator>::max_size(*this)`.
-        if (__n > (size_t(~0) / sizeof(_Tp)))
+        if (__n > allocator_traits<allocator>::max_size(*this))
             __throw_length_error("allocator<T>::allocate(size_t n)"
                                  " 'n' exceeds maximum supported size");
         return static_cast<_Tp*>(_VSTD::__libcpp_allocate(__n * sizeof(_Tp), _LIBCPP_ALIGNOF(_Tp)));
@@ -1664,7 +1706,8 @@ public:
     _Tp* allocate(size_t __n, const void*) { return allocate(__n); }
 #endif
 
-    _LIBCPP_INLINE_VISIBILITY void deallocate(_Tp* __p, size_t __n) _NOEXCEPT
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
+    void deallocate(_Tp* __p, size_t __n) _NOEXCEPT
         {_VSTD::__libcpp_deallocate((void*)__p, __n * sizeof(_Tp), _LIBCPP_ALIGNOF(_Tp));}
 
 #if _LIBCPP_STD_VER <= 17 || defined(_LIBCPP_ENABLE_CXX20_REMOVED_ALLOCATOR_MEMBERS)
@@ -1715,10 +1758,10 @@ public:
         {return _VSTD::addressof(__x);}
 #endif
 
-    _LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_INLINE_VISIBILITY const _Tp* allocate(size_t __n)
+    _LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
+    const _Tp* allocate(size_t __n)
     {
-        // TODO(mpark): Replace with `allocator_traits<allocator>::max_size(*this)`.
-        if (__n > (size_t(~0) / sizeof(_Tp)))
+        if (__n > allocator_traits<allocator>::max_size(*this))
             __throw_length_error("allocator<const T>::allocate(size_t n)"
                                  " 'n' exceeds maximum supported size");
         return static_cast<const _Tp*>(_VSTD::__libcpp_allocate(__n * sizeof(_Tp), _LIBCPP_ALIGNOF(_Tp)));
@@ -1729,7 +1772,8 @@ public:
     const _Tp* allocate(size_t __n, const void*) { return allocate(__n); }
 #endif
 
-    _LIBCPP_INLINE_VISIBILITY void deallocate(const _Tp* __p, size_t __n)
+    _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
+    void deallocate(const _Tp* __p, size_t __n)
         {_VSTD::__libcpp_deallocate((void*) const_cast<_Tp *>(__p), __n * sizeof(_Tp), _LIBCPP_ALIGNOF(_Tp));}
 
 #if _LIBCPP_STD_VER <= 17 || defined(_LIBCPP_ENABLE_CXX20_REMOVED_ALLOCATOR_MEMBERS)
@@ -1749,11 +1793,11 @@ public:
 };
 
 template <class _Tp, class _Up>
-inline _LIBCPP_INLINE_VISIBILITY
+inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
 bool operator==(const allocator<_Tp>&, const allocator<_Up>&) _NOEXCEPT {return true;}
 
 template <class _Tp, class _Up>
-inline _LIBCPP_INLINE_VISIBILITY
+inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
 bool operator!=(const allocator<_Tp>&, const allocator<_Up>&) _NOEXCEPT {return false;}
 
 template <class _OutputIterator, class _Tp>
@@ -2938,22 +2982,15 @@ uninitialized_fill_n(_ForwardIterator __f, _Size __n, const _Tp& __x)
 
 #if _LIBCPP_STD_VER > 14
 
-template <class _Tp>
-inline _LIBCPP_INLINE_VISIBILITY
-void destroy_at(_Tp* __loc) {
-    _LIBCPP_ASSERT(__loc, "null pointer given to destroy_at");
-    __loc->~_Tp();
-}
-
 template <class _ForwardIterator>
-inline _LIBCPP_INLINE_VISIBILITY
+inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
 void destroy(_ForwardIterator __first, _ForwardIterator __last) {
     for (; __first != __last; ++__first)
         _VSTD::destroy_at(_VSTD::addressof(*__first));
 }
 
 template <class _ForwardIterator, class _Size>
-inline _LIBCPP_INLINE_VISIBILITY
+inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
 _ForwardIterator destroy_n(_ForwardIterator __first, _Size __n) {
     for (; __n > 0; (void)++__first, --__n)
         _VSTD::destroy_at(_VSTD::addressof(*__first));

diff  --git a/libcxx/include/new b/libcxx/include/new
index 40d351e9b770..7db26b399229 100644
--- a/libcxx/include/new
+++ b/libcxx/include/new
@@ -234,7 +234,8 @@ _LIBCPP_CONSTEXPR inline _LIBCPP_INLINE_VISIBILITY bool __is_overaligned_for_new
 #endif
 }
 
-inline _LIBCPP_INLINE_VISIBILITY void *__libcpp_allocate(size_t __size, size_t __align) {
+inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
+void *__libcpp_allocate(size_t __size, size_t __align) {
 #ifndef _LIBCPP_HAS_NO_ALIGNED_ALLOCATION
   if (__is_overaligned_for_new(__align)) {
     const align_val_t __align_val = static_cast<align_val_t>(__align);
@@ -255,37 +256,38 @@ inline _LIBCPP_INLINE_VISIBILITY void *__libcpp_allocate(size_t __size, size_t _
 }
 
 struct _DeallocateCaller {
-  static inline _LIBCPP_INLINE_VISIBILITY
-  void __do_deallocate_handle_size_align(void *__ptr, size_t __size, size_t __align) {
-#if defined(_LIBCPP_HAS_NO_ALIGNED_ALLOCATION)
-    ((void)__align);
-    return __do_deallocate_handle_size(__ptr, __size);
+  template <class _A1, class _A2>
+  _LIBCPP_CONSTEXPR_AFTER_CXX17
+  static inline void __do_call(void *__ptr, _A1 __a1, _A2 __a2) {
+#if defined(_LIBCPP_HAS_NO_BUILTIN_OPERATOR_NEW_DELETE) || \
+    defined(_LIBCPP_HAS_NO_BUILTIN_OVERLOADED_OPERATOR_NEW_DELETE)
+    return ::operator delete(__ptr, __a1, __a2);
 #else
-    if (__is_overaligned_for_new(__align)) {
-      const align_val_t __align_val = static_cast<align_val_t>(__align);
-      return __do_deallocate_handle_size(__ptr, __size, __align_val);
-    } else {
-      return __do_deallocate_handle_size(__ptr, __size);
-    }
+    return __builtin_operator_delete(__ptr, __a1, __a2);
 #endif
   }
 
-  static inline _LIBCPP_INLINE_VISIBILITY
-  void __do_deallocate_handle_align(void *__ptr, size_t __align) {
-#if defined(_LIBCPP_HAS_NO_ALIGNED_ALLOCATION)
-    ((void)__align);
-    return __do_call(__ptr);
+  template <class _A1>
+  _LIBCPP_CONSTEXPR_AFTER_CXX17
+  static inline void __do_call(void *__ptr, _A1 __a1) {
+#if defined(_LIBCPP_HAS_NO_BUILTIN_OPERATOR_NEW_DELETE) || \
+    defined(_LIBCPP_HAS_NO_BUILTIN_OVERLOADED_OPERATOR_NEW_DELETE)
+    return ::operator delete(__ptr, __a1);
 #else
-    if (__is_overaligned_for_new(__align)) {
-      const align_val_t __align_val = static_cast<align_val_t>(__align);
-      return __do_call(__ptr, __align_val);
-    } else {
-      return __do_call(__ptr);
-    }
+    return __builtin_operator_delete(__ptr, __a1);
+#endif
+  }
+
+  _LIBCPP_CONSTEXPR_AFTER_CXX17
+  static inline void __do_call(void *__ptr) {
+#ifdef _LIBCPP_HAS_NO_BUILTIN_OPERATOR_NEW_DELETE
+    return ::operator delete(__ptr);
+#else
+    return __builtin_operator_delete(__ptr);
 #endif
   }
 
- private:
+  _LIBCPP_CONSTEXPR_AFTER_CXX17
   static inline void __do_deallocate_handle_size(void *__ptr, size_t __size) {
 #ifdef _LIBCPP_HAS_NO_SIZED_DEALLOCATION
     ((void)__size);
@@ -296,6 +298,7 @@ struct _DeallocateCaller {
   }
 
 #ifndef _LIBCPP_HAS_NO_ALIGNED_ALLOCATION
+  _LIBCPP_CONSTEXPR_AFTER_CXX17
   static inline void __do_deallocate_handle_size(void *__ptr, size_t __size, align_val_t __align) {
 #ifdef _LIBCPP_HAS_NO_SIZED_DEALLOCATION
     ((void)__size);
@@ -306,37 +309,39 @@ struct _DeallocateCaller {
   }
 #endif
 
-private:
-  template <class _A1, class _A2>
-  static inline void __do_call(void *__ptr, _A1 __a1, _A2 __a2) {
-#if defined(_LIBCPP_HAS_NO_BUILTIN_OPERATOR_NEW_DELETE) || \
-    defined(_LIBCPP_HAS_NO_BUILTIN_OVERLOADED_OPERATOR_NEW_DELETE)
-    return ::operator delete(__ptr, __a1, __a2);
-#else
-    return __builtin_operator_delete(__ptr, __a1, __a2);
-#endif
-  }
-
-  template <class _A1>
-  static inline void __do_call(void *__ptr, _A1 __a1) {
-#if defined(_LIBCPP_HAS_NO_BUILTIN_OPERATOR_NEW_DELETE) || \
-    defined(_LIBCPP_HAS_NO_BUILTIN_OVERLOADED_OPERATOR_NEW_DELETE)
-    return ::operator delete(__ptr, __a1);
+  static inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
+  void __do_deallocate_handle_size_align(void *__ptr, size_t __size, size_t __align) {
+#if defined(_LIBCPP_HAS_NO_ALIGNED_ALLOCATION)
+    ((void)__align);
+    return __do_deallocate_handle_size(__ptr, __size);
 #else
-    return __builtin_operator_delete(__ptr, __a1);
+    if (__is_overaligned_for_new(__align)) {
+      const align_val_t __align_val = static_cast<align_val_t>(__align);
+      return __do_deallocate_handle_size(__ptr, __size, __align_val);
+    } else {
+      return __do_deallocate_handle_size(__ptr, __size);
+    }
 #endif
   }
 
-  static inline void __do_call(void *__ptr) {
-#ifdef _LIBCPP_HAS_NO_BUILTIN_OPERATOR_NEW_DELETE
-    return ::operator delete(__ptr);
+  static inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
+  void __do_deallocate_handle_align(void *__ptr, size_t __align) {
+#if defined(_LIBCPP_HAS_NO_ALIGNED_ALLOCATION)
+    ((void)__align);
+    return __do_call(__ptr);
 #else
-    return __builtin_operator_delete(__ptr);
+    if (__is_overaligned_for_new(__align)) {
+      const align_val_t __align_val = static_cast<align_val_t>(__align);
+      return __do_call(__ptr, __align_val);
+    } else {
+      return __do_call(__ptr);
+    }
 #endif
   }
 };
 
-inline _LIBCPP_INLINE_VISIBILITY void __libcpp_deallocate(void* __ptr, size_t __size, size_t __align) {
+inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
+void __libcpp_deallocate(void* __ptr, size_t __size, size_t __align) {
   _DeallocateCaller::__do_deallocate_handle_size_align(__ptr, __size, __align);
 }
 

diff  --git a/libcxx/include/version b/libcxx/include/version
index d18da3d14690..3355e2d18c56 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -45,6 +45,7 @@ __cpp_lib_chrono_udls                                   201304L <chrono>
 __cpp_lib_clamp                                         201603L <algorithm>
 __cpp_lib_complex_udls                                  201309L <complex>
 __cpp_lib_concepts                                      201806L <concepts>
+__cpp_lib_constexpr_dynamic_alloc                       201907L <memory>
 __cpp_lib_constexpr_misc                                201811L <array> <functional> <iterator>
                                                                 <string_view> <tuple> <utility>
 __cpp_lib_constexpr_swap_algorithms                     201806L <algorithm>
@@ -250,6 +251,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 #   define __cpp_lib_char8_t                            201811L
 # endif
 // # define __cpp_lib_concepts                             201806L
+# define __cpp_lib_constexpr_dynamic_alloc              201907L
 // # define __cpp_lib_constexpr_misc                       201811L
 // # define __cpp_lib_constexpr_swap_algorithms            201806L
 # define __cpp_lib_constexpr_utility                    201811L

diff  --git a/libcxx/test/std/containers/sequences/vector/vector.cons/copy.move_only.verify.cpp b/libcxx/test/std/containers/sequences/vector/vector.cons/copy.move_only.verify.cpp
index cc9e1405b032..17becf2e5a28 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.cons/copy.move_only.verify.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.cons/copy.move_only.verify.cpp
@@ -22,6 +22,6 @@ struct move_only
 int main(int, char**)
 {
     std::vector<move_only> v;
-    std::vector<move_only> copy = v; // expected-error at memory:* {{call to implicitly-deleted copy constructor of 'move_only'}}
+    std::vector<move_only> copy = v; // expected-error-re at memory:* {{{{(no matching function for call to 'construct_at')|(call to implicitly-deleted copy constructor of 'move_only')}}}}
     return 0;
 }

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 0117fd83a60c..511c688099fb 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
@@ -17,6 +17,7 @@
     __cpp_lib_addressof_constexpr                 201603L [C++17]
     __cpp_lib_allocator_traits_is_always_equal    201411L [C++17]
     __cpp_lib_atomic_value_initialization         201911L [C++2a]
+    __cpp_lib_constexpr_dynamic_alloc             201907L [C++2a]
     __cpp_lib_enable_shared_from_this             201603L [C++17]
     __cpp_lib_make_unique                         201304L [C++14]
     __cpp_lib_ranges                              201811L [C++2a]
@@ -42,6 +43,10 @@
 #   error "__cpp_lib_atomic_value_initialization should not be defined before c++2a"
 # endif
 
+# ifdef __cpp_lib_constexpr_dynamic_alloc
+#   error "__cpp_lib_constexpr_dynamic_alloc should not be defined before c++2a"
+# endif
+
 # ifdef __cpp_lib_enable_shared_from_this
 #   error "__cpp_lib_enable_shared_from_this should not be defined before c++17"
 # endif
@@ -80,6 +85,10 @@
 #   error "__cpp_lib_atomic_value_initialization should not be defined before c++2a"
 # endif
 
+# ifdef __cpp_lib_constexpr_dynamic_alloc
+#   error "__cpp_lib_constexpr_dynamic_alloc should not be defined before c++2a"
+# endif
+
 # ifdef __cpp_lib_enable_shared_from_this
 #   error "__cpp_lib_enable_shared_from_this should not be defined before c++17"
 # endif
@@ -133,6 +142,10 @@
 #   error "__cpp_lib_atomic_value_initialization should not be defined before c++2a"
 # endif
 
+# ifdef __cpp_lib_constexpr_dynamic_alloc
+#   error "__cpp_lib_constexpr_dynamic_alloc should not be defined before c++2a"
+# endif
+
 # ifndef __cpp_lib_enable_shared_from_this
 #   error "__cpp_lib_enable_shared_from_this should be defined in c++17"
 # endif
@@ -213,6 +226,13 @@
 #   endif
 # endif
 
+# ifndef __cpp_lib_constexpr_dynamic_alloc
+#   error "__cpp_lib_constexpr_dynamic_alloc should be defined in c++2a"
+# endif
+# if __cpp_lib_constexpr_dynamic_alloc != 201907L
+#   error "__cpp_lib_constexpr_dynamic_alloc should have the value 201907L in c++2a"
+# endif
+
 # ifndef __cpp_lib_enable_shared_from_this
 #   error "__cpp_lib_enable_shared_from_this should be defined in c++2a"
 # endif

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 46b2e1f21d18..86579298afdb 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
@@ -40,6 +40,7 @@
     __cpp_lib_clamp                                201603L [C++17]
     __cpp_lib_complex_udls                         201309L [C++14]
     __cpp_lib_concepts                             201806L [C++2a]
+    __cpp_lib_constexpr_dynamic_alloc              201907L [C++2a]
     __cpp_lib_constexpr_misc                       201811L [C++2a]
     __cpp_lib_constexpr_swap_algorithms            201806L [C++2a]
     __cpp_lib_constexpr_utility                    201811L [C++2a]
@@ -217,6 +218,10 @@
 #   error "__cpp_lib_concepts should not be defined before c++2a"
 # endif
 
+# ifdef __cpp_lib_constexpr_dynamic_alloc
+#   error "__cpp_lib_constexpr_dynamic_alloc should not be defined before c++2a"
+# endif
+
 # ifdef __cpp_lib_constexpr_misc
 #   error "__cpp_lib_constexpr_misc should not be defined before c++2a"
 # endif
@@ -601,6 +606,10 @@
 #   error "__cpp_lib_concepts should not be defined before c++2a"
 # endif
 
+# ifdef __cpp_lib_constexpr_dynamic_alloc
+#   error "__cpp_lib_constexpr_dynamic_alloc should not be defined before c++2a"
+# endif
+
 # ifdef __cpp_lib_constexpr_misc
 #   error "__cpp_lib_constexpr_misc should not be defined before c++2a"
 # endif
@@ -1099,6 +1108,10 @@
 #   error "__cpp_lib_concepts should not be defined before c++2a"
 # endif
 
+# ifdef __cpp_lib_constexpr_dynamic_alloc
+#   error "__cpp_lib_constexpr_dynamic_alloc should not be defined before c++2a"
+# endif
+
 # ifdef __cpp_lib_constexpr_misc
 #   error "__cpp_lib_constexpr_misc should not be defined before c++2a"
 # endif
@@ -1864,6 +1877,13 @@
 #   endif
 # endif
 
+# ifndef __cpp_lib_constexpr_dynamic_alloc
+#   error "__cpp_lib_constexpr_dynamic_alloc should be defined in c++2a"
+# endif
+# if __cpp_lib_constexpr_dynamic_alloc != 201907L
+#   error "__cpp_lib_constexpr_dynamic_alloc should have the value 201907L in c++2a"
+# endif
+
 # if !defined(_LIBCPP_VERSION)
 #   ifndef __cpp_lib_constexpr_misc
 #     error "__cpp_lib_constexpr_misc should be defined in c++2a"

diff  --git a/libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/allocate.pass.cpp b/libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/allocate.pass.cpp
index 9f923074d26b..ab354f42a1d8 100644
--- a/libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/allocate.pass.cpp
+++ b/libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/allocate.pass.cpp
@@ -11,7 +11,7 @@
 // template <class Alloc>
 // struct allocator_traits
 // {
-//     static pointer allocate(allocator_type& a, size_type n);
+//     static constexpr pointer allocate(allocator_type& a, size_type n);
 //     ...
 // };
 
@@ -27,25 +27,37 @@ struct A
 {
     typedef T value_type;
 
-    value_type* allocate(std::size_t n)
+    TEST_CONSTEXPR_CXX20 A() {}
+
+    TEST_CONSTEXPR_CXX20 value_type* allocate(std::size_t n)
     {
         assert(n == 10);
-        return reinterpret_cast<value_type*>(static_cast<std::uintptr_t>(0xDEADBEEF));
+        return &storage;
     }
+
+    value_type storage;
 };
 
+TEST_CONSTEXPR_CXX20 bool test()
+{
+    {
+        A<int> a;
+        assert(std::allocator_traits<A<int> >::allocate(a, 10) == &a.storage);
+    }
+    {
+        typedef A<IncompleteHolder*> Alloc;
+        Alloc a;
+        assert(std::allocator_traits<Alloc>::allocate(a, 10) == &a.storage);
+    }
+
+    return true;
+}
+
 int main(int, char**)
 {
-  {
-    A<int> a;
-    assert(std::allocator_traits<A<int> >::allocate(a, 10) == reinterpret_cast<int*>(static_cast<std::uintptr_t>(0xDEADBEEF)));
-  }
-  {
-    typedef IncompleteHolder* VT;
-    typedef A<VT> Alloc;
-    Alloc a;
-    assert(std::allocator_traits<Alloc >::allocate(a, 10) == reinterpret_cast<VT*>(static_cast<std::uintptr_t>(0xDEADBEEF)));
-  }
-
-  return 0;
+    test();
+#if TEST_STD_VER > 17
+    static_assert(test());
+#endif
+    return 0;
 }

diff  --git a/libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/allocate.verify.cpp b/libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/allocate.verify.cpp
index c6e03cb2ff5e..b1fc7a6bf637 100644
--- a/libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/allocate.verify.cpp
+++ b/libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/allocate.verify.cpp
@@ -11,7 +11,7 @@
 // template <class Alloc>
 // struct allocator_traits
 // {
-//     static pointer allocate(allocator_type& a, size_type n);
+//     static constexpr pointer allocate(allocator_type& a, size_type n);
 //     ...
 // };
 

diff  --git a/libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/allocate_hint.pass.cpp b/libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/allocate_hint.pass.cpp
index e95247163988..a8205f7d884b 100644
--- a/libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/allocate_hint.pass.cpp
+++ b/libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/allocate_hint.pass.cpp
@@ -11,7 +11,7 @@
 // template <class Alloc>
 // struct allocator_traits
 // {
-//     static pointer allocate(allocator_type& a, size_type n, const_void_pointer hint);
+//     static constexpr pointer allocate(allocator_type& a, size_type n, const_void_pointer hint);
 //     ...
 // };
 
@@ -27,11 +27,15 @@ struct A
 {
     typedef T value_type;
 
-    value_type* allocate(std::size_t n)
+    TEST_CONSTEXPR_CXX20 A() {}
+
+    TEST_CONSTEXPR_CXX20 value_type* allocate(std::size_t n)
     {
         assert(n == 10);
-        return reinterpret_cast<value_type*>(static_cast<std::uintptr_t>(0xDEADBEEF));
+        return &storage;
     }
+
+    value_type storage;
 };
 
 template <class T>
@@ -39,44 +43,48 @@ struct B
 {
     typedef T value_type;
 
-    value_type* allocate(std::size_t n)
-    {
-        assert(n == 12);
-        return reinterpret_cast<value_type*>(static_cast<std::uintptr_t>(0xEEADBEEF));
-    }
-    value_type* allocate(std::size_t n, const void* p)
+    TEST_CONSTEXPR_CXX20 value_type* allocate(std::size_t n, const void* p)
     {
         assert(n == 11);
-        assert(p == 0);
-        return reinterpret_cast<value_type*>(static_cast<std::uintptr_t>(0xFEADBEEF));
+        assert(p == nullptr);
+        return &storage;
     }
-};
 
+    value_type storage;
+};
 
-int main(int, char**)
+TEST_CONSTEXPR_CXX20 bool test()
 {
 #if TEST_STD_VER >= 11
-  {
-    A<int> a;
-    assert(std::allocator_traits<A<int> >::allocate(a, 10, nullptr) == reinterpret_cast<int*>(static_cast<std::uintptr_t>(0xDEADBEEF)));
-  }
-  {
-    typedef IncompleteHolder* VT;
-    typedef A<VT> Alloc;
-    Alloc a;
-    assert(std::allocator_traits<Alloc >::allocate(a, 10, nullptr) == reinterpret_cast<VT*>(static_cast<std::uintptr_t>(0xDEADBEEF)));
-  }
+    {
+        A<int> a;
+        assert(std::allocator_traits<A<int> >::allocate(a, 10, nullptr) == &a.storage);
+    }
+    {
+        typedef A<IncompleteHolder*> Alloc;
+        Alloc a;
+        assert(std::allocator_traits<Alloc>::allocate(a, 10, nullptr) == &a.storage);
+    }
 #endif
-  {
-    B<int> b;
-    assert(std::allocator_traits<B<int> >::allocate(b, 11, nullptr) == reinterpret_cast<int*>(static_cast<std::uintptr_t>(0xFEADBEEF)));
-  }
-  {
-    typedef IncompleteHolder* VT;
-    typedef B<VT> Alloc;
-    Alloc b;
-    assert(std::allocator_traits<Alloc >::allocate(b, 11, nullptr) == reinterpret_cast<VT*>(static_cast<std::uintptr_t>(0xFEADBEEF)));
-  }
+    {
+        B<int> b;
+        assert(std::allocator_traits<B<int> >::allocate(b, 11, nullptr) == &b.storage);
+    }
+    {
+        typedef B<IncompleteHolder*> Alloc;
+        Alloc b;
+        assert(std::allocator_traits<Alloc>::allocate(b, 11, nullptr) == &b.storage);
+    }
+
+    return true;
+}
+
 
-  return 0;
+int main(int, char**)
+{
+    test();
+#if TEST_STD_VER > 17
+    static_assert(test());
+#endif
+    return 0;
 }

diff  --git a/libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/construct.pass.cpp b/libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/construct.pass.cpp
index 67a2e8f4ac8b..10e8879012fc 100644
--- a/libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/construct.pass.cpp
+++ b/libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/construct.pass.cpp
@@ -12,7 +12,7 @@
 // struct allocator_traits
 // {
 //     template <class Ptr, class... Args>
-//         static void construct(allocator_type& a, Ptr p, Args&&... args);
+//     static constexpr void construct(allocator_type& a, Ptr p, Args&&... args);
 //     ...
 // };
 
@@ -31,124 +31,147 @@ struct A
 
 };
 
-int b_construct = 0;
-
 template <class T>
 struct B
 {
     typedef T value_type;
 
+    TEST_CONSTEXPR_CXX20 B(int& count) : count(count) {}
+
 #if TEST_STD_VER >= 11
     template <class U, class ...Args>
-    void construct(U* p, Args&& ...args)
+    TEST_CONSTEXPR_CXX20 void construct(U* p, Args&& ...args)
     {
-        ++b_construct;
+        ++count;
+#if TEST_STD_VER > 17
+        std::construct_at(p, std::forward<Args>(args)...);
+#else
         ::new ((void*)p) U(std::forward<Args>(args)...);
+#endif
     }
 #endif
+
+    int& count;
 };
 
 struct A0
 {
-    static int count;
-    A0() {++count;}
+    TEST_CONSTEXPR_CXX20 A0(int* count) {++*count;}
 };
 
-int A0::count = 0;
-
 struct A1
 {
-    static int count;
-    A1(char c)
+    TEST_CONSTEXPR_CXX20 A1(int* count, char c)
     {
         assert(c == 'c');
-        ++count;
+        ++*count;
     }
 };
 
-int A1::count = 0;
-
 struct A2
 {
-    static int count;
-    A2(char c, int i)
+    TEST_CONSTEXPR_CXX20 A2(int* count, char c, int i)
     {
         assert(c == 'd');
         assert(i == 5);
-        ++count;
+        ++*count;
     }
 };
 
-int A2::count = 0;
-
-int main(int, char**)
+TEST_CONSTEXPR_CXX20 bool test()
 {
     {
-        A0::count = 0;
-        A<int> a;
-        std::aligned_storage<sizeof(A0)>::type a0;
-        assert(A0::count == 0);
-        std::allocator_traits<A<int> >::construct(a, (A0*)&a0);
-        assert(A0::count == 1);
+        int A0_count = 0;
+        A<A0> a;
+        std::allocator<A0> alloc;
+        A0* a0 = alloc.allocate(1);
+        assert(A0_count == 0);
+        std::allocator_traits<A<A0> >::construct(a, a0, &A0_count);
+        assert(A0_count == 1);
+        alloc.deallocate(a0, 1);
     }
     {
-        A1::count = 0;
-        A<int> a;
-        std::aligned_storage<sizeof(A1)>::type a1;
-        assert(A1::count == 0);
-        std::allocator_traits<A<int> >::construct(a, (A1*)&a1, 'c');
-        assert(A1::count == 1);
+        int A1_count = 0;
+        A<A1> a;
+        std::allocator<A1> alloc;
+        A1* a1 = alloc.allocate(1);
+        assert(A1_count == 0);
+        std::allocator_traits<A<A1> >::construct(a, a1, &A1_count, 'c');
+        assert(A1_count == 1);
+        alloc.deallocate(a1, 1);
     }
     {
-        A2::count = 0;
-        A<int> a;
-        std::aligned_storage<sizeof(A2)>::type a2;
-        assert(A2::count == 0);
-        std::allocator_traits<A<int> >::construct(a, (A2*)&a2, 'd', 5);
-        assert(A2::count == 1);
+        int A2_count = 0;
+        A<A2> a;
+        std::allocator<A2> alloc;
+        A2* a2 = alloc.allocate(1);
+        assert(A2_count == 0);
+        std::allocator_traits<A<A2> >::construct(a, a2, &A2_count, 'd', 5);
+        assert(A2_count == 1);
+        alloc.deallocate(a2, 1);
     }
     {
       typedef IncompleteHolder* VT;
       typedef A<VT> Alloc;
       Alloc a;
-      std::aligned_storage<sizeof(VT)>::type store;
-      std::allocator_traits<Alloc>::construct(a, (VT*)&store, nullptr);
+      std::allocator<VT> alloc;
+      VT* vt = alloc.allocate(1);
+      std::allocator_traits<Alloc>::construct(a, vt, nullptr);
+      alloc.deallocate(vt, 1);
     }
+
 #if TEST_STD_VER >= 11
     {
-        A0::count = 0;
-        b_construct = 0;
-        B<int> b;
-        std::aligned_storage<sizeof(A0)>::type a0;
-        assert(A0::count == 0);
+        int A0_count = 0;
+        int b_construct = 0;
+        B<A0> b(b_construct);
+        std::allocator<A0> alloc;
+        A0* a0 = alloc.allocate(1);
+        assert(A0_count == 0);
         assert(b_construct == 0);
-        std::allocator_traits<B<int> >::construct(b, (A0*)&a0);
-        assert(A0::count == 1);
+        std::allocator_traits<B<A0> >::construct(b, a0, &A0_count);
+        assert(A0_count == 1);
         assert(b_construct == 1);
+        alloc.deallocate(a0, 1);
     }
     {
-        A1::count = 0;
-        b_construct = 0;
-        B<int> b;
-        std::aligned_storage<sizeof(A1)>::type a1;
-        assert(A1::count == 0);
+        int A1_count = 0;
+        int b_construct = 0;
+        B<A1> b(b_construct);
+        std::allocator<A1> alloc;
+        A1* a1 = alloc.allocate(1);
+        assert(A1_count == 0);
         assert(b_construct == 0);
-        std::allocator_traits<B<int> >::construct(b, (A1*)&a1, 'c');
-        assert(A1::count == 1);
+        std::allocator_traits<B<A1> >::construct(b, a1, &A1_count, 'c');
+        assert(A1_count == 1);
         assert(b_construct == 1);
+        alloc.deallocate(a1, 1);
     }
     {
-        A2::count = 0;
-        b_construct = 0;
-        B<int> b;
-        std::aligned_storage<sizeof(A2)>::type a2;
-        assert(A2::count == 0);
+        int A2_count = 0;
+        int b_construct = 0;
+        B<A2> b(b_construct);
+        std::allocator<A2> alloc;
+        A2* a2 = alloc.allocate(1);
+        assert(A2_count == 0);
         assert(b_construct == 0);
-        std::allocator_traits<B<int> >::construct(b, (A2*)&a2, 'd', 5);
-        assert(A2::count == 1);
+        std::allocator_traits<B<A2> >::construct(b, a2, &A2_count, 'd', 5);
+        assert(A2_count == 1);
         assert(b_construct == 1);
+        alloc.deallocate(a2, 1);
     }
 #endif
 
-  return 0;
+    return true;
+}
+
+int main(int, char**)
+{
+    test();
+
+#if TEST_STD_VER > 17
+    static_assert(test());
+#endif
+
+    return 0;
 }

diff  --git a/libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/deallocate.pass.cpp b/libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/deallocate.pass.cpp
index cc2abda6da0a..fd066b97b0f5 100644
--- a/libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/deallocate.pass.cpp
+++ b/libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/deallocate.pass.cpp
@@ -11,47 +11,60 @@
 // template <class Alloc>
 // struct allocator_traits
 // {
-//     static void deallocate(allocator_type& a, pointer p, size_type n);
+//     static constexpr void deallocate(allocator_type& a, pointer p, size_type n);
 //     ...
 // };
 
 #include <memory>
-#include <cstdint>
 #include <cassert>
+#include <cstddef>
 
 #include "test_macros.h"
 #include "incomplete_type_helper.h"
 
-int called = 0;
-
 template <class T>
 struct A
 {
     typedef T value_type;
 
-    void deallocate(value_type* p, std::size_t n)
+    TEST_CONSTEXPR_CXX20 A(int& called) : called(called) {}
+
+    TEST_CONSTEXPR_CXX20 void deallocate(value_type* p, std::size_t n)
     {
-        assert(p == reinterpret_cast<value_type*>(static_cast<std::uintptr_t>(0xDEADBEEF)));
+        assert(p == &storage);
         assert(n == 10);
         ++called;
     }
+
+    int& called;
+
+    value_type storage;
 };
 
+TEST_CONSTEXPR_CXX20 bool test()
+{
+    {
+        int called = 0;
+        A<int> a(called);
+        std::allocator_traits<A<int> >::deallocate(a, &a.storage, 10);
+        assert(called == 1);
+    }
+    {
+        int called = 0;
+        typedef A<IncompleteHolder*> Alloc;
+        Alloc a(called);
+        std::allocator_traits<Alloc>::deallocate(a, &a.storage, 10);
+        assert(called == 1);
+    }
+
+    return true;
+}
+
 int main(int, char**)
 {
-  {
-    A<int> a;
-    std::allocator_traits<A<int> >::deallocate(a, reinterpret_cast<int*>(static_cast<std::uintptr_t>(0xDEADBEEF)), 10);
-    assert(called == 1);
-  }
-  called = 0;
-  {
-    typedef IncompleteHolder* VT;
-    typedef A<VT> Alloc;
-    Alloc a;
-    std::allocator_traits<Alloc >::deallocate(a, reinterpret_cast<VT*>(static_cast<std::uintptr_t>(0xDEADBEEF)), 10);
-    assert(called == 1);
-  }
-
-  return 0;
+    test();
+#if TEST_STD_VER > 17
+    static_assert(test());
+#endif
+    return 0;
 }

diff  --git a/libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/destroy.pass.cpp b/libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/destroy.pass.cpp
index 70890d8512fd..047b7c1836df 100644
--- a/libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/destroy.pass.cpp
+++ b/libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/destroy.pass.cpp
@@ -12,80 +12,123 @@
 // struct allocator_traits
 // {
 //     template <class Ptr>
-//         static void destroy(allocator_type& a, Ptr p);
+//     static constexpr void destroy(allocator_type& a, Ptr p);
 //     ...
 // };
 
 #include <memory>
-#include <new>
-#include <type_traits>
 #include <cassert>
+#include <cstddef>
 
 #include "test_macros.h"
 #include "incomplete_type_helper.h"
 
 template <class T>
-struct A
+struct NoDestroy
 {
     typedef T value_type;
 
-};
+    TEST_CONSTEXPR_CXX20 T* allocate(std::size_t n)
+    {
+        return std::allocator<T>().allocate(n);
+    }
 
-int b_destroy = 0;
+    TEST_CONSTEXPR_CXX20 void deallocate(T* p, std::size_t n)
+    {
+        return std::allocator<T>().deallocate(p, n);
+    }
+};
 
 template <class T>
-struct B
+struct CountDestroy
 {
+    TEST_CONSTEXPR explicit CountDestroy(int* counter)
+        : counter_(counter)
+    { }
+
     typedef T value_type;
 
+    TEST_CONSTEXPR_CXX20 T* allocate(std::size_t n)
+    {
+        return std::allocator<T>().allocate(n);
+    }
+
+    TEST_CONSTEXPR_CXX20 void deallocate(T* p, std::size_t n)
+    {
+        return std::allocator<T>().deallocate(p, n);
+    }
+
     template <class U>
-    void destroy(U* p)
+    TEST_CONSTEXPR_CXX20 void destroy(U* p)
     {
-        ++b_destroy;
+        ++*counter_;
         p->~U();
     }
+
+    int* counter_;
 };
 
-struct A0
+struct CountDestructor
 {
-    static int count;
-    ~A0() {++count;}
-};
+    TEST_CONSTEXPR explicit CountDestructor(int* counter)
+        : counter_(counter)
+    { }
 
-int A0::count = 0;
+    TEST_CONSTEXPR_CXX20 ~CountDestructor() { ++*counter_; }
 
-int main(int, char**)
+    int* counter_;
+};
+
+TEST_CONSTEXPR_CXX20 bool test()
 {
     {
-        A0::count = 0;
-        A<int> a;
-        std::aligned_storage<sizeof(A0)>::type a0;
-        std::allocator_traits<A<int> >::construct(a, (A0*)&a0);
-        assert(A0::count == 0);
-        std::allocator_traits<A<int> >::destroy(a, (A0*)&a0);
-        assert(A0::count == 1);
+        typedef NoDestroy<CountDestructor> Alloc;
+        int destructors = 0;
+        Alloc alloc;
+        CountDestructor* pool = std::allocator_traits<Alloc>::allocate(alloc, 1);
+
+        std::allocator_traits<Alloc>::construct(alloc, pool, &destructors);
+        assert(destructors == 0);
+
+        std::allocator_traits<Alloc>::destroy(alloc, pool);
+        assert(destructors == 1);
+
+        std::allocator_traits<Alloc>::deallocate(alloc, pool, 1);
     }
     {
-      typedef IncompleteHolder* VT;
-      typedef A<VT> Alloc;
-      Alloc a;
-      std::aligned_storage<sizeof(VT)>::type store;
-      std::allocator_traits<Alloc>::destroy(a, (VT*)&store);
+        typedef IncompleteHolder* T;
+        typedef NoDestroy<T> Alloc;
+        Alloc alloc;
+        T* pool = std::allocator_traits<Alloc>::allocate(alloc, 1);
+        std::allocator_traits<Alloc>::construct(alloc, pool, nullptr);
+        std::allocator_traits<Alloc>::destroy(alloc, pool);
+        std::allocator_traits<Alloc>::deallocate(alloc, pool, 1);
     }
-#if defined(_LIBCPP_VERSION) || TEST_STD_VER >= 11
     {
-        A0::count = 0;
-        b_destroy = 0;
-        B<int> b;
-        std::aligned_storage<sizeof(A0)>::type a0;
-        std::allocator_traits<B<int> >::construct(b, (A0*)&a0);
-        assert(A0::count == 0);
-        assert(b_destroy == 0);
-        std::allocator_traits<B<int> >::destroy(b, (A0*)&a0);
-        assert(A0::count == 1);
-        assert(b_destroy == 1);
+        typedef CountDestroy<CountDestructor> Alloc;
+        int destroys_called = 0;
+        int destructors_called = 0;
+        Alloc alloc(&destroys_called);
+
+        CountDestructor* pool = std::allocator_traits<Alloc>::allocate(alloc, 1);
+        std::allocator_traits<Alloc>::construct(alloc, pool, &destructors_called);
+        assert(destroys_called == 0);
+        assert(destructors_called == 0);
+
+        std::allocator_traits<Alloc>::destroy(alloc, pool);
+        assert(destroys_called == 1);
+        assert(destructors_called == 1);
+
+        std::allocator_traits<Alloc>::deallocate(alloc, pool, 1);
     }
-#endif
+    return true;
+}
 
-  return 0;
+int main(int, char**)
+{
+    test();
+#if TEST_STD_VER > 17
+    static_assert(test());
+#endif
+    return 0;
 }

diff  --git a/libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/max_size.pass.cpp b/libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/max_size.pass.cpp
index b758c9add8c0..301078e51549 100644
--- a/libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/max_size.pass.cpp
+++ b/libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/max_size.pass.cpp
@@ -11,7 +11,7 @@
 // template <class Alloc>
 // struct allocator_traits
 // {
-//     static size_type max_size(const allocator_type& a) noexcept;
+//     static constexpr size_type max_size(const allocator_type& a) noexcept;
 //     ...
 // };
 
@@ -36,13 +36,13 @@ struct B
 {
     typedef T value_type;
 
-    size_t max_size() const
+    TEST_CONSTEXPR_CXX20 size_t max_size() const
     {
         return 100;
     }
 };
 
-int main(int, char**)
+TEST_CONSTEXPR_CXX20 bool test()
 {
     {
         B<int> b;
@@ -75,5 +75,16 @@ int main(int, char**)
     }
 #endif
 
-  return 0;
+    return true;
+}
+
+int main(int, char**)
+{
+    test();
+
+#if TEST_STD_VER > 17
+    static_assert(test());
+#endif
+
+    return 0;
 }

diff  --git a/libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/select_on_container_copy_construction.pass.cpp b/libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/select_on_container_copy_construction.pass.cpp
index be837670dd3f..231f173f5deb 100644
--- a/libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/select_on_container_copy_construction.pass.cpp
+++ b/libcxx/test/std/utilities/memory/allocator.traits/allocator.traits.members/select_on_container_copy_construction.pass.cpp
@@ -11,7 +11,7 @@
 // template <class Alloc>
 // struct allocator_traits
 // {
-//     static allocator_type
+//     static constexpr allocator_type
 //         select_on_container_copy_construction(const allocator_type& a);
 //     ...
 // };
@@ -29,7 +29,7 @@ struct A
 {
     typedef T value_type;
     int id;
-    explicit A(int i = 0) : id(i) {}
+    TEST_CONSTEXPR_CXX20 explicit A(int i = 0) : id(i) {}
 
 };
 
@@ -39,15 +39,15 @@ struct B
     typedef T value_type;
 
     int id;
-    explicit B(int i = 0) : id(i) {}
+    TEST_CONSTEXPR_CXX20 explicit B(int i = 0) : id(i) {}
 
-    B select_on_container_copy_construction() const
+    TEST_CONSTEXPR_CXX20 B select_on_container_copy_construction() const
     {
         return B(100);
     }
 };
 
-int main(int, char**)
+TEST_CONSTEXPR_CXX20 bool test()
 {
     {
         A<int> a;
@@ -74,5 +74,14 @@ int main(int, char**)
     }
 #endif
 
-  return 0;
+    return true;
+}
+
+int main(int, char**)
+{
+    test();
+#if TEST_STD_VER > 17
+    static_assert(test());
+#endif
+    return 0;
 }

diff  --git a/libcxx/test/std/utilities/memory/default.allocator/allocator.dtor.pass.cpp b/libcxx/test/std/utilities/memory/default.allocator/allocator.dtor.pass.cpp
new file mode 100644
index 000000000000..d97abb160740
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/default.allocator/allocator.dtor.pass.cpp
@@ -0,0 +1,35 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// template <class T>
+// constexpr allocator<T>::~allocator();
+
+#include <memory>
+
+
+template <typename T>
+constexpr bool test() {
+    std::allocator<T> alloc;
+    (void)alloc;
+
+    // destructor called here
+    return true;
+}
+
+int main(int, char**)
+{
+    test<int>();
+    test<int const>();
+
+    static_assert(test<int>());
+    static_assert(test<int const>());
+
+    return 0;
+}

diff  --git a/libcxx/test/std/utilities/memory/default.allocator/allocator.globals/eq.pass.cpp b/libcxx/test/std/utilities/memory/default.allocator/allocator.globals/eq.pass.cpp
index 30f847a4cad5..0f568d6cf18b 100644
--- a/libcxx/test/std/utilities/memory/default.allocator/allocator.globals/eq.pass.cpp
+++ b/libcxx/test/std/utilities/memory/default.allocator/allocator.globals/eq.pass.cpp
@@ -11,11 +11,11 @@
 // allocator:
 
 // template <class T1, class T2>
-//   bool
+//   constexpr bool
 //   operator==(const allocator<T1>&, const allocator<T2>&) throw();
 //
 // template <class T1, class T2>
-//   bool
+//   constexpr bool
 //   operator!=(const allocator<T1>&, const allocator<T2>&) throw();
 
 #include <memory>
@@ -23,12 +23,23 @@
 
 #include "test_macros.h"
 
-int main(int, char**)
+TEST_CONSTEXPR_CXX20 bool test()
 {
     std::allocator<int> a1;
     std::allocator<int> a2;
     assert(a1 == a2);
     assert(!(a1 != a2));
 
-  return 0;
+    return true;
+}
+
+int main(int, char**)
+{
+    test();
+
+#if TEST_STD_VER > 17
+    static_assert(test());
+#endif
+
+    return 0;
 }

diff  --git a/libcxx/test/std/utilities/memory/default.allocator/allocator.members/allocate.constexpr.size.verify.cpp b/libcxx/test/std/utilities/memory/default.allocator/allocator.members/allocate.constexpr.size.verify.cpp
new file mode 100644
index 000000000000..f12b6808edeb
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/default.allocator/allocator.members/allocate.constexpr.size.verify.cpp
@@ -0,0 +1,40 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <memory>
+
+// allocator:
+// constexpr T* allocate(size_type n);
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <memory>
+#include <cassert>
+
+#include "test_macros.h"
+
+template <typename T>
+constexpr bool test()
+{
+    typedef std::allocator<T> A;
+    typedef std::allocator_traits<A> AT;
+    A a;
+    TEST_IGNORE_NODISCARD a.allocate(AT::max_size(a) + 1);           // just barely too large
+    TEST_IGNORE_NODISCARD a.allocate(AT::max_size(a) * 2);           // significantly too large
+    TEST_IGNORE_NODISCARD a.allocate(((size_t) -1) / sizeof(T) + 1); // multiply will overflow
+    TEST_IGNORE_NODISCARD a.allocate((size_t) -1);                   // way too large
+
+    return true;
+}
+
+int main(int, char**)
+{
+    static_assert(test<double>()); // expected-error {{static_assert expression is not an integral constant expression}}
+    LIBCPP_STATIC_ASSERT(test<const double>()); // expected-error {{static_assert expression is not an integral constant expression}}
+    return 0;
+}

diff  --git a/libcxx/test/std/utilities/memory/default.allocator/allocator.members/allocate.pass.cpp b/libcxx/test/std/utilities/memory/default.allocator/allocator.members/allocate.pass.cpp
index ff5287dc81a7..49c7a0b3aad8 100644
--- a/libcxx/test/std/utilities/memory/default.allocator/allocator.members/allocate.pass.cpp
+++ b/libcxx/test/std/utilities/memory/default.allocator/allocator.members/allocate.pass.cpp
@@ -9,7 +9,7 @@
 // <memory>
 
 // allocator:
-// T* allocate(size_t n);
+// constexpr T* allocate(size_t n);
 
 #include <memory>
 #include <cassert>
@@ -77,6 +77,18 @@ void test_aligned() {
   }
 }
 
+#if TEST_STD_VER > 17
+template <size_t Align>
+constexpr bool test_aligned_constexpr() {
+    typedef AlignedType<Align> T;
+    std::allocator<T> a;
+    T* ap = a.allocate(3);
+    a.deallocate(ap, 3);
+
+    return true;
+}
+#endif
+
 int main(int, char**) {
     test_aligned<1>();
     test_aligned<2>();
@@ -87,5 +99,16 @@ int main(int, char**) {
     test_aligned<OverAligned>();
     test_aligned<OverAligned * 2>();
 
+#if TEST_STD_VER > 17
+    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>());
+#endif
+
   return 0;
 }

diff  --git a/libcxx/test/std/utilities/memory/default.allocator/allocator.members/allocate.size.pass.cpp b/libcxx/test/std/utilities/memory/default.allocator/allocator.members/allocate.size.pass.cpp
index 48419174b661..42013de1ccdf 100644
--- a/libcxx/test/std/utilities/memory/default.allocator/allocator.members/allocate.size.pass.cpp
+++ b/libcxx/test/std/utilities/memory/default.allocator/allocator.members/allocate.size.pass.cpp
@@ -10,7 +10,7 @@
 // <memory>
 
 // allocator:
-// T* allocate(size_t n);
+// constexpr T* allocate(size_t n);
 
 #include <memory>
 #include <cassert>

diff  --git a/libcxx/test/std/utilities/memory/default.allocator/allocator_types.pass.cpp b/libcxx/test/std/utilities/memory/default.allocator/allocator_types.pass.cpp
index 2265ed3d66fa..95ac415e3704 100644
--- a/libcxx/test/std/utilities/memory/default.allocator/allocator_types.pass.cpp
+++ b/libcxx/test/std/utilities/memory/default.allocator/allocator_types.pass.cpp
@@ -30,7 +30,7 @@
 #include "test_macros.h"
 
 template <typename T, typename U>
-void check()
+TEST_CONSTEXPR_CXX20 bool test()
 {
     static_assert((std::is_same<typename std::allocator<T>::size_type, std::size_t>::value), "");
     static_assert((std::is_same<typename std::allocator<T>::
diff erence_type, std::ptr
diff _t>::value), "");
@@ -43,11 +43,17 @@ void check()
     a2 = a;
     std::allocator<U> a3 = a2;
     (void)a3;
+
+    return true;
 }
 
 int main(int, char**)
 {
-    check<char, int>();
-    check<char const, int const>();
+    test<char, int>();
+    test<char const, int const>();
+#if TEST_STD_VER > 17
+    static_assert(test<char, int>());
+    static_assert(test<char const, int const>());
+#endif
     return 0;
 }

diff  --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.construct/construct_at.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.construct/construct_at.pass.cpp
new file mode 100644
index 000000000000..ac0c8a138115
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.construct/construct_at.pass.cpp
@@ -0,0 +1,105 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <memory>
+
+// template <class T, class ...Args>
+// constexpr T* construct_at(T* location, Args&& ...args);
+
+#include <memory>
+#include <cassert>
+
+
+struct Foo {
+    int a;
+    char b;
+    double c;
+    constexpr Foo() { }
+    constexpr Foo(int a, char b, double c) : a(a), b(b), c(c) { }
+    constexpr Foo(int a, char b, double c, int* count) : Foo(a, b, c) { *count += 1; }
+    constexpr bool operator==(Foo const& other) const {
+        return a == other.a && b == other.b && c == other.c;
+    }
+};
+
+struct Counted {
+    int& count_;
+    constexpr Counted(int& count) : count_(count) { ++count; }
+    constexpr Counted(Counted const& that) : count_(that.count_) { ++count_; }
+    constexpr ~Counted() { --count_; }
+};
+
+constexpr bool test()
+{
+    {
+        int ints[1] = {0};
+        int* res = std::construct_at(&ints[0], 42);
+        assert(res == &ints[0]);
+        assert(*res == 42);
+    }
+
+    {
+        Foo foos[1] = {};
+        int count = 0;
+        Foo* res = std::construct_at(&foos[0], 42, 'x', 123.89, &count);
+        assert(res == &foos[0]);
+        assert(*res == Foo(42, 'x', 123.89));
+        assert(count == 1);
+    }
+
+    {
+        std::allocator<Counted> a;
+        Counted* p = a.allocate(2);
+        int count = 0;
+        std::construct_at(p, count);
+        assert(count == 1);
+        std::construct_at(p+1, count);
+        assert(count == 2);
+        (p+1)->~Counted();
+        assert(count == 1);
+        p->~Counted();
+        assert(count == 0);
+        a.deallocate(p, 2);
+    }
+
+    {
+        std::allocator<Counted const> a;
+        Counted const* p = a.allocate(2);
+        int count = 0;
+        std::construct_at(p, count);
+        assert(count == 1);
+        std::construct_at(p+1, count);
+        assert(count == 2);
+        (p+1)->~Counted();
+        assert(count == 1);
+        p->~Counted();
+        assert(count == 0);
+        a.deallocate(p, 2);
+    }
+
+    return true;
+}
+
+// Make sure std::construct_at SFINAEs out based on the validity of calling
+// the constructor, instead of hard-erroring.
+template <typename T, typename = decltype(
+    std::construct_at((T*)nullptr, 1, 2) // missing arguments for Foo(...)
+)>
+constexpr bool test_sfinae(int) { return false; }
+template <typename T>
+constexpr bool test_sfinae(...) { return true; }
+static_assert(test_sfinae<Foo>(int()));
+
+int main(int, char**)
+{
+    test();
+    static_assert(test());
+    return 0;
+}

diff  --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy.pass.cpp
index 14651dd9c7ee..be23a43d884e 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy.pass.cpp
@@ -11,38 +11,50 @@
 // <memory>
 
 // template <class ForwardIt>
-// void destroy(ForwardIt, ForwardIt);
+// constexpr void destroy(ForwardIt, ForwardIt);
 
 #include <memory>
-#include <cstdlib>
 #include <cassert>
 
 #include "test_macros.h"
 #include "test_iterators.h"
 
 struct Counted {
-  static int count;
-  static void reset() { count = 0; }
-  Counted() { ++count; }
-  Counted(Counted const&) { ++count; }
-  ~Counted() { --count; }
-  friend void operator&(Counted) = delete;
+    int* counter_;
+    TEST_CONSTEXPR Counted(int* counter) : counter_(counter) { ++*counter_; }
+    TEST_CONSTEXPR Counted(Counted const& other) : counter_(other.counter_) { ++*counter_; }
+    TEST_CONSTEXPR_CXX20 ~Counted() { --*counter_; }
+    friend void operator&(Counted) = delete;
 };
-int Counted::count = 0;
+
+TEST_CONSTEXPR_CXX20 bool test()
+{
+    using Alloc = std::allocator<Counted>;
+    int counter = 0;
+    int const N = 5;
+    Alloc alloc;
+    Counted* pool = std::allocator_traits<Alloc>::allocate(alloc, N);
+
+    for (Counted* p = pool; p != pool + N; ++p)
+        std::allocator_traits<Alloc>::construct(alloc, p, &counter);
+    assert(counter == 5);
+
+    std::destroy(pool, pool + 1);
+    assert(counter == 4);
+
+    std::destroy(forward_iterator<Counted*>(pool + 1), forward_iterator<Counted*>(pool + 5));
+    assert(counter == 0);
+
+    std::allocator_traits<Alloc>::deallocate(alloc, pool, N);
+
+    return true;
+}
 
 int main(int, char**)
 {
-    using It = forward_iterator<Counted*>;
-    const int N = 5;
-    alignas(Counted) char pool[sizeof(Counted)*N] = {};
-    Counted* p = (Counted*)pool;
-    std::uninitialized_fill(p, p+N, Counted());
-    assert(Counted::count == 5);
-    std::destroy(p, p+1);
-    p += 1;
-    assert(Counted::count == 4);
-    std::destroy(It(p), It(p + 4));
-    assert(Counted::count == 0);
-
-  return 0;
+    test();
+#if TEST_STD_VER > 17
+    static_assert(test());
+#endif
+    return 0;
 }

diff  --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_at.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_at.pass.cpp
index 4c6da6dedc74..d3d1bac1376e 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_at.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_at.pass.cpp
@@ -10,72 +10,84 @@
 
 // <memory>
 
-// template <class _Tp>
-// void destroy_at(_Tp*);
+// template <class T>
+// constexpr void destroy_at(T*);
 
 #include <memory>
-#include <cstdlib>
 #include <cassert>
 
 #include "test_macros.h"
 
 struct Counted {
-  static int count;
-  static void reset() { count = 0; }
-  Counted() { ++count; }
-  Counted(Counted const&) { ++count; }
-  ~Counted() { --count; }
-  friend void operator&(Counted) = delete;
+    int* counter_;
+    TEST_CONSTEXPR Counted(int* counter) : counter_(counter) { ++*counter_; }
+    TEST_CONSTEXPR_CXX20 ~Counted() { --*counter_; }
+    friend void operator&(Counted) = delete;
 };
-int Counted::count = 0;
-
-struct VCounted {
-  static int count;
-  static void reset() { count = 0; }
-  VCounted() { ++count; }
-  VCounted(VCounted const&) { ++count; }
-  virtual ~VCounted() { --count; }
-  friend void operator&(VCounted) = delete;
+
+struct VirtualCounted {
+    int* counter_;
+    TEST_CONSTEXPR VirtualCounted(int* counter) : counter_(counter) { ++*counter_; }
+    TEST_CONSTEXPR_CXX20 virtual ~VirtualCounted() { --*counter_; }
+    friend void operator&(VirtualCounted) = delete;
 };
-int VCounted::count = 0;
 
-struct DCounted : VCounted {
-    friend void operator&(DCounted) = delete;
+struct DerivedCounted : VirtualCounted {
+    TEST_CONSTEXPR DerivedCounted(int* counter) : VirtualCounted(counter) { }
+    friend void operator&(DerivedCounted) = delete;
 };
 
-int main(int, char**)
+TEST_CONSTEXPR_CXX20 bool test()
 {
     {
-    void* mem1 = std::malloc(sizeof(Counted));
-    void* mem2 = std::malloc(sizeof(Counted));
-    assert(mem1 && mem2);
-    assert(Counted::count == 0);
-    Counted* ptr1 = ::new(mem1) Counted();
-    Counted* ptr2 = ::new(mem2) Counted();
-    assert(Counted::count == 2);
-    std::destroy_at(ptr1);
-    assert(Counted::count == 1);
-    std::destroy_at(ptr2);
-    assert(Counted::count == 0);
-    std::free(mem1);
-    std::free(mem2);
+        using Alloc = std::allocator<Counted>;
+        Alloc alloc;
+        Counted* ptr1 = std::allocator_traits<Alloc>::allocate(alloc, 1);
+        Counted* ptr2 = std::allocator_traits<Alloc>::allocate(alloc, 1);
+
+        int counter = 0;
+        std::allocator_traits<Alloc>::construct(alloc, ptr1, &counter);
+        std::allocator_traits<Alloc>::construct(alloc, ptr2, &counter);
+        assert(counter == 2);
+
+        std::destroy_at(ptr1);
+        assert(counter == 1);
+
+        std::destroy_at(ptr2);
+        assert(counter == 0);
+
+        std::allocator_traits<Alloc>::deallocate(alloc, ptr1, 1);
+        std::allocator_traits<Alloc>::deallocate(alloc, ptr2, 1);
     }
     {
-    void* mem1 = std::malloc(sizeof(DCounted));
-    void* mem2 = std::malloc(sizeof(DCounted));
-    assert(mem1 && mem2);
-    assert(DCounted::count == 0);
-    DCounted* ptr1 = ::new(mem1) DCounted();
-    DCounted* ptr2 = ::new(mem2) DCounted();
-    assert(DCounted::count == 2);
-    assert(VCounted::count == 2);
-    std::destroy_at(ptr1);
-    assert(VCounted::count == 1);
-    std::destroy_at(ptr2);
-    assert(VCounted::count == 0);
-    std::free(mem1);
-    std::free(mem2);
+        using Alloc = std::allocator<DerivedCounted>;
+        Alloc alloc;
+        DerivedCounted* ptr1 = std::allocator_traits<Alloc>::allocate(alloc, 1);
+        DerivedCounted* ptr2 = std::allocator_traits<Alloc>::allocate(alloc, 1);
+
+        int counter = 0;
+        std::allocator_traits<Alloc>::construct(alloc, ptr1, &counter);
+        std::allocator_traits<Alloc>::construct(alloc, ptr2, &counter);
+        assert(counter == 2);
+
+        std::destroy_at(ptr1);
+        assert(counter == 1);
+
+        std::destroy_at(ptr2);
+        assert(counter == 0);
+
+        std::allocator_traits<Alloc>::deallocate(alloc, ptr1, 1);
+        std::allocator_traits<Alloc>::deallocate(alloc, ptr2, 1);
     }
 
-  return 0;
+    return true;
+}
+
+int main(int, char**)
+{
+    test();
+#if TEST_STD_VER > 17
+    static_assert(test());
+#endif
+    return 0;
 }

diff  --git a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_n.pass.cpp b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_n.pass.cpp
index 4f60367fe735..60f17331f269 100644
--- a/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_n.pass.cpp
+++ b/libcxx/test/std/utilities/memory/specialized.algorithms/specialized.destroy/destroy_n.pass.cpp
@@ -11,40 +11,52 @@
 // <memory>
 
 // template <class ForwardIt, class Size>
-// ForwardIt destroy_n(ForwardIt, Size s);
+// constexpr ForwardIt destroy_n(ForwardIt, Size s);
 
 #include <memory>
-#include <cstdlib>
 #include <cassert>
 
 #include "test_macros.h"
 #include "test_iterators.h"
 
 struct Counted {
-  static int count;
-  static void reset() { count = 0; }
-  Counted() { ++count; }
-  Counted(Counted const&) { ++count; }
-  ~Counted() { --count; }
-  friend void operator&(Counted) = delete;
+    int* counter_;
+    TEST_CONSTEXPR Counted(int* counter) : counter_(counter) { ++*counter_; }
+    TEST_CONSTEXPR Counted(Counted const& other) : counter_(other.counter_) { ++*counter_; }
+    TEST_CONSTEXPR_CXX20 ~Counted() { --*counter_; }
+    friend void operator&(Counted) = delete;
 };
-int Counted::count = 0;
+
+TEST_CONSTEXPR_CXX20 bool test()
+{
+    using Alloc = std::allocator<Counted>;
+    int counter = 0;
+    int const N = 5;
+    Alloc alloc;
+    Counted* pool = std::allocator_traits<Alloc>::allocate(alloc, N);
+
+    for (Counted* p = pool; p != pool + N; ++p)
+        std::allocator_traits<Alloc>::construct(alloc, p, &counter);
+    assert(counter == 5);
+
+    Counted* np = std::destroy_n(pool, 1);
+    assert(np == pool + 1);
+    assert(counter == 4);
+
+    forward_iterator<Counted*> it = std::destroy_n(forward_iterator<Counted*>(pool + 1), 4);
+    assert(it == forward_iterator<Counted*>(pool + 5));
+    assert(counter == 0);
+
+    std::allocator_traits<Alloc>::deallocate(alloc, pool, N);
+
+    return true;
+}
 
 int main(int, char**)
 {
-    using It = forward_iterator<Counted*>;
-    const int N = 5;
-    alignas(Counted) char pool[sizeof(Counted)*N] = {};
-    Counted* p = (Counted*)pool;
-    std::uninitialized_fill(p, p+N, Counted());
-    assert(Counted::count == 5);
-    Counted* np = std::destroy_n(p, 1);
-    assert(np == p+1);
-    assert(Counted::count == 4);
-    p += 1;
-    It it = std::destroy_n(It(p), 4);
-    assert(it == It(p+4));
-    assert(Counted::count == 0);
-
-  return 0;
+    test();
+#if TEST_STD_VER > 17
+    static_assert(test());
+#endif
+    return 0;
 }

diff  --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 2eecc0d5591c..1547443cffea 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -664,6 +664,12 @@ def add_version_header(tc):
    "depends": "!defined(_LIBCPP_HAS_NO_THREADS)",
    "internal_depends": "!defined(_LIBCPP_HAS_NO_THREADS)",
    },
+   {"name": "__cpp_lib_constexpr_dynamic_alloc",
+    "values": {
+      "c++2a": int(201907)
+    },
+    "headers": ["memory"]
+   },
 ]], key=lambda tc: tc["name"])
 
 def get_std_dialects():

diff  --git a/libcxx/www/cxx2a_status.html b/libcxx/www/cxx2a_status.html
index 88df02bcb117..d1bf4744b495 100644
--- a/libcxx/www/cxx2a_status.html
+++ b/libcxx/www/cxx2a_status.html
@@ -164,7 +164,7 @@ <h3>Paper Status</h3>
 	<tr><td><a href="https://wg21.link/P0631">P0631</a></td><td>LWG</td><td>Math Constants</td><td>Cologne</td><td>Complete</td><td>11.0</td></tr>
 	<tr><td><a href="https://wg21.link/P0645">P0645</a></td><td>LWG</td><td>Text Formatting</td><td>Cologne</td><td></td><td></td></tr>
 	<tr><td><a href="https://wg21.link/P0660">P0660</a></td><td>LWG</td><td>Stop Token and Joining Thread, Rev 10</td><td>Cologne</td><td></td><td></td></tr>
-	<tr><td><a href="https://wg21.link/P0784">P0784</a></td><td>CWG</td><td>More constexpr containers</td><td>Cologne</td><td></td><td></td></tr>
+	<tr><td><a href="https://wg21.link/P0784">P0784</a></td><td>CWG</td><td>More constexpr containers</td><td>Cologne</td><td>Complete</td><td>12.0</td></tr>
 	<tr><td><a href="https://wg21.link/P0980">P0980</a></td><td>LWG</td><td>Making std::string constexpr</td><td>Cologne</td><td></td><td></td></tr>
 	<tr><td><a href="https://wg21.link/P1004">P1004</a></td><td>LWG</td><td>Making std::vector constexpr</td><td>Cologne</td><td></td><td></td></tr>
 	<tr><td><a href="https://wg21.link/P1035">P1035</a></td><td>LWG</td><td>Input Range Adaptors</td><td>Cologne</td><td></td><td></td></tr>


        


More information about the libcxx-commits mailing list