[libcxx-commits] [libcxx] [libc++] LWG 3821 uses_allocator_construction_args should have overload for pair-like (PR #66939)

via libcxx-commits libcxx-commits at lists.llvm.org
Sun Oct 1 15:00:03 PDT 2023


https://github.com/huixie90 updated https://github.com/llvm/llvm-project/pull/66939

>From 4db14f585da9e041a7945854a04cf23ca49cae64 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Wed, 20 Sep 2023 19:41:00 +0100
Subject: [PATCH 1/3] [libc++] LWG 3821 uses_allocator_construction_args should
 have overload for pair-like

---
 libcxx/docs/Status/Cxx23Issues.csv            |  2 +-
 .../__memory/uses_allocator_construction.h    | 62 ++++++++++++++-----
 libcxx/include/__utility/pair.h               |  3 +-
 libcxx/include/memory                         | 37 +++++++++++
 .../uses_allocator_construction_args.pass.cpp | 38 ++++++++++--
 5 files changed, 119 insertions(+), 23 deletions(-)

diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 67ef35ec39b2a81..6ff2a5801d72d0c 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -262,7 +262,7 @@
 "`3742 <https://wg21.link/LWG3742>`__","``deque::prepend_range`` needs to permute","February 2023","","","|ranges|"
 "`3790 <https://wg21.link/LWG3790>`__","`P1467 <https://wg21.link/P1467>`__ accidentally changed ``nexttoward``'s signature","February 2023","","",""
 "`3819 <https://wg21.link/LWG3819>`__","``reference_meows_from_temporary`` should not use ``is_meowible``","February 2023","","",""
-"`3821 <https://wg21.link/LWG3821>`__","``uses_allocator_construction_args`` should have overload for ``pair-like``","February 2023","","",""
+"`3821 <https://wg21.link/LWG3821>`__","``uses_allocator_construction_args`` should have overload for ``pair-like``","February 2023","|Complete|","18.0.0",""
 "`3834 <https://wg21.link/LWG3834>`__","Missing ``constexpr`` for ``std::intmax_t`` math functions in ``<cinttypes>``","February 2023","","",""
 "`3839 <https://wg21.link/LWG3839>`__","``range_formatter``'s ``set_separator``, ``set_brackets``, and ``underlying`` functions should be ``noexcept``","February 2023","|Complete|","17.0","|format|"
 "`3841 <https://wg21.link/LWG3841>`__","``<version>`` should not be ""all freestanding""","February 2023","","",""
diff --git a/libcxx/include/__memory/uses_allocator_construction.h b/libcxx/include/__memory/uses_allocator_construction.h
index a2e4f6e26f4b3af..e4ac89e60379c80 100644
--- a/libcxx/include/__memory/uses_allocator_construction.h
+++ b/libcxx/include/__memory/uses_allocator_construction.h
@@ -12,6 +12,7 @@
 #include <__config>
 #include <__memory/construct_at.h>
 #include <__memory/uses_allocator.h>
+#include <__tuple/pair_like.h>
 #include <__type_traits/enable_if.h>
 #include <__type_traits/is_same.h>
 #include <__type_traits/remove_cv.h>
@@ -36,7 +37,10 @@ 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>
+template <class _Tp>
+inline constexpr bool __is_cv_std_pair = __is_std_pair<remove_cv_t<_Tp>>;
+
+template <class _Type, class _Alloc, class... _Args, __enable_if_t<!__is_cv_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...>) {
@@ -52,7 +56,7 @@ __uses_allocator_construction_args(const _Alloc& __alloc, _Args&&... __args) noe
   }
 }
 
-template <class _Pair, class _Alloc, class _Tuple1, class _Tuple2, __enable_if_t<__is_std_pair<_Pair>, int> = 0>
+template <class _Pair, class _Alloc, class _Tuple1, class _Tuple2, __enable_if_t<__is_cv_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(
@@ -71,12 +75,12 @@ _LIBCPP_HIDE_FROM_ABI constexpr auto __uses_allocator_construction_args(
           std::forward<_Tuple2>(__y)));
 }
 
