[libcxx-commits] [libcxx] [libc++] LWG3187: P0591R4 reverted DR 2586 fixes to `scoped_allocator_adaptor::construct()` (PR #152424)

A. Jiang via libcxx-commits libcxx-commits at lists.llvm.org
Wed Aug 6 20:51:12 PDT 2025


https://github.com/frederick-vs-ja updated https://github.com/llvm/llvm-project/pull/152424

>From cfef8fb36c5b535716754b3fad0dfdd12ff738d2 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Thu, 7 Aug 2025 11:50:51 +0800
Subject: [PATCH] [libc++] LWG3187: P0591R4 reverted DR 2586 fixes to ...

... `scoped_allocator_adaptor::construct()`

Applying it (adding missing `const``) to pre-C++20 dispatching
mechanisms.

Also
- make dispatch mechanisms in pre-C++20 `construct` more consistent, and
- add fallback dispatching and overloads to emit better error messages.
---
 libcxx/docs/Status/Cxx20Issues.csv            |  2 +-
 libcxx/include/__memory/allocator_arg_t.h     | 28 ++------
 .../__memory/uses_allocator_construction.h    | 38 +++++++++++
 .../__memory_resource/polymorphic_allocator.h | 51 ++++++--------
 libcxx/include/scoped_allocator               | 68 ++++++-------------
 libcxx/include/tuple                          | 10 +++
 .../construct_pair.pass.cpp                   | 35 ++--------
 .../construct_pair_const_lvalue_pair.pass.cpp | 32 ++-------
 .../construct_pair_piecewise.pass.cpp         | 32 ++-------
 .../construct_pair_rvalue.pass.cpp            | 32 ++-------
 .../construct_pair_values.pass.cpp            | 32 ++-------
 .../construct_type.pass.cpp                   | 25 +++----
 .../construct_piecewise_pair_evil.pass.cpp    |  6 +-
 libcxx/test/support/uses_alloc_types.h        | 16 +++++
 14 files changed, 143 insertions(+), 264 deletions(-)

diff --git a/libcxx/docs/Status/Cxx20Issues.csv b/libcxx/docs/Status/Cxx20Issues.csv
index 98b49f92bdf7a..7b5bcd69f6abd 100644
--- a/libcxx/docs/Status/Cxx20Issues.csv
+++ b/libcxx/docs/Status/Cxx20Issues.csv
@@ -151,7 +151,7 @@
 "`LWG3184 <https://wg21.link/LWG3184>`__","Inconsistencies in ``bind_front``\  wording","2019-07 (Cologne)","|Complete|","13",""
 "`LWG3185 <https://wg21.link/LWG3185>`__","Uses-allocator construction functions missing ``constexpr``\  and ``noexcept``\ ","2019-07 (Cologne)","|Complete|","16",""
 "`LWG3186 <https://wg21.link/LWG3186>`__","``ranges``\  removal, partition, and ``partial_sort_copy``\  algorithms discard useful information","2019-07 (Cologne)","|Complete|","15",""
-"`LWG3187 <https://wg21.link/LWG3187>`__","`P0591R4 <https://wg21.link/p0591r4>`__ reverted DR 2586 fixes to ``scoped_allocator_adaptor::construct()``\ ","2019-07 (Cologne)","","",""
+"`LWG3187 <https://wg21.link/LWG3187>`__","`P0591R4 <https://wg21.link/p0591r4>`__ reverted DR 2586 fixes to ``scoped_allocator_adaptor::construct()``\ ","2019-07 (Cologne)","|Complete|","22",""
 "`LWG3191 <https://wg21.link/LWG3191>`__","``std::ranges::shuffle``\  synopsis does not match algorithm definition","2019-07 (Cologne)","|Complete|","15",""
 "`LWG3196 <https://wg21.link/LWG3196>`__","``std::optional<T>``\  is ill-formed is ``T``\  is an array","2019-07 (Cologne)","|Complete|","",""
 "`LWG3198 <https://wg21.link/LWG3198>`__","Bad constraint on ``std::span::span()``\ ","2019-07 (Cologne)","|Complete|","",""
diff --git a/libcxx/include/__memory/allocator_arg_t.h b/libcxx/include/__memory/allocator_arg_t.h
index 31a73fc4557ef..a3dac879f45b6 100644
--- a/libcxx/include/__memory/allocator_arg_t.h
+++ b/libcxx/include/__memory/allocator_arg_t.h
@@ -14,6 +14,7 @@
 #include <__memory/uses_allocator.h>
 #include <__type_traits/integral_constant.h>
 #include <__type_traits/is_constructible.h>
+#include <__type_traits/remove_cv.h>
 #include <__type_traits/remove_cvref.h>
 #include <__utility/forward.h>
 
@@ -40,34 +41,15 @@ constexpr allocator_arg_t allocator_arg = allocator_arg_t();
 template <class _Tp, class _Alloc, class... _Args>
 struct __uses_alloc_ctor_imp {
   using _RawAlloc _LIBCPP_NODEBUG = __remove_cvref_t<_Alloc>;
-  static const bool __ua          = uses_allocator<_Tp, _RawAlloc>::value;
-  static const bool __ic          = is_constructible<_Tp, allocator_arg_t, _Alloc, _Args...>::value;
-  static const int value          = __ua ? 2 - __ic : 0;
+  static constexpr bool __ua      = uses_allocator<__remove_cv_t<_Tp>, _RawAlloc>::value;
+  static constexpr bool __ic_head = is_constructible<_Tp, allocator_arg_t, const _RawAlloc&, _Args...>::value;
+  static constexpr bool __ic_tail = is_constructible<_Tp, _Args..., const _RawAlloc&>::value;
+  static constexpr int value      = __ua ? (__ic_head ? 1 : __ic_tail ? 2 : -1) : 0;
 };
 
 template <class _Tp, class _Alloc, class... _Args>
 struct __uses_alloc_ctor : integral_constant<int, __uses_alloc_ctor_imp<_Tp, _Alloc, _Args...>::value> {};
 
-template <class _Tp, class _Allocator, class... _Args>
-inline _LIBCPP_HIDE_FROM_ABI void
-__user_alloc_construct_impl(integral_constant<int, 0>, _Tp* __storage, const _Allocator&, _Args&&... __args) {
-  new (__storage) _Tp(std::forward<_Args>(__args)...);
-}
-
-// FIXME: This should have a version which takes a non-const alloc.
-template <class _Tp, class _Allocator, class... _Args>
-inline _LIBCPP_HIDE_FROM_ABI void
-__user_alloc_construct_impl(integral_constant<int, 1>, _Tp* __storage, const _Allocator& __a, _Args&&... __args) {
-  new (__storage) _Tp(allocator_arg, __a, std::forward<_Args>(__args)...);
-}
-
-// FIXME: This should have a version which takes a non-const alloc.
-template <class _Tp, class _Allocator, class... _Args>
-inline _LIBCPP_HIDE_FROM_ABI void
-__user_alloc_construct_impl(integral_constant<int, 2>, _Tp* __storage, const _Allocator& __a, _Args&&... __args) {
-  new (__storage) _Tp(std::forward<_Args>(__args)..., __a);
-}
-
 #endif // _LIBCPP_CXX03_LANG
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__memory/uses_allocator_construction.h b/libcxx/include/__memory/uses_allocator_construction.h
index 6733f5cf6fc35..dd8e851e32455 100644
--- a/libcxx/include/__memory/uses_allocator_construction.h
+++ b/libcxx/include/__memory/uses_allocator_construction.h
@@ -16,6 +16,7 @@
 #include <__type_traits/enable_if.h>
 #include <__type_traits/remove_cv.h>
 #include <__utility/declval.h>
+#include <__utility/integer_sequence.h>
 #include <__utility/pair.h>
 #include <__utility/piecewise_construct.h>
 #include <tuple>
@@ -29,6 +30,43 @@ _LIBCPP_PUSH_MACROS
 
 _LIBCPP_BEGIN_NAMESPACE_STD
 
+// TODO: Guard this furtherly with _LIBCPP_STD_VER < 20 once P0591R4 is fully implemented.
+#if !defined(_LIBCPP_CXX03_LANG)
+
+template <class _Alloc, class... _Args, size_t... _Is>
+_LIBCPP_HIDE_FROM_ABI void __transform_tuple_using_allocator_impl(
+    integral_constant<int, -1>, const _Alloc&, tuple<_Args...>&&, __index_sequence<_Is...>) {
+  static_assert(false, "If uses_allocator_v<T, A> is true, T has to be allocator-constructible");
+}
+
+template <class _Alloc, class... _Args, size_t... _Is>
+_LIBCPP_HIDE_FROM_ABI tuple<_Args&&...> __transform_tuple_using_allocator_impl(
+    integral_constant<int, 0>, const _Alloc&, tuple<_Args...>&& __t, __index_sequence<_Is...>) {
+  return tuple<_Args&&...>(std::move(__t));
+}
+
+template <class _Alloc, class... _Args, size_t... _Is>
+_LIBCPP_HIDE_FROM_ABI tuple<allocator_arg_t, const _Alloc&, _Args&&...> __transform_tuple_using_allocator_impl(
+    integral_constant<int, 1>, const _Alloc& __a, tuple<_Args...>&& __t, __index_sequence<_Is...>) {
+  return tuple<allocator_arg_t, const _Alloc&, _Args&&...>(allocator_arg, __a, std::get<_Is>(std::move(__t))...);
+}
+
+template <class _Alloc, class... _Args, size_t... _Is>
+_LIBCPP_HIDE_FROM_ABI tuple<_Args&&..., const _Alloc&> __transform_tuple_using_allocator_impl(
+    integral_constant<int, 2>, const _Alloc& __a, tuple<_Args...>&& __t, __index_sequence<_Is...>) {
+  return tuple<_Args&&..., const _Alloc&>(std::get<_Is>(std::move(__t))..., __a);
+}
+
+template <class _Tp, class _Alloc, class... _Args>
+_LIBCPP_HIDE_FROM_ABI auto __transform_tuple_using_allocator(const _Alloc& __a, tuple<_Args...>&& __t)
+    -> decltype(std::__transform_tuple_using_allocator_impl(
+        __uses_alloc_ctor<_Tp, _Alloc, _Args...>{}, __a, std::move(__t), __make_index_sequence<sizeof...(_Args)>{})) {
+  return std::__transform_tuple_using_allocator_impl(
+      __uses_alloc_ctor<_Tp, _Alloc, _Args...>{}, __a, std::move(__t), __make_index_sequence<sizeof...(_Args)>{});
+}
+
+#endif // !defined(_LIBCPP_CXX03_LANG)
+
 #if _LIBCPP_STD_VER >= 17
 
 template <class _Tp>
diff --git a/libcxx/include/__memory_resource/polymorphic_allocator.h b/libcxx/include/__memory_resource/polymorphic_allocator.h
index 9a351199b5b16..ec38a3469675b 100644
--- a/libcxx/include/__memory_resource/polymorphic_allocator.h
+++ b/libcxx/include/__memory_resource/polymorphic_allocator.h
@@ -14,9 +14,15 @@
 #include <__cstddef/byte.h>
 #include <__cstddef/max_align_t.h>
 #include <__fwd/pair.h>
+#include <__memory/allocator_arg_t.h>
+#include <__memory/uses_allocator.h>
+#include <__memory/uses_allocator_construction.h>
 #include <__memory_resource/memory_resource.h>
 #include <__new/exceptions.h>
 #include <__new/placement_new_delete.h>
+#include <__type_traits/is_constructible.h>
+#include <__type_traits/remove_cv.h>
+#include <__utility/as_const.h>
 #include <__utility/exception_guard.h>
 #include <__utility/piecewise_construct.h>
 #include <limits>
@@ -122,11 +128,18 @@ class _LIBCPP_AVAILABILITY_PMR polymorphic_allocator {
 
   template <class _Tp, class... _Ts>
   _LIBCPP_HIDE_FROM_ABI void construct(_Tp* __p, _Ts&&... __args) {
-    std::__user_alloc_construct_impl(
-        typename __uses_alloc_ctor<_Tp, polymorphic_allocator&, _Ts...>::type(),
-        __p,
-        *this,
-        std::forward<_Ts>(__args)...);
+    if constexpr (!uses_allocator_v<remove_cv_t<_Tp>, polymorphic_allocator>) {
+      static_assert(is_constructible_v<_Tp, _Ts...>,
+                    "If uses_allocator_v<T, polymorphic_allocator> is false, T has to be constructible from arguments");
+      ::new ((void*)__p) _Tp(std::forward<_Ts>(__args)...);
+    } else if constexpr (is_constructible_v<_Tp, allocator_arg_t, const polymorphic_allocator&, _Ts...>) {
+      ::new ((void*)__p) _Tp(allocator_arg, std::as_const(*this), std::forward<_Ts>(__args)...);
+    } else if constexpr (is_constructible_v<_Tp, _Ts..., const polymorphic_allocator&>) {
+      ::new ((void*)__p) _Tp(std::forward<_Ts>(__args)..., std::as_const(*this));
+    } else {
+      static_assert(
+          false, "If uses_allocator_v<T, polymorphic_allocator> is true, T has to be allocator-constructible");
+    }
   }
 
   template <class _T1, class _T2, class... _Args1, class... _Args2>
@@ -134,12 +147,8 @@ class _LIBCPP_AVAILABILITY_PMR polymorphic_allocator {
   construct(pair<_T1, _T2>* __p, piecewise_construct_t, tuple<_Args1...> __x, tuple<_Args2...> __y) {
     ::new ((void*)__p) pair<_T1, _T2>(
         piecewise_construct,
-        __transform_tuple(typename __uses_alloc_ctor< _T1, polymorphic_allocator&, _Args1... >::type(),
-                          std::move(__x),
-                          make_index_sequence<sizeof...(_Args1)>()),
-        __transform_tuple(typename __uses_alloc_ctor< _T2, polymorphic_allocator&, _Args2... >::type(),
-                          std::move(__y),
-                          make_index_sequence<sizeof...(_Args2)>()));
+        std::__transform_tuple_using_allocator<_T1>(*this, std::move(__x)),
+        std::__transform_tuple_using_allocator<_T2>(*this, std::move(__y)));
   }
 
   template <class _T1, class _T2>
@@ -193,26 +202,6 @@ class _LIBCPP_AVAILABILITY_PMR polymorphic_allocator {
 #  endif
 
 private:
-  template <class... _Args, size_t... _Is>
-  _LIBCPP_HIDE_FROM_ABI tuple<_Args&&...>
-  __transform_tuple(integral_constant<int, 0>, tuple<_Args...>&& __t, index_sequence<_Is...>) {
-    return std::forward_as_tuple(std::get<_Is>(std::move(__t))...);
-  }
-
-  template <class... _Args, size_t... _Is>
-  _LIBCPP_HIDE_FROM_ABI tuple<allocator_arg_t const&, polymorphic_allocator&, _Args&&...>
-  __transform_tuple(integral_constant<int, 1>, tuple<_Args...>&& __t, index_sequence<_Is...>) {
-    using _Tup = tuple<allocator_arg_t const&, polymorphic_allocator&, _Args&&...>;
-    return _Tup(allocator_arg, *this, std::get<_Is>(std::move(__t))...);
-  }
-
-  template <class... _Args, size_t... _Is>
-  _LIBCPP_HIDE_FROM_ABI tuple<_Args&&..., polymorphic_allocator&>
-  __transform_tuple(integral_constant<int, 2>, tuple<_Args...>&& __t, index_sequence<_Is...>) {
-    using _Tup = tuple<_Args&&..., polymorphic_allocator&>;
-    return _Tup(std::get<_Is>(std::move(__t))..., *this);
-  }
-
   _LIBCPP_HIDE_FROM_ABI size_t __max_size() const noexcept {
     return numeric_limits<size_t>::max() / sizeof(value_type);
   }
diff --git a/libcxx/include/scoped_allocator b/libcxx/include/scoped_allocator
index 74effc547f3e2..4d1307b08af09 100644
--- a/libcxx/include/scoped_allocator
+++ b/libcxx/include/scoped_allocator
@@ -113,12 +113,15 @@ template <class OuterA1, class OuterA2, class... InnerAllocs>
 #  include <__cxx03/__config>
 #else
 #  include <__config>
+#  include <__memory/allocator_arg_t.h>
 #  include <__memory/allocator_traits.h>
+#  include <__memory/uses_allocator.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>
 #  include <__type_traits/is_constructible.h>
+#  include <__type_traits/remove_cv.h>
 #  include <__type_traits/remove_reference.h>
 #  include <__utility/declval.h>
 #  include <__utility/forward.h>
@@ -421,23 +424,32 @@ public:
 #    else
   template <class _Tp, class... _Args>
   _LIBCPP_HIDE_FROM_ABI void construct(_Tp* __p, _Args&&... __args) {
-    __construct(__uses_alloc_ctor<_Tp, inner_allocator_type&, _Args...>(), __p, std::forward<_Args>(__args)...);
+    using _OM = __outermost<outer_allocator_type>;
+    if constexpr (!uses_allocator<__remove_cv_t<_Tp>, inner_allocator_type>::value) {
+      allocator_traits<typename _OM::type>::construct(_OM()(outer_allocator()), __p, std::forward<_Args>(__args)...);
+    } else if constexpr (is_constructible<_Tp, allocator_arg_t, const inner_allocator_type&, _Args...>::value) {
+      const auto& __inner_alloc = inner_allocator();
+      allocator_traits<typename _OM::type>::construct(
+          _OM()(outer_allocator()), __p, allocator_arg, __inner_alloc, std::forward<_Args>(__args)...);
+    } else if constexpr (is_constructible<_Tp, _Args..., const inner_allocator_type&>::value) {
+      const auto& __inner_alloc = inner_allocator();
+      allocator_traits<typename _OM::type>::construct(
+          _OM()(outer_allocator()), __p, std::forward<_Args>(__args)..., __inner_alloc);
+    } else {
+      static_assert(false, "If uses_allocator_v<T, inner_allocator_type> is true, T has to be allocator-constructible");
+    }
   }
 
   template <class _T1, class _T2, class... _Args1, class... _Args2>
   _LIBCPP_HIDE_FROM_ABI void
   construct(pair<_T1, _T2>* __p, piecewise_construct_t, tuple<_Args1...> __x, tuple<_Args2...> __y) {
-    typedef __outermost<outer_allocator_type> _OM;
+    using _OM = __outermost<outer_allocator_type>;
     allocator_traits<typename _OM::type>::construct(
         _OM()(outer_allocator()),
         __p,
         piecewise_construct,
-        __transform_tuple(typename __uses_alloc_ctor< _T1, inner_allocator_type&, _Args1... >::type(),
-                          std::move(__x),
-                          __make_index_sequence<sizeof...(_Args1)>()),
-        __transform_tuple(typename __uses_alloc_ctor< _T2, inner_allocator_type&, _Args2... >::type(),
-                          std::move(__y),
-                          __make_index_sequence<sizeof...(_Args2)>()));
+        std::__transform_tuple_using_allocator<_T1>(inner_allocator(), std::move(__x)),
+        std::__transform_tuple_using_allocator<_T2>(inner_allocator(), std::move(__y)));
   }
 
   template <class _T1, class _T2>
@@ -481,46 +493,6 @@ private:
   _LIBCPP_HIDE_FROM_ABI explicit scoped_allocator_adaptor(
       outer_allocator_type&& __o, inner_allocator_type&& __i) _NOEXCEPT : _Base(std::move(__o), std::move(__i)) {}
 
-  template <class _Tp, class... _Args>
-  _LIBCPP_HIDE_FROM_ABI void __construct(integral_constant<int, 0>, _Tp* __p, _Args&&... __args) {
-    typedef __outermost<outer_allocator_type> _OM;
-    allocator_traits<typename _OM::type>::construct(_OM()(outer_allocator()), __p, std::forward<_Args>(__args)...);
-  }
-
-  template <class _Tp, class... _Args>
-  _LIBCPP_HIDE_FROM_ABI void __construct(integral_constant<int, 1>, _Tp* __p, _Args&&... __args) {
-    typedef __outermost<outer_allocator_type> _OM;
-    allocator_traits<typename _OM::type>::construct(
-        _OM()(outer_allocator()), __p, allocator_arg, inner_allocator(), std::forward<_Args>(__args)...);
-  }
-
-  template <class _Tp, class... _Args>
-  _LIBCPP_HIDE_FROM_ABI void __construct(integral_constant<int, 2>, _Tp* __p, _Args&&... __args) {
-    typedef __outermost<outer_allocator_type> _OM;
-    allocator_traits<typename _OM::type>::construct(
-        _OM()(outer_allocator()), __p, std::forward<_Args>(__args)..., inner_allocator());
-  }
-
-  template <class... _Args, size_t... _Idx>
-  _LIBCPP_HIDE_FROM_ABI tuple<_Args&&...>
-  __transform_tuple(integral_constant<int, 0>, tuple<_Args...>&& __t, __index_sequence<_Idx...>) {
-    return std::forward_as_tuple(std::get<_Idx>(std::move(__t))...);
-  }
-
-  template <class... _Args, size_t... _Idx>
-  _LIBCPP_HIDE_FROM_ABI tuple<allocator_arg_t, inner_allocator_type&, _Args&&...>
-  __transform_tuple(integral_constant<int, 1>, tuple<_Args...>&& __t, __index_sequence<_Idx...>) {
-    using _Tup = tuple<allocator_arg_t, inner_allocator_type&, _Args&&...>;
-    return _Tup(allocator_arg, inner_allocator(), std::get<_Idx>(std::move(__t))...);
-  }
-
-  template <class... _Args, size_t... _Idx>
-  _LIBCPP_HIDE_FROM_ABI tuple<_Args&&..., inner_allocator_type&>
-  __transform_tuple(integral_constant<int, 2>, tuple<_Args...>&& __t, __index_sequence<_Idx...>) {
-    using _Tup = tuple<_Args&&..., inner_allocator_type&>;
-    return _Tup(std::get<_Idx>(std::move(__t))..., inner_allocator());
-  }
-
   template <class...>
   friend class __scoped_allocator_storage;
 };
diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index be30ab5b2173d..27c13fa63e0a9 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -382,6 +382,11 @@ public:
     static_assert(!is_reference<_Hp>::value, "Attempted to default construct a reference element in a tuple");
   }
 
+  template <class _Alloc, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI __tuple_leaf(integral_constant<int, -1>, const _Alloc&, _Args&&...) {
+    static_assert(false, "If uses_allocator_v<T, A> is true, T has to be allocator-constructible");
+  }
+
   template <class _Alloc>
   _LIBCPP_HIDE_FROM_ABI constexpr __tuple_leaf(integral_constant<int, 0>, const _Alloc&) : __value_() {
     static_assert(!is_reference<_Hp>::value, "Attempted to default construct a reference element in a tuple");
@@ -456,6 +461,11 @@ public:
 
   _LIBCPP_HIDE_FROM_ABI constexpr __tuple_leaf() noexcept(is_nothrow_default_constructible<_Hp>::value) {}
 
+  template <class _Alloc, class... _Args>
+  _LIBCPP_HIDE_FROM_ABI __tuple_leaf(integral_constant<int, -1>, const _Alloc&, _Args&&...) {
+    static_assert(false, "If uses_allocator_v<T, A> is true, T has to be allocator-constructible");
+  }
+
   template <class _Alloc>
   _LIBCPP_HIDE_FROM_ABI constexpr __tuple_leaf(integral_constant<int, 0>, const _Alloc&) {}
 
diff --git a/libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair.pass.cpp b/libcxx/test/std/utilities/allocator.adaptor/allocator.adaptor.members/construct_pair.pass.cpp
index eac0452c04825..add6d419bf35e 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
@@ -44,15 +44,9 @@ 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&&,
+    assert((P.checkConstruct<piecewise_construct_ref_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);
   }
@@ -71,15 +65,8 @@ 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
+    assert((P.checkConstruct<piecewise_construct_ref_t, std::tuple<std::allocator_arg_t, const SA&>&&, std::tuple<>&&>(
+        CA, ptr)));
     A.destroy(ptr);
     std::free(ptr);
   }
@@ -108,15 +95,9 @@ 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&&,
+    assert((POuter.checkConstruct<piecewise_construct_ref_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);
   }
@@ -140,15 +121,9 @@ 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&&,
+    assert((POuter.checkConstruct<piecewise_construct_ref_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 6fbbea68afbbc..77d0a9e987d65 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
@@ -48,15 +48,9 @@ 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&&,
+    assert((P.checkConstruct<piecewise_construct_ref_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);
   }
@@ -79,15 +73,9 @@ 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&&,
+    assert((P.checkConstruct<piecewise_construct_ref_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);
   }
@@ -120,15 +108,9 @@ 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&&,
+    assert((POuter.checkConstruct<piecewise_construct_ref_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);
   }
@@ -156,15 +138,9 @@ 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&&,
+    assert((POuter.checkConstruct<piecewise_construct_ref_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 79cb05ebee049..2db607070ae9e 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
@@ -47,15 +47,9 @@ void test_no_inner_alloc() {
     A.construct(ptr, std::piecewise_construct, std::forward_as_tuple(x), 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&&,
+    assert((P.checkConstruct<piecewise_construct_ref_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);
   }
@@ -76,15 +70,9 @@ void test_no_inner_alloc() {
     A.construct(ptr, std::piecewise_construct, std::forward_as_tuple(std::move(x)), 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&&,
+    assert((P.checkConstruct<piecewise_construct_ref_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,15 +103,9 @@ void test_with_inner_alloc() {
     A.construct(ptr, std::piecewise_construct, std::forward_as_tuple(x), 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&&,
+    assert((POuter.checkConstruct<piecewise_construct_ref_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);
   }
@@ -150,15 +132,9 @@ void test_with_inner_alloc() {
         ptr, std::piecewise_construct, std::forward_as_tuple(std::move(x)), 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&&,
+    assert((POuter.checkConstruct<piecewise_construct_ref_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 f6bd83550d271..eafe3eddbb523 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
@@ -48,15 +48,9 @@ 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&&,
+    assert((P.checkConstruct<piecewise_construct_ref_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);
   }
@@ -79,15 +73,9 @@ 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&&,
+    assert((P.checkConstruct<piecewise_construct_ref_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);
   }
@@ -120,15 +108,9 @@ 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&&,
+    assert((POuter.checkConstruct<piecewise_construct_ref_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);
   }
@@ -156,15 +138,9 @@ 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&&,
+    assert((POuter.checkConstruct<piecewise_construct_ref_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 66c3259072e64..3c9f8b8e7a407 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
@@ -46,15 +46,9 @@ 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&&,
+    assert((P.checkConstruct<piecewise_construct_ref_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);
   }
@@ -75,15 +69,9 @@ 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&&,
+    assert((P.checkConstruct<piecewise_construct_ref_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,15 +102,9 @@ 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&&,
+    assert((POuter.checkConstruct<piecewise_construct_ref_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);
   }
@@ -148,15 +130,9 @@ 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&&,
+    assert((POuter.checkConstruct<piecewise_construct_ref_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 0ae3e6715606f..9022c1b5d9d8f 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
@@ -59,10 +59,11 @@ void test_bullet_one() {
   POuter.reset();
 }
 
+// `const` is added per LWG3187
 // Otherwise, if uses_allocator_v<T, inner_allocator_type> is true and
-// is_constructible_v<T, allocator_arg_t, inner_allocator_type&, Args...> is
+// is_constructible_v<T, allocator_arg_t, const inner_allocator_type&, Args...> is
 // true, calls OUTERMOST_ALLOC_TRAITS(*this)::construct(OUTERMOST (*this), p,
-//     allocator_arg, inner_allocator(), std::forward<Args>(args)...).
+//     allocator_arg, as_const(inner_allocator()), std::forward<Args>(args)...).
 void test_bullet_two() {
   using VoidAlloc2 = CountingAllocator<void, 2>;
 
@@ -83,13 +84,8 @@ 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
+    assert(
+        (POuter.checkConstruct<allocator_arg_ref_t, const SA::inner_allocator_type&, int&, int const&, int&&>(O, ptr)));
     A.destroy(ptr);
     ::operator delete((void*)ptr);
   }
@@ -97,10 +93,11 @@ void test_bullet_two() {
   POuter.reset();
 }
 
+// `const` is added per LWG3187
 // Otherwise, if uses_allocator_v<T, inner_allocator_type> is true and
-// is_constructible_v<T, Args..., inner_allocator_type&> is true, calls
+// is_constructible_v<T, Args..., const inner_allocator_type&> is true, calls
 // OUTERMOST_ALLOC_TRAITS(*this)::construct(OUTERMOST (*this), p,
-//   std::forward<Args>(args)..., inner_allocator()).
+//   std::forward<Args>(args)..., as_const(inner_allocator())).
 void test_bullet_three() {
   using VoidAlloc2 = CountingAllocator<void, 2>;
 
@@ -121,11 +118,7 @@ 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
+    assert((POuter.checkConstruct<int&, int const&, int&&, const SA::inner_allocator_type&>(O, ptr)));
     A.destroy(ptr);
     ::operator delete((void*)ptr);
   }
diff --git a/libcxx/test/std/utilities/utility/mem.res/mem.poly.allocator.class/mem.poly.allocator.mem/construct_piecewise_pair_evil.pass.cpp b/libcxx/test/std/utilities/utility/mem.res/mem.poly.allocator.class/mem.poly.allocator.mem/construct_piecewise_pair_evil.pass.cpp
index 701381fde5adb..b74e5ec879621 100644
--- a/libcxx/test/std/utilities/utility/mem.res/mem.poly.allocator.class/mem.poly.allocator.mem/construct_piecewise_pair_evil.pass.cpp
+++ b/libcxx/test/std/utilities/utility/mem.res/mem.poly.allocator.class/mem.poly.allocator.mem/construct_piecewise_pair_evil.pass.cpp
@@ -30,10 +30,10 @@ template <class T>
 struct EvilAlloc {
   explicit EvilAlloc() : inner_(std::pmr::null_memory_resource()) {}
 
-  EvilAlloc(std::pmr::polymorphic_allocator<T>& a) : inner_(a) {}
+  EvilAlloc(std::pmr::polymorphic_allocator<T>&) = delete;
   EvilAlloc(std::pmr::polymorphic_allocator<T>&& a) : inner_(a) {}
-  EvilAlloc(std::pmr::polymorphic_allocator<T> const& a)  = delete;
-  EvilAlloc(std::pmr::polymorphic_allocator<T> const&& a) = delete;
+  EvilAlloc(std::pmr::polymorphic_allocator<T> const& a) : inner_(a) {}
+  EvilAlloc(std::pmr::polymorphic_allocator<T> const&&) = delete;
 
   using value_type = T;
   template <class U>
diff --git a/libcxx/test/support/uses_alloc_types.h b/libcxx/test/support/uses_alloc_types.h
index 66746960fc87c..6f2f971d02424 100644
--- a/libcxx/test/support/uses_alloc_types.h
+++ b/libcxx/test/support/uses_alloc_types.h
@@ -12,6 +12,7 @@
 #include <cassert>
 #include <cstdlib>
 #include <memory>
+#include <utility>
 
 #include "test_macros.h"
 #include "test_workarounds.h"
@@ -335,4 +336,19 @@ class NotUsesAllocator : public UsesAllocatorTestBase<NotUsesAllocator<Alloc, Ar
   NotUsesAllocator(Args&&... args) : Base(AllocLastTag{}, std::forward<Args>(args)...) {}
 };
 
+////////////////////////////////////////////////////////////////////////////////
+
+// P0591R4 changed the uses-allocator construction to pack `allocator_arg` and `piecewise_construct` into
+// a `tuple` by value first, so `allocator_traits<A>::construct` receives `allocator_arg_t&&` and
+// `piecewise_construct_t&&` respectively.
+// Until C++20, `allocator_arg` and `piecewise_construct` were directly passed to `allocator_traits<A>::construct`
+// and thus the latter received `const allocator_arg_t&` and `const piecewise_construct_t&`.
+#if TEST_STD_VER >= 20
+using allocator_arg_ref_t       = allocator_arg_t&&;
+using piecewise_construct_ref_t = piecewise_construct_t&&;
+#else
+using allocator_arg_ref_t       = const allocator_arg_t&;
+using piecewise_construct_ref_t = const piecewise_construct_t&;
+#endif
+
 #endif /* USES_ALLOC_TYPES_H */



More information about the libcxx-commits mailing list