[libcxx-commits] [libcxx] 79df8e1 - [libc++] Implement P0591R4 (Utility functions to implement uses-allocator construction)

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Thu Oct 6 07:59:00 PDT 2022


Author: Nikolas Klauser
Date: 2022-10-06T16:58:51+02:00
New Revision: 79df8e19beb9db218547bae5265139d0ff67343a

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

LOG: [libc++] Implement P0591R4 (Utility functions to implement uses-allocator construction)

Reviewed By: ldionne, #libc, huixie90

Spies: huixie90, libcxx-commits, mgorny

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

Added: 
    libcxx/include/__memory/uses_allocator_construction.h
    libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/common.h
    libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/make_obj_using_allocator.pass.cpp
    libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/uninitialized_construct_using_allocator.pass.cpp
    libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/uses_allocator_construction_args.pass.cpp

Modified: 
    libcxx/docs/ReleaseNotes.rst
    libcxx/docs/Status/Cxx20.rst
    libcxx/docs/Status/Cxx20Issues.csv
    libcxx/docs/Status/Cxx20Papers.csv
    libcxx/docs/Status/ZipProjects.csv
    libcxx/include/CMakeLists.txt
    libcxx/include/memory
    libcxx/include/module.modulemap.in
    libcxx/include/scoped_allocator
    libcxx/test/libcxx/private_headers.verify.cpp
    libcxx/test/libcxx/transitive_includes/cxx2b.csv
    libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct.pass.cpp
    libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair.pass.cpp
    libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair_const_lvalue_pair.pass.cpp
    libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair_piecewise.pass.cpp
    libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair_rvalue.pass.cpp
    libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair_values.pass.cpp
    libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct_type.pass.cpp
    libcxx/test/support/MoveOnly.h

Removed: 
    libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/tested_elsewhere.pass.cpp


################################################################################
diff  --git a/libcxx/docs/ReleaseNotes.rst b/libcxx/docs/ReleaseNotes.rst
index 7558fd2a9b7b6..8a49ec4443c33 100644
--- a/libcxx/docs/ReleaseNotes.rst
+++ b/libcxx/docs/ReleaseNotes.rst
@@ -41,6 +41,7 @@ Implemented Papers
 - P2417R2 - A more constexpr bitset
 - P2445R1 - ``std::forward_like``
 - P2273R3 - Making ``std::unique_ptr`` constexpr
+- P0591R4 - Utility functions to implement uses-allocator construction
 
 Improvements and New Features
 -----------------------------

diff  --git a/libcxx/docs/Status/Cxx20.rst b/libcxx/docs/Status/Cxx20.rst
index 90f2acc6a7bb8..2da9a2e903224 100644
--- a/libcxx/docs/Status/Cxx20.rst
+++ b/libcxx/docs/Status/Cxx20.rst
@@ -40,6 +40,7 @@ Paper Status
 
 .. note::
 