-template <class _Pair, class _Alloc, __enable_if_t<__is_std_pair<_Pair>, int> = 0>
+template <class _Pair, class _Alloc, __enable_if_t<__is_cv_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>
+template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_cv_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>(
@@ -87,7 +91,7 @@ __uses_allocator_construction_args(const _Alloc& __alloc, _Up&& __u, _Vp&& __v)
 }
 
 #  if _LIBCPP_STD_VER >= 23
-template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_std_pair<_Pair>, int> = 0>
+template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_cv_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>(
@@ -95,14 +99,14 @@ __uses_allocator_construction_args(const _Alloc& __alloc, pair<_Up, _Vp>& __pair
 }
 #  endif
 
-template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_std_pair<_Pair>, int> = 0>
+template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_cv_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>
+template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_cv_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>(
@@ -113,7 +117,7 @@ __uses_allocator_construction_args(const _Alloc& __alloc, pair<_Up, _Vp>&& __pai
 }
 
 #  if _LIBCPP_STD_VER >= 23
-template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_std_pair<_Pair>, int> = 0>
+template <class _Pair, class _Alloc, class _Up, class _Vp, __enable_if_t<__is_cv_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>(
@@ -122,6 +126,20 @@ __uses_allocator_construction_args(const _Alloc& __alloc, const pair<_Up, _Vp>&&
       std::forward_as_tuple(std::get<0>(std::move(__pair))),
       std::forward_as_tuple(std::get<1>(std::move(__pair))));
 }
+
+template < class _Pair,
+           class _Alloc,
+           __pair_like _PairLike,
+           __enable_if_t<__is_cv_std_pair<_Pair> && !__is_specialization_of_subrange<remove_cvref_t<_PairLike>>::value,
+                         int> = 0>
+_LIBCPP_HIDE_FROM_ABI constexpr auto
+__uses_allocator_construction_args(const _Alloc& __alloc, _PairLike&& __p) noexcept {
+  return std::__uses_allocator_construction_args<_Pair>(
+      __alloc,
+      piecewise_construct,
+      std::forward_as_tuple(std::get<0>(std::forward<_PairLike>(__p))),
+      std::forward_as_tuple(std::get<1>(std::forward<_PairLike>(__p))));
+}
 #  endif
 
 namespace __uses_allocator_detail {
@@ -139,23 +157,33 @@ template <class _Tp>
 inline constexpr bool __convertible_to_const_pair_ref =
     decltype(__uses_allocator_detail::__convertible_to_const_pair_ref_impl<_Tp>(0))::value;
 
+#  if _LIBCPP_STD_VER >= 23
+template <class _Tp, class _Up>
+inline constexpr bool __uses_allocator_constraints =
+    __is_cv_std_pair<_Tp> &&
+    (__is_specialization_of_subrange<remove_cvref_t<_Up>>::value ||
+     (!__pair_like<_Up> && !__convertible_to_const_pair_ref<_Up>));
+#  else
+template <class _Tp, class _Up>
+inline constexpr bool __uses_allocator_constraints = __is_cv_std_pair<_Tp> && !__convertible_to_const_pair_ref<_Up>;
+#  endif
+
 } // 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>
+template < class _Pair,
+           class _Alloc,
+           class _Type,
+           __enable_if_t<__uses_allocator_detail::__uses_allocator_constraints<_Pair, _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>>
+template < class _Pair,
+           class _Alloc,
+           class _Type,
+           __enable_if_t< __uses_allocator_detail::__uses_allocator_constraints<_Pair, _Type>, int>>
 _LIBCPP_HIDE_FROM_ABI constexpr auto
 __uses_allocator_construction_args(const _Alloc& __alloc, _Type&& __value) noexcept {
   struct __pair_constructor {
diff --git a/libcxx/include/__utility/pair.h b/libcxx/include/__utility/pair.h
index 62dac6dd1da3f02..535344eb1e2d63f 100644
--- a/libcxx/include/__utility/pair.h
+++ b/libcxx/include/__utility/pair.h
@@ -284,7 +284,8 @@ struct _LIBCPP_TEMPLATE_VIS pair
     }
 
     template <__pair_like _PairLike>
-      requires(is_constructible_v<first_type, decltype(std::get<0>(std::declval<_PairLike&&>()))> &&
+      requires(!__is_specialization_of_subrange<remove_cvref_t<_PairLike>>::value &&
+               is_constructible_v<first_type, decltype(std::get<0>(std::declval<_PairLike&&>()))> &&
                is_constructible_v<second_type, decltype(std::get<1>(std::declval<_PairLike&&>()))>)
     _LIBCPP_HIDE_FROM_ABI constexpr explicit(__pair_like_explicit_wknd<_PairLike>())
         pair(_PairLike&& __p)
diff --git a/libcxx/include/memory b/libcxx/include/memory
index cd6bcc7eaa35d76..f8f7a62951f15e3 100644
--- a/libcxx/include/memory
+++ b/libcxx/include/memory
@@ -867,6 +867,43 @@ template <class T> struct hash<shared_ptr<T> >;
 template <class T, class Alloc>
   inline constexpr bool uses_allocator_v = uses_allocator<T, Alloc>::value;
 
+// [allocator.uses.construction], uses-allocator construction
+template<class T, class Alloc, class... Args>
+  constexpr auto uses_allocator_construction_args(const Alloc& alloc,             // freestanding, since C++20
+                                                  Args&&... args) noexcept;
+template<class T, class Alloc, class Tuple1, class Tuple2>
+  constexpr auto uses_allocator_construction_args(const Alloc& alloc,             // freestanding, since C++20
+                                                  piecewise_construct_t,
+                                                  Tuple1&& x, Tuple2&& y) noexcept;
+template<class T, class Alloc>
+  constexpr auto uses_allocator_construction_args(const Alloc& alloc) noexcept;   // freestanding, since C++20
+template<class T, class Alloc, class U, class V>
+  constexpr auto uses_allocator_construction_args(const Alloc& alloc,             // freestanding, since C++20
+                                                  U&& u, V&& v) noexcept;
+template<class T, class Alloc, class U, class V>
+  constexpr auto uses_allocator_construction_args(const Alloc& alloc,             // freestanding, since C++23
+                                                  pair<U, V>& pr) noexcept;
+template<class T, class Alloc, class U, class V>
+  constexpr auto uses_allocator_construction_args(const Alloc& alloc,             // freestanding, since C++20
+                                                  const pair<U, V>& pr) noexcept;
+template<class T, class Alloc, class U, class V>
+  constexpr auto uses_allocator_construction_args(const Alloc& alloc,             // freestanding, since C++20
+                                                  pair<U, V>&& pr) noexcept;
+template<class T, class Alloc, class U, class V>
+  constexpr auto uses_allocator_construction_args(const Alloc& alloc,             // freestanding, since C++23
+                                                  const pair<U, V>&& pr) noexcept;
+template<class T, class Alloc, pair-like P>
+  constexpr auto uses_allocator_construction_args(const Alloc& alloc,             // freestanding, since C++20
+                                                  P&& p) noexcept;
+template<class T, class Alloc, class U>
+  constexpr auto uses_allocator_construction_args(const Alloc& alloc,             // freestanding, since C++20
+                                                  U&& u) noexcept;
+template<class T, class Alloc, class... Args>
+  constexpr T make_obj_using_allocator(const Alloc& alloc, Args&&... args);       // freestanding, since C++20
+template<class T, class Alloc, class... Args>
+  constexpr T* uninitialized_construct_using_allocator(T* p,                      // freestanding, since C++20
+                                                         const Alloc& alloc, Args&&... args);
+
 // [ptr.align]
 void* align(size_t alignment, size_t size, void*& ptr, size_t& space);
 
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
index 739278e74ddab4f..0ad5e12e8e1c115 100644
--- 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
@@ -16,6 +16,7 @@
 
 #include <concepts>
 #include <memory>
+#include <ranges>
 #include <tuple>
 #include <utility>
 
@@ -132,19 +133,48 @@ constexpr bool test() {
   }
 #endif
   {
-    ConvertibleToPair ctp {};
-    auto ret = test_uses_allocator_construction_args<std::pair<int, int>>(a, ctp);
+    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));
+    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);
   }
+#if TEST_STD_VER >= 23
+  // LWG 3821
+  // uses_allocator_construction_args should have overload for pair-like
+  {
+    // pair-like with explicit ctr should work
+    struct Foo {
+      int i = 5;
+    };
+    struct Bar {
+      int i;
+      constexpr explicit Bar(Foo foo) : i(foo.i) {}
+    };
+
+    std::tuple<Foo, Foo> pair_like;
+    auto ret  = test_uses_allocator_construction_args<std::pair<Bar, Bar>>(a, pair_like);
+    auto pair = std::make_from_tuple<std::pair<Bar, Bar>>(std::move(ret));
+    assert(pair.first.i == 5);
+    assert(pair.second.i == 5);
+  }
+  {
+    // subrange should work
+    int i = 5;
+    std::ranges::subrange<int*, int*> r{&i, &i};
+    auto ret  = std::__uses_allocator_construction_args<std::pair<int*, int*>>(a, r);
+    auto pair = std::make_from_tuple<std::pair<int*, int*>>(std::move(ret));
+    assert(pair.first == &i);
+    assert(pair.second == &i);
+  }
+#endif
 
   return true;
 }