+   .. [#note-P0591] P0591: The changes in [mem.poly.allocator.mem] are missing.
    .. [#note-P0600] P0600: The missing bits in P0600 are in |sect|\ [mem.res.class] and |sect|\ [mem.poly.allocator.class].
    .. [#note-P0645] P0645: The paper is implemented but still marked as an incomplete feature
       (the feature-test macro is not set and the libary is only available when built with ``-fexperimental-library``).

diff  --git a/libcxx/docs/Status/Cxx20Issues.csv b/libcxx/docs/Status/Cxx20Issues.csv
index 4525a53999eda..bd93c95b8f2b1 100644
--- a/libcxx/docs/Status/Cxx20Issues.csv
+++ b/libcxx/docs/Status/Cxx20Issues.csv
@@ -149,7 +149,7 @@
 "`3169 <https://wg21.link/LWG3169>`__","``ranges``\  permutation generators discard useful information","Cologne","|Complete|","15.0","|ranges|"
 "`3183 <https://wg21.link/LWG3183>`__","Normative permission to specialize Ranges variable templates","Cologne","|Nothing To Do|","","|ranges|"
 "`3184 <https://wg21.link/LWG3184>`__","Inconsistencies in ``bind_front``\  wording","Cologne","|Complete|","13.0"
-"`3185 <https://wg21.link/LWG3185>`__","Uses-allocator construction functions missing ``constexpr``\  and ``noexcept``\ ","Cologne","",""
+"`3185 <https://wg21.link/LWG3185>`__","Uses-allocator construction functions missing ``constexpr``\  and ``noexcept``\ ","Cologne","|Complete|","16.0"
 "`3186 <https://wg21.link/LWG3186>`__","``ranges``\  removal, partition, and ``partial_sort_copy``\  algorithms discard useful information","Cologne","|Complete|","15.0","|ranges|"
 "`3187 <https://wg21.link/LWG3187>`__","`P0591R4 <https://wg21.link/p0591r4>`__ reverted DR 2586 fixes to ``scoped_allocator_adaptor::construct()``\ ","Cologne","",""
 "`3191 <https://wg21.link/LWG3191>`__","``std::ranges::shuffle``\  synopsis does not match algorithm definition","Cologne","|Complete|","15.0","|ranges|"
@@ -243,7 +243,7 @@
 "`3318 <https://wg21.link/LWG3318>`__","Clarify whether clocks can represent time before their epoch","Prague","","","|chrono|"
 "`3319 <https://wg21.link/LWG3319>`__","Properly reference specification of IANA time zone database","Prague","","","|chrono|"
 "`3320 <https://wg21.link/LWG3320>`__","``span::cbegin/cend``\  methods produce 
diff erent results than ``std::[ranges::]cbegin/cend``\ ","Prague","|Complete|",""
-"`3321 <https://wg21.link/LWG3321>`__","``uninitialized_construct_using_allocator``\  should use ``construct_at``\ ","Prague","",""
+"`3321 <https://wg21.link/LWG3321>`__","``uninitialized_construct_using_allocator``\  should use ``construct_at``\ ","Prague","|Complete|","16.0"
 "`3323 <https://wg21.link/LWG3323>`__","``*has-tuple-element*``\  helper concept needs ``convertible_to``\ ","Prague","","","|ranges|"
 "`3324 <https://wg21.link/LWG3324>`__","Special-case ``std::strong/weak/partial_order``\  for pointers","Prague","|Complete|","14.0","|spaceship|"
 "`3325 <https://wg21.link/LWG3325>`__","Constrain return type of transformation function for ``transform_view``\ ","Prague","|Complete|","15.0","|ranges|"

diff  --git a/libcxx/docs/Status/Cxx20Papers.csv b/libcxx/docs/Status/Cxx20Papers.csv
index ed1d0cea6f109..ab7a84ba3198b 100644
--- a/libcxx/docs/Status/Cxx20Papers.csv
+++ b/libcxx/docs/Status/Cxx20Papers.csv
@@ -55,8 +55,8 @@
 "`P0357R3 <https://wg21.link/P0357R3>`__","LWG","reference_wrapper for incomplete types","San Diego","|Complete|","8.0"
 "`P0482R6 <https://wg21.link/P0482R6>`__","CWG","char8_t: A type for UTF-8 characters and strings","San Diego","|Partial| [#note-P0482]_","16.0"
 "`P0487R1 <https://wg21.link/P0487R1>`__","LWG","Fixing ``operator>>(basic_istream&, CharT*)``\  (LWG 2499)","San Diego","|Complete|","8.0"
-"`P0591R4 <https://wg21.link/P0591R4>`__","LWG","Utility functions to implement uses-allocator construction","San Diego","* *",""
-"`P0595R2 <https://wg21.link/P0595R2>`__","CWG","P0595R2 std::is_constant_evaluated()","San Diego","|Complete|","9.0"
+"`P0591R4 <https://wg21.link/P0591R4>`__","LWG","Utility functions to implement uses-allocator construction","San Diego","|Partial| [#note-P0591]_",""
+"`P0595R2 <https://wg21.link/P0595R2>`__","CWG","std::is_constant_evaluated()","San Diego","|Complete|","9.0"
 "`P0602R4 <https://wg21.link/P0602R4>`__","LWG","variant and optional should propagate copy/move triviality","San Diego","|Complete|","8.0"
 "`P0608R3 <https://wg21.link/P0608R3>`__","LWG","A sane variant converting constructor","San Diego","|Complete|","9.0"
 "`P0655R1 <https://wg21.link/P0655R1>`__","LWG","visit<R>: Explicit Return Type for visit","San Diego","|Complete|","12.0"

diff  --git a/libcxx/docs/Status/ZipProjects.csv b/libcxx/docs/Status/ZipProjects.csv
index 17c1178f167fb..699a382ff66b7 100644
--- a/libcxx/docs/Status/ZipProjects.csv
+++ b/libcxx/docs/Status/ZipProjects.csv
@@ -4,7 +4,7 @@ Section,Description,Dependencies,Assignee,Complete
 | `[utility.syn] <https://wg21.link/utility.syn>`_, "[pair] basic_common_reference, common_type", None, Nikolas Klauser, |Complete|
 | `[pairs.pair] <https://wg21.link/pairs.pair>`_, "`[pair] constructor, assignment and swap overloads <https://reviews.llvm.org/D131495>`_", None, Hui Xie, |Complete|
 "| `[memory.syn] <https://wg21.link/memory.syn>`_
-| `[allocator.uses.construction] <https://wg21.link/allocator.uses.construction>`_", "[pair] uses_allocator_construction_args overloads", None, Unassigned, |Not Started|
+| `[allocator.uses.construction] <https://wg21.link/allocator.uses.construction>`_", "[pair] uses_allocator_construction_args overloads", None, Nikolas Klauser, |Complete|
 | `[vector.bool] <https://wg21.link/vector.bool>`_, "[vector<bool>::reference] add const operator= overload", None, Hui Xie, |Not Started|
 | `[iterator.concept.winc] <https://wg21.link/iterator.concept.winc>`_, "Update weakly_comparable", None, Hui Xie, |Not Started|
 | `[range.zip] <https://wg21.link/ranges.syn>`_, "`zip_view <https://reviews.llvm.org/D122806>`_", "| `zip_view::iterator`

diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 5154b4b6d38ff..8e79e106a5a92 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -412,6 +412,7 @@ set(files
   __memory/uninitialized_algorithms.h
   __memory/unique_ptr.h
   __memory/uses_allocator.h
+  __memory/uses_allocator_construction.h
   __memory/voidify.h
   __mutex_base
   __node_handle

diff  --git a/libcxx/include/__memory/uses_allocator_construction.h b/libcxx/include/__memory/uses_allocator_construction.h
new file mode 100644
index 0000000000000..02e0668d1233d
--- /dev/null
+++ b/libcxx/include/__memory/uses_allocator_construction.h
@@ -0,0 +1,219 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___MEMORY_USES_ALLOCATOR_CONSTRUCTION_H
+#define _LIBCPP___MEMORY_USES_ALLOCATOR_CONSTRUCTION_H
+
+#include <__config>
+#include <__memory/construct_at.h>
+#include <__memory/uses_allocator.h>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/is_same.h>
+#include <__utility/pair.h>
+#include <tuple>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 17
+
+template <class _Type>
+inline constexpr bool __is_std_pair = false;
+
+template <class _Type1, class _Type2>
+inline constexpr bool __is_std_pair<pair<_Type1, _Type2>> = true;
+
+template <class _Type, class _Alloc, class... _Args, __enable_if_t<!__is_std_pair<_Type>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr auto
+__uses_allocator_construction_args(const _Alloc& __alloc, _Args&&... __args) noexcept {
+  if constexpr (!uses_allocator_v<_Type, _Alloc> && is_constructible_v<_Type, _Args...>) {
+    return std::forward_as_tuple(std::forward<_Args>(__args)...);
+  } else if constexpr (uses_allocator_v<_Type, _Alloc> &&
+                       is_constructible_v<_Type, allocator_arg_t, const _Alloc&, _Args...>) {
+    return tuple<allocator_arg_t, const _Alloc&, _Args&&...>(allocator_arg, __alloc, std::forward<_Args>(__args)...);
+  } else if constexpr (uses_allocator_v<_Type, _Alloc> && is_constructible_v<_Type, _Args..., const _Alloc&>) {
+    return std::forward_as_tuple(std::forward<_Args>(__args)..., __alloc);
+  } else {
+    static_assert(
+        sizeof(_Type) + 1 == 0, "If uses_allocator_v<Type> is true, the type has to be allocator-constructible");
+  }
+}
+
+template <class _Pair, class _Alloc, class _Tuple1, class _Tuple2, __enable_if_t<__is_std_pair<_Pair>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr auto __uses_allocator_construction_args(
+    const _Alloc& __alloc, piecewise_construct_t, _Tuple1&& __x, _Tuple2&& __y) noexcept {
+  return std::make_tuple(
+      piecewise_construct,
+      std::apply(
+          [&__alloc](auto&&... __args1) {
+            return std::__uses_allocator_construction_args<typename _Pair::first_type>(
+                __alloc, std::forward<decltype(__args1)>(__args1)...);
+          },
+          std::forward<_Tuple1>(__x)),
+      std::apply(
+          [&__alloc](auto&&... __args2) {
+            return std::__uses_allocator_construction_args<typename _Pair::second_type>(
+                __alloc, std::forward<decltype(__args2)>(__args2)...);
+          },
+          std::forward<_Tuple2>(__y)));
+}
+
+template <class _Pair, class _Alloc, __enable_if_t<__is_std_pair<_Pair>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr auto __uses_allocator_construction_args(const _Alloc& __alloc) noexcept {
+  return std::__uses_allocator_construction_args<_Pair>(__alloc, piecewise_construct, tuple<>{}, tuple<>{});
+}
+
+template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_std_pair<_Pair>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr auto
+__uses_allocator_construction_args(const _Alloc& __alloc, _Up&& __u, _Vp&& __v) noexcept {
+  return std::__uses_allocator_construction_args<_Pair>(
+      __alloc,
+      piecewise_construct,
+      std::forward_as_tuple(std::forward<_Up>(__u)),
+      std::forward_as_tuple(std::forward<_Vp>(__v)));
+}
+
+#  if _LIBCPP_STD_VER > 20
+template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_std_pair<_Pair>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr auto
+__uses_allocator_construction_args(const _Alloc& __alloc, pair<_Up, _Vp>& __pair) noexcept {
+  return std::__uses_allocator_construction_args<_Pair>(
+      __alloc, piecewise_construct, std::forward_as_tuple(__pair.first), std::forward_as_tuple(__pair.second));
+}
+#  endif
+
+template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_std_pair<_Pair>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr auto
+__uses_allocator_construction_args(const _Alloc& __alloc, const pair<_Up, _Vp>& __pair) noexcept {
+  return std::__uses_allocator_construction_args<_Pair>(
+      __alloc, piecewise_construct, std::forward_as_tuple(__pair.first), std::forward_as_tuple(__pair.second));
+}
+
+template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_std_pair<_Pair>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr auto
+__uses_allocator_construction_args(const _Alloc& __alloc, pair<_Up, _Vp>&& __pair) noexcept {
+  return std::__uses_allocator_construction_args<_Pair>(
+      __alloc,
+      piecewise_construct,
+      std::forward_as_tuple(std::get<0>(std::move(__pair))),
+      std::forward_as_tuple(std::get<1>(std::move(__pair))));
+}
+
+#  if _LIBCPP_STD_VER > 20
+template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_std_pair<_Pair>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr auto
+__uses_allocator_construction_args(const _Alloc& __alloc, const pair<_Up, _Vp>&& __pair) noexcept {
+  return std::__uses_allocator_construction_args<_Pair>(
+      __alloc,
+      piecewise_construct,
+      std::forward_as_tuple(std::get<0>(std::move(__pair))),
+      std::forward_as_tuple(std::get<1>(std::move(__pair))));
+}
+#  endif
+
+namespace __uses_allocator_detail {
+
+template <class _Ap, class _Bp>
+void __fun(const pair<_Ap, _Bp>&);
+
+template <class _Tp>
+decltype(__uses_allocator_detail::__fun(std::declval<_Tp>()), true_type()) __convertible_to_const_pair_ref_impl(int);
+
+template <class>
+false_type __convertible_to_const_pair_ref_impl(...);
+
+template <class _Tp>
+inline constexpr bool __convertible_to_const_pair_ref =
+    decltype(__uses_allocator_detail::__convertible_to_const_pair_ref_impl<_Tp>(0))::value;
+
+} // namespace __uses_allocator_detail
+
+template <
+    class _Pair,
+    class _Alloc,
+    class _Type,
+    __enable_if_t<__is_std_pair<_Pair> && !__uses_allocator_detail::__convertible_to_const_pair_ref<_Type>, int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr auto
+__uses_allocator_construction_args(const _Alloc& __alloc, _Type&& __value) noexcept;
+
+template <class _Type, class _Alloc, class... _Args>
+_LIBCPP_HIDE_FROM_ABI constexpr _Type __make_obj_using_allocator(const _Alloc& __alloc, _Args&&... __args);
+
+template <class _Pair,
+          class _Alloc,
+          class _Type,
+          __enable_if_t<__is_std_pair<_Pair> && !__uses_allocator_detail::__convertible_to_const_pair_ref<_Type>, int>>
+_LIBCPP_HIDE_FROM_ABI constexpr auto
+__uses_allocator_construction_args(const _Alloc& __alloc, _Type&& __value) noexcept {
+  struct __pair_constructor {
+    using _PairMutable = remove_cv_t<_Pair>;
+
+    _LIBCPP_HIDE_FROM_ABI constexpr auto __do_construct(const _PairMutable& __pair) const {
+      return std::__make_obj_using_allocator<_PairMutable>(__alloc_, __pair);
+    }
+
+    _LIBCPP_HIDE_FROM_ABI constexpr auto __do_construct(_PairMutable&& __pair) const {
+      return std::__make_obj_using_allocator<_PairMutable>(__alloc_, std::move(__pair));
+    }
+
+    const _Alloc& __alloc_;
+    _Type& __value_;
+
+    _LIBCPP_HIDE_FROM_ABI constexpr operator _PairMutable() const {
+      return __do_construct(std::forward<_Type>(this->__value_));
+    }
+  };
+
+  return std::make_tuple(__pair_constructor{__alloc, __value});
+}
+
+template <class _Type, class _Alloc, class... _Args>
+_LIBCPP_HIDE_FROM_ABI constexpr _Type __make_obj_using_allocator(const _Alloc& __alloc, _Args&&... __args) {
+  return std::make_from_tuple<_Type>(
+      std::__uses_allocator_construction_args<_Type>(__alloc, std::forward<_Args>(__args)...));
+}
+
+template <class _Type, class _Alloc, class... _Args>
+_LIBCPP_HIDE_FROM_ABI constexpr _Type*
+__uninitialized_construct_using_allocator(_Type* __ptr, const _Alloc& __alloc, _Args&&... __args) {
+  return std::apply(
+      [&__ptr](auto&&... __xs) { return std::__construct_at(__ptr, std::forward<decltype(__xs)>(__xs)...); },
+      std::__uses_allocator_construction_args<_Type>(__alloc, std::forward<_Args>(__args)...));
+}
+
+#endif // _LIBCPP_STD_VER >= 17
+
+#if _LIBCPP_STD_VER >= 20
+
+template <class _Type, class _Alloc, class... _Args>
+_LIBCPP_HIDE_FROM_ABI constexpr auto uses_allocator_construction_args(const _Alloc& __alloc, _Args&&... __args) noexcept
+    -> decltype(std::__uses_allocator_construction_args<_Type>(__alloc, std::forward<_Args>(__args)...)) {
+  return /*--*/ std::__uses_allocator_construction_args<_Type>(__alloc, std::forward<_Args>(__args)...);
+}
+
+template <class _Type, class _Alloc, class... _Args>
+_LIBCPP_HIDE_FROM_ABI constexpr auto make_obj_using_allocator(const _Alloc& __alloc, _Args&&... __args)
+    -> decltype(std::__make_obj_using_allocator<_Type>(__alloc, std::forward<_Args>(__args)...)) {
+  return /*--*/ std::__make_obj_using_allocator<_Type>(__alloc, std::forward<_Args>(__args)...);
+}
+
+template <class _Type, class _Alloc, class... _Args>
+_LIBCPP_HIDE_FROM_ABI constexpr auto
+uninitialized_construct_using_allocator(_Type* __ptr, const _Alloc& __alloc, _Args&&... __args)
+    -> decltype(std::__uninitialized_construct_using_allocator(__ptr, __alloc, std::forward<_Args>(__args)...)) {
+  return /*--*/ std::__uninitialized_construct_using_allocator(__ptr, __alloc, std::forward<_Args>(__args)...);
+}
+
+#endif // _LIBCPP_STD_VER >= 20
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP___MEMORY_USES_ALLOCATOR_CONSTRUCTION_H

diff  --git a/libcxx/include/memory b/libcxx/include/memory
index 5975383aa26f4..8694cf6994a7c 100644
--- a/libcxx/include/memory
+++ b/libcxx/include/memory
@@ -883,6 +883,7 @@ template<size_t N, class T>
 #include <__memory/uninitialized_algorithms.h>
 #include <__memory/unique_ptr.h>
 #include <__memory/uses_allocator.h>
+#include <__memory/uses_allocator_construction.h>
 #include <version>
 
 // standard-mandated includes

diff  --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index b7f3380151161..dd7fd087e927a 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -878,6 +878,7 @@ module std [system] {
       module uninitialized_algorithms        { private header "__memory/uninitialized_algorithms.h" }
       module unique_ptr                      { private header "__memory/unique_ptr.h" }
       module uses_allocator                  { private header "__memory/uses_allocator.h" }
+      module uses_allocator_construction     { private header "__memory/uses_allocator_construction.h" }
       module voidify                         { private header "__memory/voidify.h" }
     }
   }

diff  --git a/libcxx/include/scoped_allocator b/libcxx/include/scoped_allocator
index b38e1dbcf787a..30a22ab7b4369 100644
--- a/libcxx/include/scoped_allocator
+++ b/libcxx/include/scoped_allocator
@@ -112,6 +112,7 @@ template <class OuterA1, class OuterA2, class... InnerAllocs>
 #include <__assert> // all public C++ headers provide the assertion handler
 #include <__config>
 #include <__memory/allocator_traits.h>
+#include <__memory/uses_allocator_construction.h>
 #include <__type_traits/common_type.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/integral_constant.h>
@@ -523,6 +524,18 @@ public:
     size_type max_size() const
         {return allocator_traits<outer_allocator_type>::max_size(outer_allocator());}
 
+#if _LIBCPP_STD_VER >= 20
+    template <class _Type, class... _Args>
+    _LIBCPP_HIDE_FROM_ABI void construct(_Type* __ptr, _Args&&... __args) {
+      using _OM = __outermost<outer_allocator_type>;
+      std::apply(
+          [__ptr, this](auto&&... __newargs) {
+            allocator_traits<typename _OM::type>::construct(
+                _OM()(outer_allocator()), __ptr, std::forward<decltype(__newargs)>(__newargs)...);
+          },
+          std::uses_allocator_construction_args<_Type>(inner_allocator(), std::forward<_Args>(__args)...));
+    }
+#else
     template <class _Tp, class... _Args>
         _LIBCPP_INLINE_VISIBILITY
         void construct(_Tp* __p, _Args&& ...__args)
@@ -577,6 +590,7 @@ public:
                   _VSTD::forward_as_tuple(_VSTD::forward<_Up>(__x.first)),
                   _VSTD::forward_as_tuple(_VSTD::forward<_Vp>(__x.second)));
     }
+#endif
 
     template <class _Tp>
         _LIBCPP_INLINE_VISIBILITY

diff  --git a/libcxx/test/libcxx/private_headers.verify.cpp b/libcxx/test/libcxx/private_headers.verify.cpp
index 409283fbca990..693dd1d3d15b3 100644
--- a/libcxx/test/libcxx/private_headers.verify.cpp
+++ b/libcxx/test/libcxx/private_headers.verify.cpp
@@ -443,6 +443,7 @@ END-SCRIPT
 #include <__memory/uninitialized_algorithms.h> // expected-error@*:* {{use of private header from outside its module: '__memory/uninitialized_algorithms.h'}}
 #include <__memory/unique_ptr.h> // expected-error@*:* {{use of private header from outside its module: '__memory/unique_ptr.h'}}
 #include <__memory/uses_allocator.h> // expected-error@*:* {{use of private header from outside its module: '__memory/uses_allocator.h'}}
+#include <__memory/uses_allocator_construction.h> // expected-error@*:* {{use of private header from outside its module: '__memory/uses_allocator_construction.h'}}
 #include <__memory/voidify.h> // expected-error@*:* {{use of private header from outside its module: '__memory/voidify.h'}}
 #include <__mutex_base> // expected-error@*:* {{use of private header from outside its module: '__mutex_base'}}
 #include <__node_handle> // expected-error@*:* {{use of private header from outside its module: '__node_handle'}}

diff  --git a/libcxx/test/libcxx/transitive_includes/cxx2b.csv b/libcxx/test/libcxx/transitive_includes/cxx2b.csv
index d052fb41f9ce8..4561b39e237c5 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx2b.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx2b.csv
@@ -479,6 +479,7 @@ memory iosfwd
 memory limits
 memory new
 memory stdexcept
+memory tuple
 memory type_traits
 memory typeinfo
 memory version

diff  --git a/libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct.pass.cpp b/libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct.pass.cpp
index 2be387e15a0eb..aa74bf5a8e889 100644
--- a/libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct.pass.cpp
+++ b/libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct.pass.cpp
@@ -115,10 +115,10 @@ struct G
 {
     static bool constructed;
 
-    typedef std::allocator<G> allocator_type;
+    typedef std::scoped_allocator_adaptor<std::allocator<G>> allocator_type;
 
     G(std::allocator_arg_t, allocator_type&&) { assert(false); }
-    G(allocator_type&) { constructed = true; }
+    G(const allocator_type&) { constructed = true; }
 };
 
 bool G::constructed = false;
@@ -202,7 +202,7 @@ int main(int, char**)
     // Test that is_constructible uses an lvalue ref so the correct constructor
     // is picked.
     {
-        std::scoped_allocator_adaptor<G::allocator_type> sa;
+        G::allocator_type sa;
         G* ptr = sa.allocate(1);
         sa.construct(ptr);
         assert(G::constructed);

diff  --git a/libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair.pass.cpp b/libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair.pass.cpp
index 88e7234d9b5ca..39623f8043659 100644
--- a/libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair.pass.cpp
+++ b/libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair.pass.cpp
@@ -46,10 +46,17 @@ void test_no_inner_alloc()
         A.construct(ptr);
         assert(checkConstruct<>(ptr->first, UA_AllocArg, CA));
         assert(checkConstruct<>(ptr->second, UA_AllocLast, CA));
+#if TEST_STD_VER >= 20
+        assert((P.checkConstruct<std::piecewise_construct_t&&,
+                                 std::tuple<std::allocator_arg_t, const SA&>&&,
+                                 std::tuple<const SA&>&&
+              >(CA, ptr)));
+#else
         assert((P.checkConstruct<std::piecewise_construct_t const&,
                                  std::tuple<std::allocator_arg_t, SA&>&&,
                                  std::tuple<SA&>&&
               >(CA, ptr)));
+#endif
         A.destroy(ptr);
         std::free(ptr);
 
@@ -69,10 +76,17 @@ void test_no_inner_alloc()
         A.construct(ptr);
         assert(checkConstruct<>(ptr->first, UA_AllocArg, CA));
         assert(checkConstruct<>(ptr->second, UA_None));
+#if TEST_STD_VER >= 20
+        assert((P.checkConstruct<std::piecewise_construct_t&&,
+                                 std::tuple<std::allocator_arg_t, const SA&>&&,
+                                 std::tuple<>&&
+                   >(CA, ptr)));
+#else
         assert((P.checkConstruct<std::piecewise_construct_t const&,
                                  std::tuple<std::allocator_arg_t, SA&>&&,
                                  std::tuple<>&&
                    >(CA, ptr)));
+#endif
         A.destroy(ptr);
         std::free(ptr);
     }
@@ -102,10 +116,17 @@ void test_with_inner_alloc()
         A.construct(ptr);
         assert(checkConstruct<>(ptr->first, UA_AllocArg, I));
         assert(checkConstruct<>(ptr->second, UA_AllocLast));
+#if TEST_STD_VER >= 20
+        assert((POuter.checkConstruct<std::piecewise_construct_t&&,
+                                 std::tuple<std::allocator_arg_t, const SAInner&>&&,
+                                 std::tuple<const SAInner&>&&
+              >(O, ptr)));
+#else
         assert((POuter.checkConstruct<std::piecewise_construct_t const&,
                                  std::tuple<std::allocator_arg_t, SAInner&>&&,
                                  std::tuple<SAInner&>&&
               >(O, ptr)));
+#endif
         A.destroy(ptr);
         std::free(ptr);
     }
@@ -129,10 +150,17 @@ void test_with_inner_alloc()
         A.construct(ptr);
         assert(checkConstruct<>(ptr->first, UA_AllocArg, I));
         assert(checkConstruct<>(ptr->second, UA_None));
+#if TEST_STD_VER >= 20
+        assert((POuter.checkConstruct<std::piecewise_construct_t&&,
+                                 std::tuple<std::allocator_arg_t, const SAInner&>&&,
+                                 std::tuple<>&&
+              >(O, ptr)));
+#else
         assert((POuter.checkConstruct<std::piecewise_construct_t const&,
                                  std::tuple<std::allocator_arg_t, SAInner&>&&,
                                  std::tuple<>&&
               >(O, ptr)));
+#endif
         A.destroy(ptr);
         std::free(ptr);
     }

diff  --git a/libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair_const_lvalue_pair.pass.cpp b/libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair_const_lvalue_pair.pass.cpp
index 46830a4649512..57c3263322a0a 100644
--- a/libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair_const_lvalue_pair.pass.cpp
+++ b/libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair_const_lvalue_pair.pass.cpp
@@ -50,10 +50,17 @@ void test_no_inner_alloc()
         A.construct(ptr, in);
         assert(checkConstruct<int&>(ptr->first, UA_AllocArg, CA));
         assert(checkConstruct<int const&>(ptr->second, UA_AllocLast, CA));