>From 9d6bfe0822c8f1e9372d65b0c36e91334a9eb83b Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Thu, 21 Sep 2023 22:32:28 +0100
Subject: [PATCH 2/3] remove freestanding

---
 libcxx/include/memory | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/memory b/libcxx/include/memory
index f8f7a62951f15e3..24ba82f43ddd309 100644
--- a/libcxx/include/memory
+++ b/libcxx/include/memory
@@ -869,39 +869,39 @@ template <class T, class Alloc>
 
 // [allocator.uses.construction], uses-allocator construction
 template<class T, class Alloc, class... Args>
-  constexpr auto uses_allocator_construction_args(const Alloc& alloc,             // freestanding, since C++20
+  constexpr auto uses_allocator_construction_args(const Alloc& alloc,             // since C++20
                                                   Args&&... args) noexcept;
 template<class T, class Alloc, class Tuple1, class Tuple2>
-  constexpr auto uses_allocator_construction_args(const Alloc& alloc,             // freestanding, since C++20
+  constexpr auto uses_allocator_construction_args(const Alloc& alloc,             // since C++20
                                                   piecewise_construct_t,
                                                   Tuple1&& x, Tuple2&& y) noexcept;
 template<class T, class Alloc>
-  constexpr auto uses_allocator_construction_args(const Alloc& alloc) noexcept;   // freestanding, since C++20
+  constexpr auto uses_allocator_construction_args(const Alloc& alloc) noexcept;   // since C++20
 template<class T, class Alloc, class U, class V>
-  constexpr auto uses_allocator_construction_args(const Alloc& alloc,             // freestanding, since C++20
+  constexpr auto uses_allocator_construction_args(const Alloc& alloc,             // since C++20
                                                   U&& u, V&& v) noexcept;
 template<class T, class Alloc, class U, class V>
-  constexpr auto uses_allocator_construction_args(const Alloc& alloc,             // freestanding, since C++23
+  constexpr auto uses_allocator_construction_args(const Alloc& alloc,             // since C++23
                                                   pair<U, V>& pr) noexcept;
 template<class T, class Alloc, class U, class V>
-  constexpr auto uses_allocator_construction_args(const Alloc& alloc,             // freestanding, since C++20
+  constexpr auto uses_allocator_construction_args(const Alloc& alloc,             // since C++20
                                                   const pair<U, V>& pr) noexcept;
 template<class T, class Alloc, class U, class V>
-  constexpr auto uses_allocator_construction_args(const Alloc& alloc,             // freestanding, since C++20
+  constexpr auto uses_allocator_construction_args(const Alloc& alloc,             // since C++20
                                                   pair<U, V>&& pr) noexcept;
 template<class T, class Alloc, class U, class V>
-  constexpr auto uses_allocator_construction_args(const Alloc& alloc,             // freestanding, since C++23
+  constexpr auto uses_allocator_construction_args(const Alloc& alloc,             // since C++23
                                                   const pair<U, V>&& pr) noexcept;
 template<class T, class Alloc, pair-like P>
-  constexpr auto uses_allocator_construction_args(const Alloc& alloc,             // freestanding, since C++20
+  constexpr auto uses_allocator_construction_args(const Alloc& alloc,             // since C++20
                                                   P&& p) noexcept;
 template<class T, class Alloc, class U>
-  constexpr auto uses_allocator_construction_args(const Alloc& alloc,             // freestanding, since C++20
+  constexpr auto uses_allocator_construction_args(const Alloc& alloc,             // since C++20
                                                   U&& u) noexcept;
 template<class T, class Alloc, class... Args>
-  constexpr T make_obj_using_allocator(const Alloc& alloc, Args&&... args);       // freestanding, since C++20
+  constexpr T make_obj_using_allocator(const Alloc& alloc, Args&&... args);       // since C++20
 template<class T, class Alloc, class... Args>
-  constexpr T* uninitialized_construct_using_allocator(T* p,                      // freestanding, since C++20
+  constexpr T* uninitialized_construct_using_allocator(T* p,                      // since C++20
                                                          const Alloc& alloc, Args&&... args);
 
 // [ptr.align]

>From be33b767b5635d4461325218ef636efe2dfc5e55 Mon Sep 17 00:00:00 2001
From: Hui <hui.xie0621 at gmail.com>
Date: Sun, 1 Oct 2023 22:59:18 +0100
Subject: [PATCH 3/3] address feedback

---
 libcxx/docs/Status/Cxx23Issues.csv            |  2 +-
 .../__memory/uses_allocator_construction.h    |  7 +-
 .../operator.pair_like.pass.cpp               | 80 +++++++++++++++++++
 .../uses_allocator_construction_args.pass.cpp | 46 +++++++----
 .../pairs/pairs.pair/ctor.pair_like.pass.cpp  | 24 ++----
 5 files changed, 121 insertions(+), 38 deletions(-)
 create mode 100644 libcxx/test/std/ranges/range.utility/range.subrange/operator.pair_like.pass.cpp

diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index 6ff2a5801d72d0c..8be1ff588cf3b5c 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -198,7 +198,7 @@
 "`3629 <https://wg21.link/LWG3629>`__","``make_error_code`` and ``make_error_condition`` are customization points","November 2022","|Complete|","16.0",""
 "`3636 <https://wg21.link/LWG3636>`__","``formatter<T>::format`` should be ``const``-qualified","November 2022","|Complete|","16.0","|format|"
 "`3646 <https://wg21.link/LWG3646>`__","``std::ranges::view_interface::size`` returns a signed type","November 2022","|Complete|","16.0","|ranges|"
-"`3677 <https://wg21.link/LWG3677>`__","Is a cv-qualified ``pair`` specially handled in uses-allocator construction?", "November 2022","","",""
+"`3677 <https://wg21.link/LWG3677>`__","Is a cv-qualified ``pair`` specially handled in uses-allocator construction?", "November 2022","|Complete|","18.0",""
 "`3717 <https://wg21.link/LWG3717>`__","``common_view::end`` should improve ``random_access_range`` case", "November 2022","","","|ranges|"
 "`3732 <https://wg21.link/LWG3732>`__","``prepend_range`` and ``append_range`` can't be amortized constant time", "November 2022","|Nothing to do|","","|ranges|"
 "`3736 <https://wg21.link/LWG3736>`__","``move_iterator`` missing ``disable_sized_sentinel_for`` specialization", "November 2022","","","|ranges|"
diff --git a/libcxx/include/__memory/uses_allocator_construction.h b/libcxx/include/__memory/uses_allocator_construction.h
index e4ac89e60379c80..71ae5bcd323315b 100644
--- a/libcxx/include/__memory/uses_allocator_construction.h
+++ b/libcxx/include/__memory/uses_allocator_construction.h
@@ -43,12 +43,13 @@ inline constexpr bool __is_cv_std_pair = __is_std_pair<remove_cv_t<_Tp>>;
 template <class _Type, class _Alloc, class... _Args, __enable_if_t<!__is_cv_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...>) {
+  if constexpr (!uses_allocator_v<remove_cv_t<_Type>, _Alloc> && is_constructible_v<_Type, _Args...>) {
     return std::forward_as_tuple(std::forward<_Args>(__args)...);
-  } else if constexpr (uses_allocator_v<_Type, _Alloc> &&
+  } else if constexpr (uses_allocator_v<remove_cv_t<_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&>) {
+  } else if constexpr (uses_allocator_v<remove_cv_t<_Type>, _Alloc> &&
+                       is_constructible_v<_Type, _Args..., const _Alloc&>) {
     return std::forward_as_tuple(std::forward<_Args>(__args)..., __alloc);
   } else {
     static_assert(
diff --git a/libcxx/test/std/ranges/range.utility/range.subrange/operator.pair_like.pass.cpp b/libcxx/test/std/ranges/range.utility/range.subrange/operator.pair_like.pass.cpp
new file mode 100644
index 000000000000000..1d0dfd05b3f7e2b
--- /dev/null
+++ b/libcxx/test/std/ranges/range.utility/range.subrange/operator.pair_like.pass.cpp
@@ -0,0 +1,80 @@
+
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// template<different-from<subrange> PairLike>
+//   requires pair-like-convertible-from<PairLike, const I&, const S&>
+// constexpr operator PairLike() const;
+
+#include <cassert>
+#include <concepts>
+#include <ranges>
+
+#include "test_macros.h"
+
+static_assert(std::convertible_to<std::ranges::subrange<int*>, std::pair<int*, int*>>);
+static_assert(std::convertible_to<std::ranges::subrange<int*>, std::tuple<int*, int*>>);
+static_assert(!std::convertible_to<std::ranges::subrange<int*>, std::pair<long*, int*>>);
+static_assert(!std::convertible_to<std::ranges::subrange<int*>, std::pair<int*, long*>>);
+static_assert(!std::convertible_to<std::ranges::subrange<int*>, std::pair<long*, long*>>);
+static_assert(!std::convertible_to<std::ranges::subrange<int*>, std::array<int*, 2>>);
+
+constexpr bool test() {
+  // Check to std::pair
+  {
+    int data[] = {1, 2, 3, 4, 5};
+    const std::ranges::subrange a(data);
+    {
+      std::pair<int*, int*> p(a);
+      assert(p.first == data + 0);
+      assert(p.second == data + 5);
+    }
+    {
+      std::pair<int*, int*> p{a};
+      assert(p.first == data + 0);
+      assert(p.second == data + 5);
+    }
+    {
+      std::pair<int*, int*> p = a;
+      assert(p.first == data + 0);
+      assert(p.second == data + 5);
+    }
+  }
+
+  // Check to std::tuple
+  {
+    int data[] = {1, 2, 3, 4, 5};
+    const std::ranges::subrange a(data);
+    {
+      std::tuple<int*, int*> p(a);
+      assert(std::get<0>(p) == data + 0);
+      assert(std::get<1>(p) == data + 5);
+    }
+    {
+      std::tuple<int*, int*> p{a};
+      assert(std::get<0>(p) == data + 0);
+      assert(std::get<1>(p) == data + 5);
+    }
+    {
+      std::tuple<int*, int*> p = a;
+      assert(std::get<0>(p) == data + 0);
+      assert(std::get<1>(p) == data + 5);
+    }
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+
+  return 0;
+}
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
index 0ad5e12e8e1c115..ab3c039497dd955 100644
--- 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
@@ -29,25 +29,27 @@ constexpr decltype(auto) test_uses_allocator_construction_args(Args&&... args) {
   return std::uses_allocator_construction_args<Type>(std::forward<Args>(args)...);
 }
 
-constexpr bool test() {
+template <template <class> class CV>
+constexpr void testOne() {
   Alloc a(12);
   {
     std::same_as<std::tuple<std::allocator_arg_t, const Alloc&>> auto ret =
-        test_uses_allocator_construction_args<UsesAllocArgT>(a);
+        test_uses_allocator_construction_args<CV<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);
+    std::same_as<std::tuple<const Alloc&>> auto ret = test_uses_allocator_construction_args<CV<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);
+    [[maybe_unused]] std::same_as<std::tuple<>> auto ret =
+        test_uses_allocator_construction_args<CV<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>>(
+        test_uses_allocator_construction_args<CV<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);
@@ -56,7 +58,7 @@ constexpr bool test() {
     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);
+        test_uses_allocator_construction_args<CV<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);
   }
@@ -65,7 +67,7 @@ constexpr bool test() {
     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);
+        test_uses_allocator_construction_args<CV<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);
@@ -76,7 +78,7 @@ constexpr bool test() {
     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>>(
+        test_uses_allocator_construction_args<CV<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);
@@ -90,7 +92,7 @@ constexpr bool test() {
     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);
+        test_uses_allocator_construction_args<CV<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);
@@ -102,7 +104,7 @@ constexpr bool test() {
     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));
+        test_uses_allocator_construction_args<CV<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);
@@ -113,7 +115,7 @@ constexpr bool test() {
     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));
+        test_uses_allocator_construction_args<CV<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);
@@ -125,7 +127,8 @@ constexpr bool test() {
     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)));
+        test_uses_allocator_construction_args<CV<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);
@@ -134,20 +137,20 @@ constexpr bool test() {
 #endif
   {
     ConvertibleToPair ctp{};
-    auto ret              = test_uses_allocator_construction_args<std::pair<int, int>>(a, ctp);
+    auto ret              = test_uses_allocator_construction_args<CV<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));
+    auto ret              = test_uses_allocator_construction_args<CV<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);
   }
 #if TEST_STD_VER >= 23
-  // LWG 3821
+  // P2165R4 with LWG3821 applied
   // uses_allocator_construction_args should have overload for pair-like
   {
     // pair-like with explicit ctr should work
@@ -160,7 +163,7 @@ constexpr bool test() {
     };
 
     std::tuple<Foo, Foo> pair_like;
-    auto ret  = test_uses_allocator_construction_args<std::pair<Bar, Bar>>(a, pair_like);
+    auto ret  = test_uses_allocator_construction_args<CV<std::pair<Bar, Bar>>>(a, pair_like);
     auto pair = std::make_from_tuple<std::pair<Bar, Bar>>(std::move(ret));
     assert(pair.first.i == 5);
     assert(pair.second.i == 5);
@@ -169,12 +172,21 @@ constexpr bool test() {
     // subrange should work
     int i = 5;
     std::ranges::subrange<int*, int*> r{&i, &i};
-    auto ret  = std::__uses_allocator_construction_args<std::pair<int*, int*>>(a, r);
+    auto ret  = test_uses_allocator_construction_args<CV<std::pair<int*, int*>>>(a, r);
     auto pair = std::make_from_tuple<std::pair<int*, int*>>(std::move(ret));
     assert(pair.first == &i);
     assert(pair.second == &i);
   }
 #endif
+}
+
+constexpr bool test() {
+  testOne<std::type_identity_t>();
+
+  // LWG 3677. Is a cv-qualified pair specially handled in uses-allocator construction
+  testOne<std::add_const_t>();
+  testOne<std::add_volatile_t>();
+  testOne<std::add_cv_t>();
 
   return true;
 }
diff --git a/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor.pair_like.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor.pair_like.pass.cpp
index b93adb0ef3ebc6d..3810c4450e57fd7 100644
--- a/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor.pair_like.pass.cpp
+++ b/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor.pair_like.pass.cpp
@@ -79,25 +79,15 @@ constexpr bool test() {
       static_assert(!std::is_constructible_v<std::pair<int, int>, std::tuple<int, int, int>>); // too large
     }
 
-    // Check from ranges::subrange
+    // Check that the constructor excludes ranges::subrange
     {
       int data[] = {1, 2, 3, 4, 5};
-      std::ranges::subrange a(data);
-      {
-        std::pair<int*, int*> p(a);
-        assert(p.first == data + 0);
-        assert(p.second == data + 5);
-      }
-      {
-        std::pair<int*, int*> p{a};
-        assert(p.first == data + 0);
-        assert(p.second == data + 5);
-      }
-      {
-        std::pair<int*, int*> p = a;
-        assert(p.first == data + 0);
-        assert(p.second == data + 5);
-      }
+      const std::ranges::subrange a(data);
+      // Note the expression below would be ambiguous if pair's
+      // constructor does not exclude subrange
+      std::pair<int*, int*> p = a;
+      assert(p.first == data + 0);
+      assert(p.second == data + 5);
     }
   }
 



More information about the libcxx-commits mailing list