+#if TEST_STD_VER >= 20
+        assert((P.checkConstruct<std::piecewise_construct_t&&,
+                                 std::tuple<std::allocator_arg_t, const SA&, int&>&&,
+                                 std::tuple<int const&, const SA&>&&
+              >(CA, ptr)));
+#else
         assert((P.checkConstruct<std::piecewise_construct_t const&,
                                  std::tuple<std::allocator_arg_t, SA&, int&>&&,
                                  std::tuple<int const&, SA&>&&
               >(CA, ptr)));
+#endif
         A.destroy(ptr);
         std::free(ptr);
 
@@ -77,10 +84,17 @@ void test_no_inner_alloc()
         A.construct(ptr, in);
         assert(checkConstruct<int const&>(ptr->first, UA_AllocArg, CA));
         assert(checkConstruct<int const&>(ptr->second, UA_None));
+#if TEST_STD_VER >= 20
+        assert((P.checkConstruct<std::piecewise_construct_t&&,
+                                 std::tuple<std::allocator_arg_t, const SA&, int const&>&&,
+                                 std::tuple<int const&>&&
+                   >(CA, ptr)));
+#else
         assert((P.checkConstruct<std::piecewise_construct_t const&,
                                  std::tuple<std::allocator_arg_t, SA&, int const&>&&,
                                  std::tuple<int const&>&&
                    >(CA, ptr)));
+#endif
         A.destroy(ptr);
         std::free(ptr);
     }
@@ -114,10 +128,17 @@ void test_with_inner_alloc()
         A.construct(ptr, in);
         assert(checkConstruct<int&>(ptr->first, UA_AllocArg, I));
         assert(checkConstruct<int const&>(ptr->second, UA_AllocLast));
+#if TEST_STD_VER >= 20
+        assert((POuter.checkConstruct<std::piecewise_construct_t&&,
+                                 std::tuple<std::allocator_arg_t, const SAInner&, int&>&&,
+                                 std::tuple<int const&, const SAInner&>&&
+              >(O, ptr)));
+#else
         assert((POuter.checkConstruct<std::piecewise_construct_t const&,
                                  std::tuple<std::allocator_arg_t, SAInner&, int&>&&,
                                  std::tuple<int const&, SAInner&>&&
               >(O, ptr)));
+#endif
         A.destroy(ptr);
         std::free(ptr);
     }
@@ -145,10 +166,17 @@ void test_with_inner_alloc()
         A.construct(ptr, in);
         assert(checkConstruct<int const&>(ptr->first, UA_AllocArg, I));
         assert(checkConstruct<int const&>(ptr->second, UA_None));
+#if TEST_STD_VER >= 20
+        assert((POuter.checkConstruct<std::piecewise_construct_t&&,
+                                 std::tuple<std::allocator_arg_t, const SAInner&, int const&>&&,
+                                 std::tuple<int const&>&&
+              >(O, ptr)));
+#else
         assert((POuter.checkConstruct<std::piecewise_construct_t const&,
                                  std::tuple<std::allocator_arg_t, SAInner&, int const&>&&,
                                  std::tuple<int const&>&&
               >(O, ptr)));
+#endif
         A.destroy(ptr);
         std::free(ptr);
     }

diff  --git a/libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair_piecewise.pass.cpp b/libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair_piecewise.pass.cpp
index 9b169b3b1071c..b07f0362c7a64 100644
--- a/libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair_piecewise.pass.cpp
+++ b/libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair_piecewise.pass.cpp
@@ -51,10 +51,17 @@ void test_no_inner_alloc()
                     std::forward_as_tuple(std::move(y)));
         assert(checkConstruct<int&>(ptr->first, UA_AllocArg, CA));
         assert(checkConstruct<int const&&>(ptr->second, UA_AllocLast, CA));
+#if TEST_STD_VER >= 20
+        assert((P.checkConstruct<std::piecewise_construct_t&&,
+                                 std::tuple<std::allocator_arg_t, const SA&, int&>&&,
+                                 std::tuple<int const&&, const SA&>&&
+              >(CA, ptr)));
+#else
         assert((P.checkConstruct<std::piecewise_construct_t const&,
                                  std::tuple<std::allocator_arg_t, SA&, int&>&&,
                                  std::tuple<int const&&, SA&>&&
               >(CA, ptr)));
+#endif
         A.destroy(ptr);
         std::free(ptr);
 
@@ -78,10 +85,17 @@ void test_no_inner_alloc()
                     std::forward_as_tuple(y));
         assert(checkConstruct<int&&>(ptr->first, UA_AllocArg, CA));
         assert(checkConstruct<int const&>(ptr->second, UA_None));
+#if TEST_STD_VER >= 20
+        assert((P.checkConstruct<std::piecewise_construct_t&&,
+                                 std::tuple<std::allocator_arg_t, const SA&, int&&>&&,
+                                 std::tuple<int const&>&&
+                   >(CA, ptr)));
+#else
         assert((P.checkConstruct<std::piecewise_construct_t const&,
                                  std::tuple<std::allocator_arg_t, SA&, int&&>&&,
                                  std::tuple<int const&>&&
                    >(CA, ptr)));
+#endif
         A.destroy(ptr);
         std::free(ptr);
     }
@@ -115,10 +129,17 @@ void test_with_inner_alloc()
                     std::forward_as_tuple(std::move(y)));
         assert(checkConstruct<int&>(ptr->first, UA_AllocArg, I));
         assert(checkConstruct<int &&>(ptr->second, UA_AllocLast));
+#if TEST_STD_VER >= 20
+        assert((POuter.checkConstruct<std::piecewise_construct_t&&,
+                                 std::tuple<std::allocator_arg_t, const SAInner&, int&>&&,
+                                 std::tuple<int &&, const SAInner&>&&
+              >(O, ptr)));
+#else
         assert((POuter.checkConstruct<std::piecewise_construct_t const&,
                                  std::tuple<std::allocator_arg_t, SAInner&, int&>&&,
                                  std::tuple<int &&, SAInner&>&&
               >(O, ptr)));
+#endif
         A.destroy(ptr);
         std::free(ptr);
     }
@@ -146,10 +167,17 @@ void test_with_inner_alloc()
                     std::forward_as_tuple(std::move(y)));
         assert(checkConstruct<int&&>(ptr->first, UA_AllocArg, I));
         assert(checkConstruct<int const&&>(ptr->second, UA_None));
+#if TEST_STD_VER >= 20
+        assert((POuter.checkConstruct<std::piecewise_construct_t&&,
+                                 std::tuple<std::allocator_arg_t, const SAInner&, int&&>&&,
+                                 std::tuple<int const&&>&&
+              >(O, ptr)));
+#else
         assert((POuter.checkConstruct<std::piecewise_construct_t const&,
                                  std::tuple<std::allocator_arg_t, SAInner&, int&&>&&,
                                  std::tuple<int const&&>&&
               >(O, ptr)));
+#endif
         A.destroy(ptr);
         std::free(ptr);
     }

diff  --git a/libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair_rvalue.pass.cpp b/libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair_rvalue.pass.cpp
index 2e9b1432ac0e1..4d01e3e1cf792 100644
--- a/libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair_rvalue.pass.cpp
+++ b/libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair_rvalue.pass.cpp
@@ -50,10 +50,17 @@ void test_no_inner_alloc()
         A.construct(ptr, std::move(in));
         assert(checkConstruct<int&>(ptr->first, UA_AllocArg, CA));
         assert(checkConstruct<int const&&>(ptr->second, UA_AllocLast, CA));
+#if TEST_STD_VER >= 20
+        assert((P.checkConstruct<std::piecewise_construct_t&&,
+                                 std::tuple<std::allocator_arg_t, const SA&, int&>&&,
+                                 std::tuple<int const&&, const SA&>&&
+              >(CA, ptr)));
+#else
         assert((P.checkConstruct<std::piecewise_construct_t const&,
                                  std::tuple<std::allocator_arg_t, SA&, int&>&&,
                                  std::tuple<int const&&, SA&>&&
               >(CA, ptr)));
+#endif
         A.destroy(ptr);
         std::free(ptr);
 
@@ -77,10 +84,17 @@ void test_no_inner_alloc()
         A.construct(ptr, std::move(in));
         assert(checkConstruct<int&&>(ptr->first, UA_AllocArg, CA));
         assert(checkConstruct<int const&>(ptr->second, UA_None));
+#if TEST_STD_VER >= 20
+        assert((P.checkConstruct<std::piecewise_construct_t&&,
+                                 std::tuple<std::allocator_arg_t, const SA&, int&&>&&,
+                                 std::tuple<int const&>&&
+                   >(CA, ptr)));
+#else
         assert((P.checkConstruct<std::piecewise_construct_t const&,
                                  std::tuple<std::allocator_arg_t, SA&, int&&>&&,
                                  std::tuple<int const&>&&
                    >(CA, ptr)));
+#endif
         A.destroy(ptr);
         std::free(ptr);
     }
@@ -114,10 +128,17 @@ void test_with_inner_alloc()
         A.construct(ptr, std::move(in));
         assert(checkConstruct<int&>(ptr->first, UA_AllocArg, I));
         assert(checkConstruct<int const&&>(ptr->second, UA_AllocLast));
+#if TEST_STD_VER >= 20
+        assert((POuter.checkConstruct<std::piecewise_construct_t&&,
+                                 std::tuple<std::allocator_arg_t, const SAInner&, int&>&&,
+                                 std::tuple<int const&&, const SAInner&>&&
+              >(O, ptr)));
+#else
         assert((POuter.checkConstruct<std::piecewise_construct_t const&,
                                  std::tuple<std::allocator_arg_t, SAInner&, int&>&&,
                                  std::tuple<int const&&, SAInner&>&&
               >(O, ptr)));
+#endif
         A.destroy(ptr);
         std::free(ptr);
     }
@@ -145,10 +166,17 @@ void test_with_inner_alloc()
         A.construct(ptr, std::move(in));
         assert(checkConstruct<int&&>(ptr->first, UA_AllocArg, I));
         assert(checkConstruct<int const&>(ptr->second, UA_None));
+#if TEST_STD_VER >= 20
+        assert((POuter.checkConstruct<std::piecewise_construct_t&&,
+                                 std::tuple<std::allocator_arg_t, const SAInner&, int&&>&&,
+                                 std::tuple<int const&>&&
+              >(O, ptr)));
+#else
         assert((POuter.checkConstruct<std::piecewise_construct_t const&,
                                  std::tuple<std::allocator_arg_t, SAInner&, int&&>&&,
                                  std::tuple<int const&>&&
               >(O, ptr)));
+#endif
         A.destroy(ptr);
         std::free(ptr);
     }

diff  --git a/libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair_values.pass.cpp b/libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair_values.pass.cpp
index 4ea22440081c5..10f2f4843230b 100644
--- a/libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair_values.pass.cpp
+++ b/libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair_values.pass.cpp
@@ -48,10 +48,17 @@ void test_no_inner_alloc()
         A.construct(ptr, x, std::move(y));
         assert(checkConstruct<int&>(ptr->first, UA_AllocArg, CA));
         assert(checkConstruct<int const&&>(ptr->second, UA_AllocLast, CA));
+#if TEST_STD_VER >= 20
+        assert((P.checkConstruct<std::piecewise_construct_t&&,
+                                 std::tuple<std::allocator_arg_t, const SA&, int&>&&,
+                                 std::tuple<int const&&, const SA&>&&
+              >(CA, ptr)));
+#else
         assert((P.checkConstruct<std::piecewise_construct_t const&,
                                  std::tuple<std::allocator_arg_t, SA&, int&>&&,
                                  std::tuple<int const&&, SA&>&&
               >(CA, ptr)));
+#endif
         A.destroy(ptr);
         std::free(ptr);
 
@@ -73,10 +80,17 @@ void test_no_inner_alloc()
         A.construct(ptr, std::move(x), y);
         assert(checkConstruct<int&&>(ptr->first, UA_AllocArg, CA));
         assert(checkConstruct<int const&>(ptr->second, UA_None));
+#if TEST_STD_VER >= 20
+        assert((P.checkConstruct<std::piecewise_construct_t&&,
+                                 std::tuple<std::allocator_arg_t, const SA&, int&&>&&,
+                                 std::tuple<int const&>&&
+                   >(CA, ptr)));
+#else
         assert((P.checkConstruct<std::piecewise_construct_t const&,
                                  std::tuple<std::allocator_arg_t, SA&, int&&>&&,
                                  std::tuple<int const&>&&
                    >(CA, ptr)));
+#endif
         A.destroy(ptr);
         std::free(ptr);
     }
@@ -108,10 +122,17 @@ void test_with_inner_alloc()
         A.construct(ptr, x, std::move(y));
         assert(checkConstruct<int&>(ptr->first, UA_AllocArg, I));
         assert(checkConstruct<int &&>(ptr->second, UA_AllocLast));
+#if TEST_STD_VER >= 20
+        assert((POuter.checkConstruct<std::piecewise_construct_t&&,
+                                 std::tuple<std::allocator_arg_t, const SAInner&, int&>&&,
+                                 std::tuple<int &&, const SAInner&>&&
+              >(O, ptr)));
+#else
         assert((POuter.checkConstruct<std::piecewise_construct_t const&,
                                  std::tuple<std::allocator_arg_t, SAInner&, int&>&&,
                                  std::tuple<int &&, SAInner&>&&
               >(O, ptr)));
+#endif
         A.destroy(ptr);
         std::free(ptr);
     }
@@ -137,10 +158,17 @@ void test_with_inner_alloc()
         A.construct(ptr, std::move(x), std::move(y));
         assert(checkConstruct<int&&>(ptr->first, UA_AllocArg, I));
         assert(checkConstruct<int const&&>(ptr->second, UA_None));
+#if TEST_STD_VER >= 20
+        assert((POuter.checkConstruct<std::piecewise_construct_t&&,
+                                 std::tuple<std::allocator_arg_t, const SAInner&, int&&>&&,
+                                 std::tuple<int const&&>&&
+              >(O, ptr)));
+#else
         assert((POuter.checkConstruct<std::piecewise_construct_t const&,
                                  std::tuple<std::allocator_arg_t, SAInner&, int&&>&&,
                                  std::tuple<int const&&>&&
               >(O, ptr)));
+#endif
         A.destroy(ptr);
         std::free(ptr);
     }

diff  --git a/libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct_type.pass.cpp b/libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct_type.pass.cpp
index b3b548abb92b9..ef35897ef636a 100644
--- a/libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct_type.pass.cpp
+++ b/libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct_type.pass.cpp
@@ -84,8 +84,13 @@ void test_bullet_two() {
         int const& cx = x;
         A.construct(ptr, x, cx, std::move(x));
         assert((checkConstruct<int&, int const&, int&&>(*ptr, UA_AllocArg, I)));
+#if TEST_STD_VER >= 20
+        assert((POuter.checkConstruct<std::allocator_arg_t&&,
+                   const SA::inner_allocator_type&, int&, int const&, int&&>(O, ptr)));
+#else
         assert((POuter.checkConstruct<std::allocator_arg_t const&,
                    SA::inner_allocator_type&, int&, int const&, int&&>(O, ptr)));
+#endif
         A.destroy(ptr);
         ::operator delete((void*)ptr);
     }
@@ -117,9 +122,15 @@ void test_bullet_three() {
         int const& cx = x;
         A.construct(ptr, x, cx, std::move(x));
         assert((checkConstruct<int&, int const&, int&&>(*ptr, UA_AllocLast, I)));
+#if TEST_STD_VER >= 20
+        assert((POuter.checkConstruct<
+                   int&, int const&, int&&,
+                   const SA::inner_allocator_type&>(O, ptr)));
+#else
         assert((POuter.checkConstruct<
                    int&, int const&, int&&,
                    SA::inner_allocator_type&>(O, ptr)));
+#endif
         A.destroy(ptr);
         ::operator delete((void*)ptr);
     }

diff  --git a/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/common.h b/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/common.h
new file mode 100644
index 0000000000000..48b0fa85a8c23
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/common.h
@@ -0,0 +1,72 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "test_allocator.h"
+
+using Alloc = test_allocator<int>;
+
+enum class RefType {
+  LValue,
+  ConstLValue,
+  RValue,
+  ConstRValue,
+};
+
+struct UsesAllocArgT {
+  using allocator_type = Alloc;
+
+  bool allocator_constructed_ = false;
+  Alloc a_;
+  const Alloc& alloc_ = a_;
+  const int* val_ptr_;
+  RefType ref_type_;
+
+  constexpr UsesAllocArgT() = default;
+  constexpr UsesAllocArgT(std::allocator_arg_t, const Alloc& alloc) : allocator_constructed_(true), alloc_(alloc) {}
+  constexpr UsesAllocArgT(std::allocator_arg_t, const Alloc& alloc, int& val)
+      : allocator_constructed_(true), alloc_(alloc), val_ptr_(&val), ref_type_(RefType::LValue) {}
+  constexpr UsesAllocArgT(std::allocator_arg_t, const Alloc& alloc, const int& val)
+      : allocator_constructed_(true), alloc_(alloc), val_ptr_(&val), ref_type_(RefType::ConstLValue) {}
+  constexpr UsesAllocArgT(std::allocator_arg_t, const Alloc& alloc, int&& val)
+      : allocator_constructed_(true), alloc_(alloc), val_ptr_(&val), ref_type_(RefType::RValue) {}
+  constexpr UsesAllocArgT(std::allocator_arg_t, const Alloc& alloc, const int&& val)
+      : allocator_constructed_(true), alloc_(alloc), val_ptr_(&val), ref_type_(RefType::ConstRValue) {}
+};
+
+struct UsesAllocLast {
+  using allocator_type = Alloc;
+
+  bool allocator_constructed_ = false;
+  Alloc a_;
+  const Alloc& alloc_ = a_;
+  const int* val_ptr_;
+  RefType ref_type_;
+
+  constexpr UsesAllocLast() = default;
+  constexpr UsesAllocLast(const Alloc& alloc) : allocator_constructed_(true), alloc_(alloc) {}
+  constexpr UsesAllocLast(int& val, const Alloc& alloc)
+      : allocator_constructed_(true), alloc_(alloc), val_ptr_(&val), ref_type_(RefType::LValue) {}
+  constexpr UsesAllocLast(const int& val, const Alloc& alloc)
+      : allocator_constructed_(true), alloc_(alloc), val_ptr_(&val), ref_type_(RefType::ConstLValue) {}
+  constexpr UsesAllocLast(int&& val, const Alloc& alloc)
+      : allocator_constructed_(true), alloc_(alloc), val_ptr_(&val), ref_type_(RefType::RValue) {}
+  constexpr UsesAllocLast(const int&& val, const Alloc& alloc)
+      : allocator_constructed_(true), alloc_(alloc), val_ptr_(&val), ref_type_(RefType::ConstRValue) {}
+};
+
+struct NotAllocatorAware {
+  bool allocator_constructed_ = false;
+
+  constexpr NotAllocatorAware() = default;
+  constexpr NotAllocatorAware(const Alloc&) : allocator_constructed_(true) {}
+  constexpr NotAllocatorAware(const Alloc&, int) : allocator_constructed_(true) {}
+};
+
+struct ConvertibleToPair {
+  constexpr operator std::pair<int, int>() const { return {1, 2}; }
+};

diff  --git a/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/make_obj_using_allocator.pass.cpp b/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/make_obj_using_allocator.pass.cpp
new file mode 100644
index 0000000000000..faafcd0dd9906
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/make_obj_using_allocator.pass.cpp
@@ -0,0 +1,139 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// template<class T, class Alloc, class... Args>
+//   constexpr T make_obj_using_allocator(const Alloc& alloc, Args&&... args);
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <concepts>
+#include <memory>
+#include <tuple>
+#include <utility>
+
+#include "common.h"
+#include "test_allocator.h"
+
+constexpr bool test() {
+  Alloc a(12);
+  {
+    std::same_as<UsesAllocArgT> auto ret = std::make_obj_using_allocator<UsesAllocArgT>(a);
+    assert(ret.allocator_constructed_);
+    assert(&ret.alloc_ == &a);
+  }
+  {
+    std::same_as<UsesAllocLast> auto ret = std::make_obj_using_allocator<UsesAllocLast>(a);
+    assert(ret.allocator_constructed_);
+    assert(&ret.alloc_ == &a);
+  }
+  {
+    std::same_as<NotAllocatorAware> auto ret = std::make_obj_using_allocator<NotAllocatorAware>(a);
+    assert(!ret.allocator_constructed_);
+  }
+  {
+    std::same_as<std::pair<UsesAllocArgT, UsesAllocLast>> auto ret =
+        std::make_obj_using_allocator<std::pair<UsesAllocArgT, UsesAllocLast>>(
+            a, std::piecewise_construct, std::tuple<>{}, std::tuple<>{});
+    assert(ret.first.allocator_constructed_);
+    assert(&ret.first.alloc_ == &a);
+    assert(ret.second.allocator_constructed_);
+    assert(&ret.second.alloc_ == &a);
+  }
+  {
+    std::same_as<std::pair<UsesAllocArgT, UsesAllocLast>> auto ret =
+        std::make_obj_using_allocator<std::pair<UsesAllocArgT, UsesAllocLast>>(a);
+    assert(ret.first.allocator_constructed_);
+    assert(&ret.first.alloc_ == &a);
+    assert(ret.second.allocator_constructed_);
+    assert(&ret.second.alloc_ == &a);
+  }
+  {
+    std::same_as<std::pair<UsesAllocArgT, UsesAllocLast>> auto ret =
+        std::make_obj_using_allocator<std::pair<UsesAllocArgT, UsesAllocLast>>(a, 0, 0);
+    assert(ret.first.allocator_constructed_);
+    assert(&ret.first.alloc_ == &a);
+    assert(ret.second.allocator_constructed_);
+    assert(&ret.second.alloc_ == &a);
+  }
+#if TEST_STD_VER >= 23
+  {
+    std::pair p{0, 0};
+
+    std::same_as<std::pair<UsesAllocArgT, UsesAllocLast>> auto ret =
+        std::make_obj_using_allocator<std::pair<UsesAllocArgT, UsesAllocLast>>(a, p);
+    assert(ret.first.allocator_constructed_);
+    assert(&ret.first.alloc_ == &a);
+    assert(ret.first.ref_type_ == RefType::LValue);
+    assert(ret.first.val_ptr_ == &p.first);
+    assert(ret.second.allocator_constructed_);
+    assert(&ret.second.alloc_ == &a);
+    assert(ret.second.ref_type_ == RefType::LValue);
+    assert(ret.second.val_ptr_ == &p.second);
+  }
+#endif
+  {
+    std::pair p{0, 0};
+    std::same_as<std::pair<UsesAllocArgT, UsesAllocLast>> auto ret =
+        std::make_obj_using_allocator<std::pair<UsesAllocArgT, UsesAllocLast>>(a, std::as_const(p));
+    assert(ret.first.allocator_constructed_);
+    assert(&ret.first.alloc_ == &a);
+    assert(ret.first.ref_type_ == RefType::ConstLValue);
+    assert(ret.first.val_ptr_ == &p.first);
+    assert(ret.second.allocator_constructed_);
+    assert(&ret.second.alloc_ == &a);
+    assert(ret.second.ref_type_ == RefType::ConstLValue);
+    assert(ret.second.val_ptr_ == &p.second);
+  }
+  {
+    std::pair p{0, 0};
+    std::same_as<std::pair<UsesAllocArgT, UsesAllocLast>> auto ret =
+        std::make_obj_using_allocator<std::pair<UsesAllocArgT, UsesAllocLast>>(a, std::move(p));
+    assert(ret.first.allocator_constructed_);
+    assert(&ret.first.alloc_ == &a);
+    assert(ret.first.ref_type_ == RefType::RValue);
+    assert(ret.first.val_ptr_ == &p.first);
+    assert(ret.second.allocator_constructed_);
+    assert(&ret.second.alloc_ == &a);
+    assert(ret.second.ref_type_ == RefType::RValue);
+    assert(ret.second.val_ptr_ == &p.second);
+  }
+#if TEST_STD_VER >= 23
+  {
+    std::pair p{0, 0};
+    std::same_as<std::pair<UsesAllocArgT, UsesAllocLast>> auto ret =
+        std::make_obj_using_allocator<std::pair<UsesAllocArgT, UsesAllocLast>>(a, std::move(std::as_const(p)));
+    assert(ret.first.allocator_constructed_);
+    assert(&ret.first.alloc_ == &a);
+    assert(ret.first.ref_type_ == RefType::ConstRValue);
+    assert(ret.first.val_ptr_ == &p.first);
+    assert(ret.second.allocator_constructed_);
+    assert(&ret.second.alloc_ == &a);
+    assert(ret.second.ref_type_ == RefType::ConstRValue);
+    assert(ret.second.val_ptr_ == &p.second);
+  }
+#endif
+  {
+    ConvertibleToPair ctp;
+    std::same_as<std::pair<int, int>> auto ret = std::make_obj_using_allocator<std::pair<int, int>>(a, ctp);
+    assert(ret.first == 1);
+    assert(ret.second == 2);
+  }
+  {
+    ConvertibleToPair ctp;
+    std::same_as<std::pair<int, int>> auto ret = std::make_obj_using_allocator<std::pair<int, int>>(a, std::move(ctp));
+    assert(ret.first == 1);
+    assert(ret.second == 2);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+}

diff  --git a/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/tested_elsewhere.pass.cpp b/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/tested_elsewhere.pass.cpp
deleted file mode 100644
index 1f764da05d6b5..0000000000000
--- a/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/tested_elsewhere.pass.cpp
+++ /dev/null
@@ -1,13 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-int main(int, char**)
-{
-
-  return 0;
-}

diff  --git a/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/uninitialized_construct_using_allocator.pass.cpp b/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/uninitialized_construct_using_allocator.pass.cpp
new file mode 100644
index 0000000000000..1bafed0a798a7
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/uninitialized_construct_using_allocator.pass.cpp
@@ -0,0 +1,185 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// template<class T, class Alloc, class... Args>
+//   constexpr T uninitialized_construct_using_allocator(const Alloc& alloc, Args&&... args);
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <concepts>
+#include <memory>
+#include <tuple>
+#include <utility>
+
+#include "common.h"
+#include "test_allocator.h"
+
+constexpr bool test() {
+  Alloc a(12);
+  {
+    auto* ptr                             = std::allocator<UsesAllocArgT>{}.allocate(1);
+    std::same_as<UsesAllocArgT*> auto ret = std::uninitialized_construct_using_allocator(ptr, a);
+    assert(ret == ptr);
+    assert(ret->allocator_constructed_);
+    assert(&ret->alloc_ == &a);
+    std::allocator<UsesAllocArgT>{}.deallocate(ptr, 1);
+  }
+  {
+    auto* ptr                             = std::allocator<UsesAllocLast>{}.allocate(1);
+    std::same_as<UsesAllocLast*> auto ret = std::uninitialized_construct_using_allocator(ptr, a);
+    assert(ret->allocator_constructed_);
+    assert(&ret->alloc_ == &a);
+    std::allocator<UsesAllocLast>{}.deallocate(ptr, 1);
+  }
+  {
+    auto* ptr                                 = std::allocator<NotAllocatorAware>{}.allocate(1);
+    std::same_as<NotAllocatorAware*> auto ret = std::uninitialized_construct_using_allocator(ptr, a);
+    assert(!ret->allocator_constructed_);
+    std::allocator<NotAllocatorAware>{}.deallocate(ptr, 1);
+  }
+  {
+    auto* ptr = std::allocator<std::pair<UsesAllocArgT, UsesAllocLast>>{}.allocate(1);
+    std::same_as<std::pair<UsesAllocArgT, UsesAllocLast>*> auto ret =
+        std::uninitialized_construct_using_allocator(ptr, a, std::piecewise_construct, std::tuple<>{}, std::tuple<>{});
+    assert(ret->first.allocator_constructed_);
+    assert(&ret->first.alloc_ == &a);
+    assert(ret->second.allocator_constructed_);
+    assert(&ret->second.alloc_ == &a);
+    std::allocator<std::pair<UsesAllocArgT, UsesAllocLast>>{}.deallocate(ptr, 1);
+  }
+  {
+    auto* ptr = std::allocator<std::pair<UsesAllocArgT, UsesAllocLast>>{}.allocate(1);
+    std::same_as<std::pair<UsesAllocArgT, UsesAllocLast>*> auto ret =
+        std::uninitialized_construct_using_allocator(ptr, a);
+    assert(ret->first.allocator_constructed_);
+    assert(&ret->first.alloc_ == &a);
+    assert(ret->second.allocator_constructed_);
+    assert(&ret->second.alloc_ == &a);
+    std::allocator<std::pair<UsesAllocArgT, UsesAllocLast>>{}.deallocate(ptr, 1);
+  }
+  {
+    int val   = 0;
+    auto* ptr = std::allocator<std::pair<UsesAllocArgT, UsesAllocLast>>{}.allocate(1);
+    std::same_as<std::pair<UsesAllocArgT, UsesAllocLast>*> auto ret =
+        std::uninitialized_construct_using_allocator(ptr, a, val, val);
+    assert(ret->first.allocator_constructed_);
+    assert(&ret->first.alloc_ == &a);
+    assert(ret->first.ref_type_ == RefType::LValue);
+    assert(ret->first.val_ptr_ == &val);
+    assert(ret->second.allocator_constructed_);
+    assert(&ret->second.alloc_ == &a);
+    assert(ret->second.ref_type_ == RefType::LValue);
+    assert(ret->second.val_ptr_ == &val);
+    std::allocator<std::pair<UsesAllocArgT, UsesAllocLast>>{}.deallocate(ptr, 1);
+  }
+  {
+    int val   = 0;
+    auto* ptr = std::allocator<std::pair<UsesAllocArgT, UsesAllocLast>>{}.allocate(1);
+    std::same_as<std::pair<UsesAllocArgT, UsesAllocLast>*> auto ret =
+        std::uninitialized_construct_using_allocator(ptr, a, std::move(val), std::move(val));
+    assert(ret->first.allocator_constructed_);
+    assert(&ret->first.alloc_ == &a);
+    assert(ret->first.ref_type_ == RefType::RValue);
+    assert(ret->first.val_ptr_ == &val);
+    assert(ret->second.allocator_constructed_);
+    assert(&ret->second.alloc_ == &a);
+    assert(ret->second.ref_type_ == RefType::RValue);
+    assert(ret->second.val_ptr_ == &val);
+    std::allocator<std::pair<UsesAllocArgT, UsesAllocLast>>{}.deallocate(ptr, 1);
+  }
+#if TEST_STD_VER >= 23
+  {
+    std::pair p{0, 0};
+
+    auto* ptr = std::allocator<std::pair<UsesAllocArgT, UsesAllocLast>>{}.allocate(1);
+    std::same_as<std::pair<UsesAllocArgT, UsesAllocLast>*> auto ret =
+        std::uninitialized_construct_using_allocator(ptr, a, p);
+    assert(ret->first.allocator_constructed_);
+    assert(&ret->first.alloc_ == &a);
+    assert(ret->first.ref_type_ == RefType::LValue);
+    assert(ret->first.val_ptr_ == &p.first);
+    assert(ret->second.allocator_constructed_);
+    assert(&ret->second.alloc_ == &a);
+    assert(ret->second.ref_type_ == RefType::LValue);
+    assert(ret->second.val_ptr_ == &p.second);
+    std::allocator<std::pair<UsesAllocArgT, UsesAllocLast>>{}.deallocate(ptr, 1);
+  }
+#endif
+  {
+    std::pair p{0, 0};
+    auto* ptr = std::allocator<std::pair<UsesAllocArgT, UsesAllocLast>>{}.allocate(1);
+    std::same_as<std::pair<UsesAllocArgT, UsesAllocLast>*> auto ret =
+        std::uninitialized_construct_using_allocator(ptr, a, std::as_const(p));
+    assert(ret->first.allocator_constructed_);
+    assert(&ret->first.alloc_ == &a);
+    assert(ret->first.ref_type_ == RefType::ConstLValue);
+    assert(ret->first.val_ptr_ == &p.first);
+    assert(ret->second.allocator_constructed_);
+    assert(&ret->second.alloc_ == &a);
+    assert(ret->second.ref_type_ == RefType::ConstLValue);
+    assert(ret->second.val_ptr_ == &p.second);
+    std::allocator<std::pair<UsesAllocArgT, UsesAllocLast>>{}.deallocate(ptr, 1);
+  }
+  {
+    std::pair p{0, 0};
+    auto* ptr = std::allocator<std::pair<UsesAllocArgT, UsesAllocLast>>{}.allocate(1);
+    std::same_as<std::pair<UsesAllocArgT, UsesAllocLast>*> auto ret =
+        std::uninitialized_construct_using_allocator(ptr, a, std::move(p));
+    assert(ret->first.allocator_constructed_);
+    assert(&ret->first.alloc_ == &a);
+    assert(ret->first.ref_type_ == RefType::RValue);
+    assert(ret->first.val_ptr_ == &p.first);
+    assert(ret->second.allocator_constructed_);
+    assert(&ret->second.alloc_ == &a);
+    assert(ret->second.ref_type_ == RefType::RValue);
+    assert(ret->second.val_ptr_ == &p.second);
+    std::allocator<std::pair<UsesAllocArgT, UsesAllocLast>>{}.deallocate(ptr, 1);
+  }
+#if TEST_STD_VER >= 23
+  {
+    std::pair p{0, 0};
+    auto* ptr = std::allocator<std::pair<UsesAllocArgT, UsesAllocLast>>{}.allocate(1);
+    std::same_as<std::pair<UsesAllocArgT, UsesAllocLast>*> auto ret =
+        std::uninitialized_construct_using_allocator(ptr, a, std::move(std::as_const(p)));
+    assert(ret->first.allocator_constructed_);
+    assert(&ret->first.alloc_ == &a);
+    assert(ret->first.ref_type_ == RefType::ConstRValue);
+    assert(ret->first.val_ptr_ == &p.first);
+    assert(ret->second.allocator_constructed_);
+    assert(&ret->second.alloc_ == &a);
+    assert(ret->second.ref_type_ == RefType::ConstRValue);
+    assert(ret->second.val_ptr_ == &p.second);
+    std::allocator<std::pair<UsesAllocArgT, UsesAllocLast>>{}.deallocate(ptr, 1);
+  }
+#endif
+  {
+    ConvertibleToPair ctp;
+    auto* ptr                                   = std::allocator<std::pair<int, int>>{}.allocate(1);
+    std::same_as<std::pair<int, int>*> auto ret = std::uninitialized_construct_using_allocator(ptr, a, ctp);
+    assert(ret == ptr);
+    assert(ret->first == 1);
+    assert(ret->second == 2);
+    std::allocator<std::pair<int, int>>{}.deallocate(ptr, 1);
+  }
+  {
+    ConvertibleToPair ctp;
+    auto* ptr                                   = std::allocator<std::pair<int, int>>{}.allocate(1);
+    std::same_as<std::pair<int, int>*> auto ret = std::uninitialized_construct_using_allocator(ptr, a, std::move(ctp));
+    assert(ret == ptr);
+    assert(ret->first == 1);
+    assert(ret->second == 2);
+    std::allocator<std::pair<int, int>>{}.deallocate(ptr, 1);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+}

diff  --git a/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/uses_allocator_construction_args.pass.cpp b/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/uses_allocator_construction_args.pass.cpp
new file mode 100644
index 0000000000000..0677d7ffd1c5d
--- /dev/null
+++ b/libcxx/test/std/utilities/memory/allocator.uses/allocator.uses.construction/uses_allocator_construction_args.pass.cpp
@@ -0,0 +1,152 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// template<class T, class Alloc, ...>
+// constexpr auto uses_allocator_construction_args(const Alloc& alloc, ...) noexcept;
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+#include <concepts>
+#include <memory>
+#include <tuple>
+#include <utility>
+
+#include "common.h"
+#include "test_allocator.h"
+
+template <class Type, class... Args>
+constexpr decltype(auto) test_uses_allocator_construction_args(Args&&... args) {
+  static_assert(noexcept(std::uses_allocator_construction_args<Type>(std::forward<Args>(args)...)));
+  return std::uses_allocator_construction_args<Type>(std::forward<Args>(args)...);
+}
+
+constexpr bool test() {
+  Alloc a(12);
+  {
+    std::same_as<std::tuple<std::allocator_arg_t, const Alloc&>> auto ret =
+        test_uses_allocator_construction_args<UsesAllocArgT>(a);
+    assert(std::get<1>(ret).get_data() == 12);
+  }
+  {
+    std::same_as<std::tuple<const Alloc&>> auto ret = test_uses_allocator_construction_args<UsesAllocLast>(a);
+    assert(std::get<0>(ret).get_data() == 12);
+  }
+  {
+    [[maybe_unused]] std::same_as<std::tuple<>> auto ret = test_uses_allocator_construction_args<NotAllocatorAware>(a);
+  }
+  {
+    std::same_as<std::tuple<std::piecewise_construct_t,
+                            std::tuple<std::allocator_arg_t, const Alloc&>,
+                            std::tuple<const Alloc&>>> auto ret =
+        test_uses_allocator_construction_args<std::pair<UsesAllocArgT, UsesAllocLast>>(
+            a, std::piecewise_construct, std::tuple<>{}, std::tuple<>{});
+    assert(std::get<1>(std::get<1>(ret)).get_data() == 12);
+    assert(std::get<0>(std::get<2>(ret)).get_data() == 12);
+  }
+  {
+    std::same_as<std::tuple<std::piecewise_construct_t,
+                            std::tuple<std::allocator_arg_t, const Alloc&>,
+                            std::tuple<const Alloc&>>> auto ret =
+        test_uses_allocator_construction_args<std::pair<UsesAllocArgT, UsesAllocLast>>(a);
+    assert(std::get<1>(std::get<1>(ret)).get_data() == 12);
+    assert(std::get<0>(std::get<2>(ret)).get_data() == 12);
+  }
+  {
+    int val = 0;
+    std::same_as<std::tuple<std::piecewise_construct_t,
+                            std::tuple<std::allocator_arg_t, const Alloc&, int&>,
+                            std::tuple<int&, const Alloc&>>> auto ret =
+        test_uses_allocator_construction_args<std::pair<UsesAllocArgT, UsesAllocLast>>(a, val, val);
+    assert(std::get<1>(std::get<1>(ret)).get_data() == 12);
+    assert(std::get<1>(std::get<2>(ret)).get_data() == 12);
+    assert(&std::get<2>(std::get<1>(ret)) == &val);
+    assert(&std::get<0>(std::get<2>(ret)) == &val);
+  }
+  {
+    int val = 0;
+    std::same_as<std::tuple<std::piecewise_construct_t,
+                            std::tuple<std::allocator_arg_t, const Alloc&, int&&>,
+                            std::tuple<int&&, const Alloc&>>> auto ret =
+        test_uses_allocator_construction_args<std::pair<UsesAllocArgT, UsesAllocLast>>(
+            a, std::move(val), std::move(val));
+    assert(std::get<1>(std::get<1>(ret)).get_data() == 12);
+    assert(std::get<1>(std::get<2>(ret)).get_data() == 12);
+    assert(&std::get<2>(std::get<1>(ret)) == &val);
+    assert(&std::get<0>(std::get<2>(ret)) == &val);
+  }
+#if TEST_STD_VER >= 23
+  {
+    std::pair p{3, 4};
+
+    std::same_as<std::tuple<std::piecewise_construct_t,
+                            std::tuple<std::allocator_arg_t, const Alloc&, int&>,
+                            std::tuple<int&, const Alloc&>>> auto ret =
+        test_uses_allocator_construction_args<std::pair<UsesAllocArgT, UsesAllocLast>>(a, p);
+    assert(std::get<1>(std::get<1>(ret)).get_data() == 12);
+    assert(std::get<1>(std::get<2>(ret)).get_data() == 12);
+    assert(std::get<2>(std::get<1>(ret)) == 3);
+    assert(std::get<0>(std::get<2>(ret)) == 4);
+  }
+#endif
+  {
+    std::pair p{3, 4};
+    std::same_as<std::tuple<std::piecewise_construct_t,
+                            std::tuple<std::allocator_arg_t, const Alloc&, const int&>,
+                            std::tuple<const int&, const Alloc&>>> auto ret =
+        test_uses_allocator_construction_args<std::pair<UsesAllocArgT, UsesAllocLast>>(a, std::as_const(p));
+    assert(std::get<1>(std::get<1>(ret)).get_data() == 12);
+    assert(std::get<1>(std::get<2>(ret)).get_data() == 12);
+    assert(std::get<2>(std::get<1>(ret)) == 3);
+    assert(std::get<0>(std::get<2>(ret)) == 4);
+  }
+  {
+    std::pair p{3, 4};
+    std::same_as<std::tuple<std::piecewise_construct_t,
+                            std::tuple<std::allocator_arg_t, const Alloc&, int&&>,
+                            std::tuple<int&&, const Alloc&>>> auto ret =
+        test_uses_allocator_construction_args<std::pair<UsesAllocArgT, UsesAllocLast>>(a, std::move(p));
+    assert(std::get<1>(std::get<1>(ret)).get_data() == 12);
+    assert(std::get<1>(std::get<2>(ret)).get_data() == 12);
+    assert(std::get<2>(std::get<1>(ret)) == 3);
+    assert(std::get<0>(std::get<2>(ret)) == 4);
+  }
+#if TEST_STD_VER >= 23
+  {
+    std::pair p{3, 4};
+    std::same_as<std::tuple<std::piecewise_construct_t,
+                            std::tuple<std::allocator_arg_t, const Alloc&, const int&&>,
+                            std::tuple<const int&&, const Alloc&>>> auto ret =
+        test_uses_allocator_construction_args<std::pair<UsesAllocArgT, UsesAllocLast>>(a, std::move(std::as_const(p)));
+    assert(std::get<1>(std::get<1>(ret)).get_data() == 12);
+    assert(std::get<1>(std::get<2>(ret)).get_data() == 12);
+    assert(std::get<2>(std::get<1>(ret)) == 3);
+    assert(std::get<0>(std::get<2>(ret)) == 4);
+  }
+#endif
+  {
+    ConvertibleToPair ctp {};
+    auto ret = test_uses_allocator_construction_args<std::pair<int, int>>(a, ctp);
+    std::pair<int, int> v = std::get<0>(ret);
+    assert(std::get<0>(v) == 1);
+    assert(std::get<1>(v) == 2);
+  }
+  {
+    ConvertibleToPair ctp {};
+    auto ret = test_uses_allocator_construction_args<std::pair<int, int>>(a, std::move(ctp));
+    std::pair<int, int> v = std::get<0>(ret);
+    assert(std::get<0>(v) == 1);
+    assert(std::get<1>(v) == 2);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+}

diff  --git a/libcxx/test/support/MoveOnly.h b/libcxx/test/support/MoveOnly.h
index a67f8de607414..0795cac4ef5df 100644
--- a/libcxx/test/support/MoveOnly.h
+++ b/libcxx/test/support/MoveOnly.h
@@ -23,7 +23,7 @@ class MoveOnly
     MoveOnly(const MoveOnly&) = delete;
     MoveOnly& operator=(const MoveOnly&) = delete;
 
-    TEST_CONSTEXPR_CXX14 MoveOnly(MoveOnly&& x)
+    TEST_CONSTEXPR_CXX14 MoveOnly(MoveOnly&& x) TEST_NOEXCEPT
         : data_(x.data_) {x.data_ = 0;}
     TEST_CONSTEXPR_CXX14 MoveOnly& operator=(MoveOnly&& x)
         {data_ = x.data_; x.data_ = 0; return *this;}


        


More information about the libcxx-commits mailing list