[libcxx-commits] [libcxx] 83ead2b - [libc++] implement "pair" section of P2321R2 `zip`

Hui Xie via libcxx-commits libcxx-commits at lists.llvm.org
Wed Sep 28 03:26:02 PDT 2022


Author: Hui Xie
Date: 2022-09-28T11:24:54+01:00
New Revision: 83ead2bbc5e14ca1beb776c062ebc36c38e8bb1c

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

LOG: [libc++] implement "pair" section of P2321R2 `zip`

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

Added: 
    libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_copy_convert.pass.cpp
    libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_copy_pair.pass.cpp
    libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_move_convert.pass.cpp
    libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_move_pair.pass.cpp
    libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor_pair_U_V_const_move.pass.cpp
    libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor_pair_U_V_ref.pass.cpp
    libcxx/test/std/utilities/utility/pairs/pairs.pair/swap_member_const.pass.cpp
    libcxx/test/std/utilities/utility/pairs/pairs.spec/non_member_const_swap.pass.cpp
    libcxx/test/support/copy_move_types.h

Modified: 
    libcxx/docs/Status/ZipProjects.csv
    libcxx/include/__utility/pair.h
    libcxx/include/utility
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_convert_copy.pass.cpp
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_convert_move.pass.cpp
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_copy.pass.cpp
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_move.pass.cpp
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair_copy.pass.cpp
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair_move.pass.cpp
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_const_move_pair.pass.cpp
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_const_move.pass.cpp
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_non_const_copy.pass.cpp
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_non_const_pair.pass.cpp
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/const_move_pair.pass.cpp
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_const_move.pass.cpp
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_non_const_copy.pass.cpp
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/non_const_pair.pass.cpp

Removed: 
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/types.h
    libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_types.h


################################################################################
diff  --git a/libcxx/docs/Status/ZipProjects.csv b/libcxx/docs/Status/ZipProjects.csv
index 0b345f3d98d59..17c1178f167fb 100644
--- a/libcxx/docs/Status/ZipProjects.csv
+++ b/libcxx/docs/Status/ZipProjects.csv
@@ -1,27 +1,27 @@
 Section,Description,Dependencies,Assignee,Complete
 | `[tuple.syn] <https://wg21.link/tuple.syn>`_, "`[tuple] basic_common_reference, common_type <https://reviews.llvm.org/D116538>`_", None, Nikolas Klauser, |Complete|
-| `[tuple.tuple] <https://wg21.link/tuple.tuple>`_, "`[tuple] constructor, assignment and swap overloads <https://reviews.llvm.org/D116621>`_", None, Nikolas Klauser, |In Progress|
+| `[tuple.tuple] <https://wg21.link/tuple.tuple>`_, "`[tuple] constructor, assignment and swap overloads <https://reviews.llvm.org/D116621>`_", None, Hui Xie, |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", None, Nikolas Klauser, |Not Started|
+| `[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|
-| `[vector.bool] <https://wg21.link/vector.bool>`_, "[vector<bool>::reference] add const operator= overload", None, Nikolas Klauser, |Not Started|
-| `[iterator.concept.winc] <https://wg21.link/iterator.concept.winc>`_, "Update weakly_comparable", None, Unassigned, |Not Started|
+| `[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`
 | `zip_view::sentinel`", Hui Xie, |Complete|
 | `[range.zip.iterator] <https://wg21.link/range.zip.iterator>`_, "`zip_view::iterator <https://reviews.llvm.org/D122806>`_", None, Hui Xie, |Complete|
 | `[range.zip.sentinel] <https://wg21.link/range.zip.sentinel>`_, "`zip_view::sentinel <https://reviews.llvm.org/D122806>`_", None, Hui Xie, |Complete|
 | `[range.zip.transform.view] <https://wg21.link/range.zip.transform.view>`_, "zip_transform_view", "| `zip_transform_view::iterator`
-| `zip_transform_view::sentinel`", Unassigned, |Not Started|
-| `[range.zip.transform.iterator] <https://wg21.link/range.zip.transform.iterator>`_, "zip_transform_view::iterator", None, Unassigned, |Not Started|
-| `[range.zip.transform.sentinel] <https://wg21.link/range.zip.transform.sentinel>`_, "zip_transform_view::sentinel", None, Unassigned, |Not Started|
+| `zip_transform_view::sentinel`", Hui Xie, |Not Started|
+| `[range.zip.transform.iterator] <https://wg21.link/range.zip.transform.iterator>`_, "zip_transform_view::iterator", None, Hui Xie, |Not Started|
+| `[range.zip.transform.sentinel] <https://wg21.link/range.zip.transform.sentinel>`_, "zip_transform_view::sentinel", None, Hui Xie, |Not Started|
 | `[range.adjacent.view] <https://wg21.link/range.adjacent.view>`_, "adjacent_view", "| `adjacent_view::iterator`
-| `adjacent_view::sentinel`", Unassigned, |Not Started|
-| `[range.adjacent.iterator] <https://wg21.link/range.adjacent.iterator>`_, "adjacent_view::iterator", None, Unassigned, |Not Started|
-| `[range.adjacent.sentinel] <https://wg21.link/range.adjacent.sentinel>`_, "adjacent_view::sentinel", None, Unassigned, |Not Started|
+| `adjacent_view::sentinel`", Hui Xie, |Not Started|
+| `[range.adjacent.iterator] <https://wg21.link/range.adjacent.iterator>`_, "adjacent_view::iterator", None, unassigned, |Not Started|
+| `[range.adjacent.sentinel] <https://wg21.link/range.adjacent.sentinel>`_, "adjacent_view::sentinel", None, unassigned, |Not Started|
 | `[range.adjacent.transform.view] <https://wg21.link/range.adjacent.transform.view>`_, "adjacent_transform_view", "| `adjacent_transform_view::iterator`,
-| `adjacent_transform_view::sentinel`", Unassigned, |Not Started|
-| `[range.adjacent.transform.iterator] <https://wg21.link/range.adjacent.transform.iterator>`_, "adjacent_transform_view::iterator", None, Unassigned, |Not Started|
-| `[range.adjacent.transform.sentinel] <https://wg21.link/range.adjacent.transform.sentinel>`_, "adjacent_transform_view::sentinel", None, Unassigned, |Not Started|
+| `adjacent_transform_view::sentinel`", Hui Xie, |Not Started|
+| `[range.adjacent.transform.iterator] <https://wg21.link/range.adjacent.transform.iterator>`_, "adjacent_transform_view::iterator", None, Hui Xie, |Not Started|
+| `[range.adjacent.transform.sentinel] <https://wg21.link/range.adjacent.transform.sentinel>`_, "adjacent_transform_view::sentinel", None, Hui Xie, |Not Started|
 | `[ranges.syn] <https://wg21.link/ranges.syn>`_, "enable_borrowed_range zip_view and adjacent_view", "| `zip_view`
-| `adjacent_view`", Unassigned, |Not Started|
+| `adjacent_view`", Hui Xie, |Not Started|

diff  --git a/libcxx/include/__utility/pair.h b/libcxx/include/__utility/pair.h
index 2f6c8f5017773..b6ecb9b5761a9 100644
--- a/libcxx/include/__utility/pair.h
+++ b/libcxx/include/__utility/pair.h
@@ -90,20 +90,26 @@ struct _LIBCPP_TEMPLATE_VIS pair
       }
 
       template <class _U1, class _U2>
-      static constexpr bool __enable_explicit() {
+      static constexpr bool __is_pair_constructible() {
           return is_constructible<first_type, _U1>::value
-              && is_constructible<second_type, _U2>::value
-              && (!is_convertible<_U1, first_type>::value
-                  || !is_convertible<_U2, second_type>::value);
+              && is_constructible<second_type, _U2>::value;
       }
 
       template <class _U1, class _U2>
-      static constexpr bool __enable_implicit() {
-          return is_constructible<first_type, _U1>::value
-              && is_constructible<second_type, _U2>::value
-              && is_convertible<_U1, first_type>::value
+      static constexpr bool __is_implicit() {
+          return is_convertible<_U1, first_type>::value
               && is_convertible<_U2, second_type>::value;
       }
+
+      template <class _U1, class _U2>
+      static constexpr bool __enable_explicit() {
+          return __is_pair_constructible<_U1, _U2>() && !__is_implicit<_U1, _U2>();
+      }
+
+      template <class _U1, class _U2>
+      static constexpr bool __enable_implicit() {
+          return __is_pair_constructible<_U1, _U2>() && __is_implicit<_U1, _U2>();
+      }
     };
 
     template <bool _MaybeEnable>
@@ -198,6 +204,17 @@ struct _LIBCPP_TEMPLATE_VIS pair
                     is_nothrow_constructible<second_type, _U2>::value))
         : first(_VSTD::forward<_U1>(__u1)), second(_VSTD::forward<_U2>(__u2)) {}
 
+#if _LIBCPP_STD_VER > 20
+    template<class _U1, class _U2, __enable_if_t<
+            _CheckArgs::template __is_pair_constructible<_U1&, _U2&>()
+    >* = nullptr>
+    _LIBCPP_HIDE_FROM_ABI constexpr
+    explicit(!_CheckArgs::template __is_implicit<_U1&, _U2&>()) pair(pair<_U1, _U2>& __p)
+        noexcept((is_nothrow_constructible<first_type, _U1&>::value &&
+                  is_nothrow_constructible<second_type, _U2&>::value))
+        : first(__p.first), second(__p.second) {}
+#endif
+
     template<class _U1, class _U2, typename enable_if<
             _CheckArgs::template __enable_explicit<_U1 const&, _U2 const&>()
     >::type* = nullptr>
@@ -234,6 +251,18 @@ struct _LIBCPP_TEMPLATE_VIS pair
                     is_nothrow_constructible<second_type, _U2&&>::value))
         : first(_VSTD::forward<_U1>(__p.first)), second(_VSTD::forward<_U2>(__p.second)) {}
 
+#if _LIBCPP_STD_VER > 20
+    template<class _U1, class _U2, __enable_if_t<
+            _CheckArgs::template __is_pair_constructible<const _U1&&, const _U2&&>()
+    >* = nullptr>
+    _LIBCPP_HIDE_FROM_ABI constexpr
+    explicit(!_CheckArgs::template __is_implicit<const _U1&&, const _U2&&>())
+    pair(const pair<_U1, _U2>&& __p)
+        noexcept(is_nothrow_constructible<first_type, const _U1&&>::value &&
+                 is_nothrow_constructible<second_type, const _U2&&>::value)
+        : first(std::move(__p.first)), second(std::move(__p.second)) {}
+#endif
+
     template<class _Tuple, typename enable_if<
             _CheckTLC<_Tuple>::template __enable_explicit<_Tuple>()
     >::type* = nullptr>
@@ -286,6 +315,50 @@ struct _LIBCPP_TEMPLATE_VIS pair
         return *this;
     }
 
+#if _LIBCPP_STD_VER > 20
+    _LIBCPP_HIDE_FROM_ABI constexpr
+    const pair& operator=(pair const& __p) const
+      noexcept(is_nothrow_copy_assignable_v<const first_type> &&
+               is_nothrow_copy_assignable_v<const second_type>)
+      requires(is_copy_assignable_v<const first_type> &&
+               is_copy_assignable_v<const second_type>) {
+        first = __p.first;
+        second = __p.second;
+        return *this;
+    }
+
+    _LIBCPP_HIDE_FROM_ABI constexpr
+    const pair& operator=(pair&& __p) const
+      noexcept(is_nothrow_assignable_v<const first_type&, first_type> &&
+               is_nothrow_assignable_v<const second_type&, second_type>)
+      requires(is_assignable_v<const first_type&, first_type> &&
+               is_assignable_v<const second_type&, second_type>) {
+        first = std::forward<first_type>(__p.first);
+        second = std::forward<second_type>(__p.second);
+        return *this;
+    }
+
+    template<class _U1, class _U2>
+    _LIBCPP_HIDE_FROM_ABI constexpr
+    const pair& operator=(const pair<_U1, _U2>& __p) const
+      requires(is_assignable_v<const first_type&, const _U1&> &&
+               is_assignable_v<const second_type&, const _U2&>) {
+        first = __p.first;
+        second = __p.second;
+        return *this;
+    }
+
+    template<class _U1, class _U2>
+    _LIBCPP_HIDE_FROM_ABI constexpr
+    const pair& operator=(pair<_U1, _U2>&& __p) const
+      requires(is_assignable_v<const first_type&, _U1> &&
+               is_assignable_v<const second_type&, _U2>) {
+        first = std::forward<_U1>(__p.first);
+        second = std::forward<_U2>(__p.second);
+        return *this;
+    }
+#endif // _LIBCPP_STD_VER > 20
+
     template <class _Tuple, typename enable_if<
             _CheckTLC<_Tuple>::template __enable_assign<_Tuple>()
      >::type* = nullptr>
@@ -306,6 +379,18 @@ struct _LIBCPP_TEMPLATE_VIS pair
         swap(first,  __p.first);
         swap(second, __p.second);
     }
+
+#if _LIBCPP_STD_VER > 20
+    _LIBCPP_HIDE_FROM_ABI constexpr
+    void swap(const pair& __p) const
+        noexcept(__is_nothrow_swappable<const first_type>::value &&
+                 __is_nothrow_swappable<const second_type>::value)
+    {
+        using std::swap;
+        swap(first,  __p.first);
+        swap(second, __p.second);
+    }
+#endif
 private:
 
 #ifndef _LIBCPP_CXX03_LANG
@@ -422,6 +507,18 @@ swap(pair<_T1, _T2>& __x, pair<_T1, _T2>& __y)
     __x.swap(__y);
 }
 
+#if _LIBCPP_STD_VER > 20
+template <class _T1, class _T2>
+  requires (__is_swappable<const _T1>::value &&
+            __is_swappable<const _T2>::value)
+_LIBCPP_HIDE_FROM_ABI constexpr
+void swap(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
+    noexcept(noexcept(__x.swap(__y)))
+{
+    __x.swap(__y);
+}
+#endif
+
 template <class _T1, class _T2>
 inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX14
 pair<typename __unwrap_ref_decay<_T1>::type, typename __unwrap_ref_decay<_T2>::type>

diff  --git a/libcxx/include/utility b/libcxx/include/utility
index b14ee43b73be0..ac6c18b06abc1 100644
--- a/libcxx/include/utility
+++ b/libcxx/include/utility
@@ -84,19 +84,29 @@ struct pair
     explicit(see-below) constexpr pair();
     explicit(see-below) pair(const T1& x, const T2& y);                          // constexpr in C++14
     template <class U = T1, class V = T2> explicit(see-below) pair(U&&, V&&);    // constexpr in C++14
+    template <class U, class V> constexpr explicit(see below) pair(pair<U, V>&); // since C++23
     template <class U, class V> explicit(see-below) pair(const pair<U, V>& p);   // constexpr in C++14
     template <class U, class V> explicit(see-below) pair(pair<U, V>&& p);        // constexpr in C++14
+    template <class U, class V>
+    constexpr explicit(see below) pair(const pair<U, V>&&);                      // since C++23
     template <class... Args1, class... Args2>
         pair(piecewise_construct_t, tuple<Args1...> first_args,
              tuple<Args2...> second_args);                                       // constexpr in C++20
 
+    constexpr const pair& operator=(const pair& p) const;                        // since C++23
     template <class U, class V> pair& operator=(const pair<U, V>& p);            // constexpr in C++20
+    template <class U, class V>
+    constexpr const pair& operator=(const pair<U, V>& p) const;                  // since C++23
     pair& operator=(pair&& p) noexcept(is_nothrow_move_assignable<T1>::value &&
                                        is_nothrow_move_assignable<T2>::value);   // constexpr in C++20
+    constexpr const pair& operator=(pair&& p) const;                             // since C++23
     template <class U, class V> pair& operator=(pair<U, V>&& p);                 // constexpr in C++20
+    template <class U, class V>
+    constexpr const pair& operator=(pair<U, V>&& p) const;                       // since C++23
 
     void swap(pair& p) noexcept(is_nothrow_swappable_v<T1> &&
                                 is_nothrow_swappable_v<T2>);                     // constexpr in C++20
+    constexpr void swap(const pair& p) const noexcept(see below);                // since C++23
 };
 
 template<class T1, class T2, class U1, class U2, template<class> class TQual, template<class> class UQual>
@@ -123,6 +133,9 @@ template <class T1, class T2>
 void
 swap(pair<T1, T2>& x, pair<T1, T2>& y) noexcept(noexcept(x.swap(y)));            // constexpr in C++20
 
+template<class T1, class T2>
+constexpr void swap(const pair<T1, T2>& x, const pair<T1, T2>& y) noexcept(noexcept(x.swap(y)));    // since C++23
+
 struct piecewise_construct_t { explicit piecewise_construct_t() = default; };
 inline constexpr piecewise_construct_t piecewise_construct = piecewise_construct_t();
 

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_convert_copy.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_convert_copy.pass.cpp
index 613080ad57ee6..2bc1f4ba1e337 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_convert_copy.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_convert_copy.pass.cpp
@@ -22,7 +22,7 @@
 #include <type_traits>
 
 #include "test_macros.h"
-#include "types.h"
+#include "copy_move_types.h"
 
 // test constraints
 

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_convert_move.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_convert_move.pass.cpp
index 3da71b654681f..367da312e3357 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_convert_move.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_convert_move.pass.cpp
@@ -22,7 +22,7 @@
 #include <type_traits>
 
 #include "test_macros.h"
-#include "types.h"
+#include "copy_move_types.h"
 
 // test constraints
 

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_copy.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_copy.pass.cpp
index f073afbf2f054..b541d78cf61e2 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_copy.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_copy.pass.cpp
@@ -21,7 +21,7 @@
 #include <type_traits>
 
 #include "test_macros.h"
-#include "types.h"
+#include "copy_move_types.h"
 
 static_assert(!std::is_assignable_v<const std::tuple<int>&, const std::tuple<int>&>);
 static_assert(std::is_assignable_v<const std::tuple<int&>&, const std::tuple<int&>&>);

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_move.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_move.pass.cpp
index 9f81dddd3914f..5ee72ae97cc54 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_move.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_move.pass.cpp
@@ -21,7 +21,7 @@
 #include <type_traits>
 
 #include "test_macros.h"
-#include "types.h"
+#include "copy_move_types.h"
 
 static_assert(!std::is_assignable_v<const std::tuple<int>&, std::tuple<int>&&>);
 static_assert(std::is_assignable_v<const std::tuple<int&>&, std::tuple<int&>&&>);

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair_copy.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair_copy.pass.cpp
index c98755df5c21e..a384e493a71b4 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair_copy.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair_copy.pass.cpp
@@ -23,7 +23,7 @@
 #include <utility>
 
 #include "test_macros.h"
-#include "types.h"
+#include "copy_move_types.h"
 
 // test constraints
 

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair_move.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair_move.pass.cpp
index 99d604402d82d..29dc14f9dfcfe 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair_move.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair_move.pass.cpp
@@ -23,7 +23,7 @@
 #include <utility>
 
 #include "test_macros.h"
-#include "types.h"
+#include "copy_move_types.h"
 
 // test constraints
 

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/types.h b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/types.h
deleted file mode 100644
index b5593d9e3723b..0000000000000
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/types.h
+++ /dev/null
@@ -1,138 +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
-//
-//===----------------------------------------------------------------------===//
-#ifndef LIBCXX_TEST_STD_UTILITIES_TUPLE_ASSIGN_TYPES_H
-#define LIBCXX_TEST_STD_UTILITIES_TUPLE_ASSIGN_TYPES_H
-
-#include "test_allocator.h"
-#include <type_traits>
-
-struct CopyAssign {
-  int val;
-
-  constexpr CopyAssign() = default;
-  constexpr CopyAssign(int v) : val(v) {}
-
-  constexpr CopyAssign& operator=(const CopyAssign&) = default;
-
-  constexpr const CopyAssign& operator=(const CopyAssign&) const = delete;
-  constexpr CopyAssign& operator=(CopyAssign&&) = delete;
-  constexpr const CopyAssign& operator=(CopyAssign&&) const = delete;
-};
-
-struct ConstCopyAssign {
-  mutable int val;
-
-  constexpr ConstCopyAssign() = default;
-  constexpr ConstCopyAssign(int v) : val(v) {}
-
-  constexpr const ConstCopyAssign& operator=(const ConstCopyAssign& other) const {
-    val = other.val;
-    return *this;
-  }
-
-  constexpr ConstCopyAssign& operator=(const ConstCopyAssign&) = delete;
-  constexpr ConstCopyAssign& operator=(ConstCopyAssign&&) = delete;
-  constexpr const ConstCopyAssign& operator=(ConstCopyAssign&&) const = delete;
-};
-
-struct MoveAssign {
-  int val;
-
-  constexpr MoveAssign() = default;
-  constexpr MoveAssign(int v) : val(v) {}
-
-  constexpr MoveAssign& operator=(MoveAssign&&) = default;
-
-  constexpr MoveAssign& operator=(const MoveAssign&) = delete;
-  constexpr const MoveAssign& operator=(const MoveAssign&) const = delete;
-  constexpr const MoveAssign& operator=(MoveAssign&&) const = delete;
-};
-
-struct ConstMoveAssign {
-  mutable int val;
-
-  constexpr ConstMoveAssign() = default;
-  constexpr ConstMoveAssign(int v) : val(v) {}
-
-  constexpr const ConstMoveAssign& operator=(ConstMoveAssign&& other) const {
-    val = other.val;
-    return *this;
-  }
-
-  constexpr ConstMoveAssign& operator=(const ConstMoveAssign&) = delete;
-  constexpr const ConstMoveAssign& operator=(const ConstMoveAssign&) const = delete;
-  constexpr ConstMoveAssign& operator=(ConstMoveAssign&&) = delete;
-};
-
-template <class T>
-struct AssignableFrom {
-  T v;
-
-  constexpr AssignableFrom() = default;
-
-  template <class U>
-  constexpr AssignableFrom(U&& u)
-    requires std::is_constructible_v<T, U&&>
-  : v(std::forward<U>(u)) {}
-
-  constexpr AssignableFrom& operator=(const T& t)
-    requires std::is_copy_assignable_v<T>
-  {
-    v = t;
-    return *this;
-  }
-
-  constexpr AssignableFrom& operator=(T&& t)
-    requires std::is_move_assignable_v<T>
-  {
-    v = std::move(t);
-    return *this;
-  }
-
-  constexpr const AssignableFrom& operator=(const T& t) const
-    requires std::is_assignable_v<const T&, const T&>
-  {
-    v = t;
-    return *this;
-  }
-
-  constexpr const AssignableFrom& operator=(T&& t) const
-    requires std::is_assignable_v<const T&, T&&>
-  {
-    v = std::move(t);
-    return *this;
-  }
-};
-
-struct TracedAssignment {
-  int copyAssign = 0;
-  mutable int constCopyAssign = 0;
-  int moveAssign = 0;
-  mutable int constMoveAssign = 0;
-
-  constexpr TracedAssignment() = default;
-
-  constexpr TracedAssignment& operator=(const TracedAssignment&) {
-    copyAssign++;
-    return *this;
-  }
-  constexpr const TracedAssignment& operator=(const TracedAssignment&) const {
-    constCopyAssign++;
-    return *this;
-  }
-  constexpr TracedAssignment& operator=(TracedAssignment&&) {
-    moveAssign++;
-    return *this;
-  }
-  constexpr const TracedAssignment& operator=(TracedAssignment&&) const {
-    constMoveAssign++;
-    return *this;
-  }
-};
-
-#endif

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_const_move_pair.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_const_move_pair.pass.cpp
index 539c50526e7fc..74d071ec7de43 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_const_move_pair.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_const_move_pair.pass.cpp
@@ -25,7 +25,7 @@
 #include <tuple>
 #include <utility>
 
-#include "convert_types.h"
+#include "copy_move_types.h"
 #include "test_allocator.h"
 
 // test constraints

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_const_move.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_const_move.pass.cpp
index e3488c4a45a19..4714310320164 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_const_move.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_const_move.pass.cpp
@@ -31,7 +31,7 @@
 #include <cassert>
 #include <tuple>
 
-#include "convert_types.h"
+#include "copy_move_types.h"
 #include "test_allocator.h"
 #include "test_macros.h"
 

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_non_const_copy.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_non_const_copy.pass.cpp
index 31913422be356..b7be68e23532d 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_non_const_copy.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_non_const_copy.pass.cpp
@@ -31,7 +31,7 @@
 #include <cassert>
 #include <tuple>
 
-#include "convert_types.h"
+#include "copy_move_types.h"
 #include "test_allocator.h"
 #include "test_macros.h"
 

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_non_const_pair.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_non_const_pair.pass.cpp
index 67008512ca348..ab24afc131d45 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_non_const_pair.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_non_const_pair.pass.cpp
@@ -24,7 +24,7 @@
 #include <tuple>
 #include <utility>
 
-#include "convert_types.h"
+#include "copy_move_types.h"
 #include "test_allocator.h"
 
 // test constraints

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/const_move_pair.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/const_move_pair.pass.cpp
index 2a99860b6d725..b85275c435354 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/const_move_pair.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/const_move_pair.pass.cpp
@@ -23,7 +23,7 @@
 #include <tuple>
 #include <utility>
 
-#include "convert_types.h"
+#include "copy_move_types.h"
 
 // test constraints
 // sizeof...(Types) == 2

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_const_move.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_const_move.pass.cpp
index 8c9f4d854f5aa..8f778f3fcf3a4 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_const_move.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_const_move.pass.cpp
@@ -30,7 +30,7 @@
 #include <cassert>
 #include <tuple>
 
-#include "convert_types.h"
+#include "copy_move_types.h"
 #include "test_macros.h"
 
 // test: The expression inside explicit is equivalent to:

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_non_const_copy.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_non_const_copy.pass.cpp
index 7f325f23a7bfd..6d3d15be7da5b 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_non_const_copy.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_non_const_copy.pass.cpp
@@ -29,7 +29,7 @@
 #include <cassert>
 #include <tuple>
 
-#include "convert_types.h"
+#include "copy_move_types.h"
 #include "test_macros.h"
 
 // test: The expression inside explicit is equivalent to:

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/non_const_pair.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/non_const_pair.pass.cpp
index 0652d89562039..1c2c71d92cb60 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/non_const_pair.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/non_const_pair.pass.cpp
@@ -23,7 +23,7 @@
 #include <tuple>
 #include <utility>
 
-#include "convert_types.h"
+#include "copy_move_types.h"
 
 // test constraints
 // sizeof...(Types) == 2

diff  --git a/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_copy_convert.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_copy_convert.pass.cpp
new file mode 100644
index 0000000000000..615378116bb49
--- /dev/null
+++ b/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_copy_convert.pass.cpp
@@ -0,0 +1,87 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <utility>
+
+// template <class T1, class T2> struct pair
+// template<class U1, class U2> constexpr
+// const pair& operator=(const pair<U1, U2>& p) const;
+
+#include <cassert>
+#include <utility>
+
+#include "test_macros.h"
+#include "copy_move_types.h"
+
+// Constraints:
+// is_assignable_v<const first_type&, const U1&> is true, and
+// is_assignable_v<const second_type&, const U2&> is true.
+
+// clang-format off
+static_assert( std::is_assignable_v<const std::pair<int&, int&>&,
+                                    const std::pair<long&, long&>&>);
+static_assert(!std::is_assignable_v<const std::pair<int, int>&,
+                                    const std::pair<long, long>&>);
+static_assert(!std::is_assignable_v<const std::pair<int, int&>&,
+                                    const std::pair<long, long&>&>);
+static_assert(!std::is_assignable_v<const std::pair<int&, int>&,
+                                    const std::pair<long&, long>&>);
+
+static_assert(std::is_assignable_v<
+    const std::pair<AssignableFrom<ConstCopyAssign>, AssignableFrom<ConstCopyAssign>>&,
+    const std::pair<ConstCopyAssign, ConstCopyAssign>&>);
+
+static_assert(!std::is_assignable_v<
+    const std::pair<AssignableFrom<CopyAssign>, AssignableFrom<CopyAssign>>&,
+    const std::pair<CopyAssign, CopyAssign>&>);
+// clang-format on
+
+constexpr bool test() {
+  // reference types
+  {
+    int i1  = 1;
+    int i2  = 2;
+    long j1 = 3;
+    long j2 = 4;
+    const std::pair<int&, int&> p1{i1, i2};
+    const std::pair<long&, long&> p2{j1, j2};
+    p2 = p1;
+    assert(p2.first == 1);
+    assert(p2.second == 2);
+  }
+
+  // user defined const copy assignment
+  {
+    const std::pair<ConstCopyAssign, ConstCopyAssign> p1{1, 2};
+    const std::pair<AssignableFrom<ConstCopyAssign>, AssignableFrom<ConstCopyAssign>> p2{3, 4};
+    p2 = p1;
+    assert(p2.first.v.val == 1);
+    assert(p2.second.v.val == 2);
+  }
+
+  // The correct assignment operator of the underlying type is used
+  {
+    std::pair<TracedAssignment, TracedAssignment> t1{};
+    const std::pair<AssignableFrom<TracedAssignment>, AssignableFrom<TracedAssignment>> t2{};
+    t2 = t1;
+    assert(t2.first.v.constCopyAssign == 1);
+    assert(t2.second.v.constCopyAssign == 1);
+  }
+
+  return true;
+}
+
+int main(int, const char**) {
+  test();
+// gcc cannot have mutable member in constant expression
+#if !defined(TEST_COMPILER_GCC)
+  static_assert(test());
+#endif
+}

diff  --git a/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_copy_pair.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_copy_pair.pass.cpp
new file mode 100644
index 0000000000000..58a14b46194d0
--- /dev/null
+++ b/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_copy_pair.pass.cpp
@@ -0,0 +1,84 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <utility>
+
+// template <class T1, class T2> struct pair
+// constexpr const pair& operator=(const pair& p) const;
+
+#include <cassert>
+#include <utility>
+
+#include "test_macros.h"
+#include "copy_move_types.h"
+
+// Constraints:
+// is_copy_assignable<const first_type> is true and
+// is_copy_assignable<const second_type> is true.
+
+// clang-format off
+static_assert(std::is_assignable_v<const std::pair<int&, int&>&,
+                                   const std::pair<int&, int&>&>);
+static_assert(!std::is_assignable_v<const std::pair<int, int>&,
+                                    const std::pair<int, int>&>);
+static_assert(!std::is_assignable_v<const std::pair<int, int&>&,
+                                    const std::pair<int, int&>&>);
+static_assert(!std::is_assignable_v<const std::pair<int&, int>&,
+                                    const std::pair<int&, int>&>);
+
+static_assert(std::is_assignable_v<const std::pair<ConstCopyAssign, ConstCopyAssign>&,
+                                   const std::pair<ConstCopyAssign, ConstCopyAssign>&>);
+static_assert(!std::is_assignable_v<const std::pair<CopyAssign, CopyAssign>&,
+                                   const std::pair<CopyAssign, CopyAssign>&>);
+
+// clang-format on
+
+constexpr bool test() {
+  // reference types
+  {
+    int i1    = 1;
+    int i2    = 2;
+    double d1 = 3.0;
+    double d2 = 5.0;
+    const std::pair<int&, double&> p1{i1, d1};
+    const std::pair<int&, double&> p2{i2, d2};
+    p2 = p1;
+    assert(p2.first == 1);
+    assert(p2.second == 3.0);
+  }
+
+  // user defined const copy assignment
+  {
+    const std::pair<ConstCopyAssign, ConstCopyAssign> p1{1, 2};
+    const std::pair<ConstCopyAssign, ConstCopyAssign> p2{3, 4};
+    p2 = p1;
+    assert(p2.first.val == 1);
+    assert(p2.second.val == 2);
+  }
+
+  // The correct assignment operator of the underlying type is used
+  {
+    std::pair<TracedAssignment, const TracedAssignment> t1{};
+    const std::pair<TracedAssignment, const TracedAssignment> t2{};
+    t2 = t1;
+    assert(t2.first.constCopyAssign == 1);
+    assert(t2.second.constCopyAssign == 1);
+  }
+
+  return true;
+}
+
+int main(int, const char**) {
+  test();
+// gcc cannot have mutable member in constant expression
+#if !defined(TEST_COMPILER_GCC)
+  static_assert(test());
+#endif
+}

diff  --git a/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_move_convert.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_move_convert.pass.cpp
new file mode 100644
index 0000000000000..a2158b09e97fb
--- /dev/null
+++ b/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_move_convert.pass.cpp
@@ -0,0 +1,87 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <utility>
+
+// template <class T1, class T2> struct pair
+// template <class U1, class U2>
+// constexpr const pair& operator=(pair<U1, U2>&& p) const;
+
+#include <cassert>
+#include <utility>
+
+#include "test_macros.h"
+#include "copy_move_types.h"
+
+// Constraints:
+// is_assignable<const first_type&, U1> is true and
+// is_assignable<const second_type&, U2> is true.
+
+// clang-format off
+static_assert( std::is_assignable_v<const std::pair<int&&, int&&>&,
+                                    std::pair<long&&, long&&>&&>);
+static_assert(!std::is_assignable_v<const std::pair<int, int>&,
+                                    std::pair<long, long>&&>);
+static_assert(!std::is_assignable_v<const std::pair<int, int&&>&,
+                                    std::pair<long, long&&>&&>);
+static_assert(!std::is_assignable_v<const std::pair<int&&, int>&,
+                                    std::pair<long&&, long>&&>);
+
+static_assert(std::is_assignable_v<
+    const std::pair<AssignableFrom<ConstMoveAssign>, AssignableFrom<ConstMoveAssign>>&,
+    std::pair<ConstMoveAssign, ConstMoveAssign>&&>);
+
+static_assert(!std::is_assignable_v<
+    const std::pair<AssignableFrom<MoveAssign>, AssignableFrom<MoveAssign>>&,
+    std::pair<MoveAssign, MoveAssign>&&>);
+// clang-format on
+
+constexpr bool test() {
+  // reference types
+  {
+    int i1  = 1;
+    int i2  = 2;
+    long j1 = 3;
+    long j2 = 4;
+    std::pair<int&&, int&&> p1{std::move(i1), std::move(i2)};
+    const std::pair<long&&, long&&> p2{std::move(j1), std::move(j2)};
+    p2 = std::move(p1);
+    assert(p2.first == 1);
+    assert(p2.second == 2);
+  }
+
+  // user defined const move assignment
+  {
+    std::pair<ConstMoveAssign, ConstMoveAssign> p1{1, 2};
+    const std::pair<AssignableFrom<ConstMoveAssign>, AssignableFrom<ConstMoveAssign>> p2{3, 4};
+    p2 = std::move(p1);
+    assert(p2.first.v.val == 1);
+    assert(p2.second.v.val == 2);
+  }
+
+  // The correct assignment operator of the underlying type is used
+  {
+    std::pair<TracedAssignment, TracedAssignment> t1{};
+    const std::pair<AssignableFrom<TracedAssignment>, AssignableFrom<TracedAssignment>> t2{};
+    t2 = std::move(t1);
+    assert(t2.first.v.constMoveAssign == 1);
+    assert(t2.second.v.constMoveAssign == 1);
+  }
+
+  return true;
+}
+
+int main(int, const char**) {
+  test();
+// gcc cannot have mutable member in constant expression
+#if !defined(TEST_COMPILER_GCC)
+  static_assert(test());
+#endif
+}

diff  --git a/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_move_pair.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_move_pair.pass.cpp
new file mode 100644
index 0000000000000..b4f49ef91e7d7
--- /dev/null
+++ b/libcxx/test/std/utilities/utility/pairs/pairs.pair/assign_const_move_pair.pass.cpp
@@ -0,0 +1,84 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <utility>
+
+// template <class T1, class T2> struct pair
+// constexpr const pair& operator=(pair&& p) const;
+
+#include <cassert>
+#include <utility>
+
+#include "test_macros.h"
+#include "copy_move_types.h"
+
+// Constraints:
+// is_assignable<const first_type&, first_type> is true and
+// is_assignable<const second_type&, second_type> is true.
+
+// clang-format off
+static_assert(std::is_assignable_v<const std::pair<int&&, int&&>&,
+                                   std::pair<int&&, int&&>&&>);
+static_assert(!std::is_assignable_v<const std::pair<int, int>&,
+                                    std::pair<int, int>&&>);
+static_assert(!std::is_assignable_v<const std::pair<int, int&&>&,
+                                    std::pair<int, int&&>&&>);
+static_assert(!std::is_assignable_v<const std::pair<int&&, int>&,
+                                    std::pair<int&&, int>&&>);
+
+static_assert(std::is_assignable_v<const std::pair<ConstMoveAssign, ConstMoveAssign>&,
+                                   std::pair<ConstMoveAssign, ConstMoveAssign>&&>);
+static_assert(!std::is_assignable_v<const std::pair<MoveAssign, MoveAssign>&,
+                                   std::pair<MoveAssign, MoveAssign>&&>);
+
+// clang-format on
+
+constexpr bool test() {
+  // reference types
+  {
+    int i1    = 1;
+    int i2    = 2;
+    double d1 = 3.0;
+    double d2 = 5.0;
+    std::pair<int&&, double&&> p1{std::move(i1), std::move(d1)};
+    const std::pair<int&&, double&&> p2{std::move(i2), std::move(d2)};
+    p2 = std::move(p1);
+    assert(p2.first == 1);
+    assert(p2.second == 3.0);
+  }
+
+  // user defined const move assignment
+  {
+    std::pair<ConstMoveAssign, ConstMoveAssign> p1{1, 2};
+    const std::pair<ConstMoveAssign, ConstMoveAssign> p2{3, 4};
+    p2 = std::move(p1);
+    assert(p2.first.val == 1);
+    assert(p2.second.val == 2);
+  }
+
+  // The correct assignment operator of the underlying type is used
+  {
+    std::pair<TracedAssignment, const TracedAssignment> t1{};
+    const std::pair<TracedAssignment, const TracedAssignment> t2{};
+    t2 = std::move(t1);
+    assert(t2.first.constMoveAssign == 1);
+    assert(t2.second.constCopyAssign == 1);
+  }
+
+  return true;
+}
+
+int main(int, const char**) {
+  test();
+// gcc cannot have mutable member in constant expression
+#if !defined(TEST_COMPILER_GCC)
+  static_assert(test());
+#endif
+}

diff  --git a/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor_pair_U_V_const_move.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor_pair_U_V_const_move.pass.cpp
new file mode 100644
index 0000000000000..d3fbf6fab3b4c
--- /dev/null
+++ b/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor_pair_U_V_const_move.pass.cpp
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <utility>
+
+// template <class T1, class T2> struct pair
+// template <class U1, class U2>
+//   constexpr explicit(see below) pair(const pair<U1, U2>&& p);
+
+#include <cassert>
+#include <utility>
+
+#include "copy_move_types.h"
+#include "test_macros.h"
+
+// Constraints:
+// is_constructible_v<T1, decltype(get<0>(FWD(p)))> is true and
+// is_constructible_v<T2, decltype(get<1>(FWD(p)))> is true.
+struct X {};
+struct Y {};
+struct NotConvertibleToXorY {};
+
+static_assert(std::is_constructible_v<std::pair<X, Y>, const std::pair<X, Y>&&>);
+static_assert(!std::is_constructible_v<std::pair<X, Y>, const std::pair<NotConvertibleToXorY, Y>&&>);
+static_assert(!std::is_constructible_v<std::pair<X, Y>, const std::pair<X, NotConvertibleToXorY>&&>);
+static_assert(!std::is_constructible_v<std::pair<X, Y>, const std::pair<NotConvertibleToXorY, NotConvertibleToXorY>&&>);
+
+// The expression inside explicit is equivalent to:
+// !is_convertible_v<decltype(get<0>(FWD(p))), first_type> ||
+// !is_convertible_v<decltype(get<1>(FWD(p))), second_type>.
+// clang-format off
+static_assert( std::is_convertible_v<const std::pair<X, Y>&&,
+                                     std::pair<ConvertibleFrom<X>, ConvertibleFrom<Y>>>);
+static_assert(!std::is_convertible_v<const std::pair<X, Y>&&,
+                                     std::pair<ConvertibleFrom<X>, ExplicitConstructibleFrom<Y>>>);
+static_assert(!std::is_convertible_v<const std::pair<X, Y>&&,
+                                     std::pair<ExplicitConstructibleFrom<X>, ConvertibleFrom<Y>>>);
+static_assert(!std::is_convertible_v<const std::pair<X, Y>&&,
+                                     std::pair<ExplicitConstructibleFrom<X>, ExplicitConstructibleFrom<Y>>>);
+// clang-format on
+
+constexpr bool test() {
+  // simple case: init pair<const T&&, const U&&> from const pair<T, U>&&
+  {
+    const std::pair<int, int> p1{1, 2};
+    std::pair<const int&&, const int&&> p2{std::move(p1)};
+    assert(&(p2.first) == &(p1.first));
+    assert(&(p2.second) == &(p1.second));
+  }
+
+  // test implicit conversions.
+  {
+    const std::pair<ConstMove, int> p1{1, 2};
+    std::pair<ConvertibleFrom<ConstMove>, ConvertibleFrom<int>> p2 = std::move(p1);
+    assert(p2.first.v.val == 1);
+    assert(p2.second.v == 2);
+  }
+
+  // test explicit conversions.
+  {
+    const std::pair<ConstMove, int> p1{1, 2};
+    std::pair<ExplicitConstructibleFrom<ConstMove>, ExplicitConstructibleFrom<int>> p2{std::move(p1)};
+    assert(p2.first.v.val == 1);
+    assert(p2.second.v == 2);
+  }
+
+  // test correct constructors of underlying types are called
+  {
+    const std::pair<TracedCopyMove, TracedCopyMove> p1{};
+    std::pair<ConvertibleFrom<TracedCopyMove>, ConvertibleFrom<TracedCopyMove>> p2{std::move(p1)};
+    assert(constMoveCtrCalled(p2.first.v));
+    assert(constMoveCtrCalled(p2.second.v));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}

diff  --git a/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor_pair_U_V_ref.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor_pair_U_V_ref.pass.cpp
new file mode 100644
index 0000000000000..7ac70bf36a780
--- /dev/null
+++ b/libcxx/test/std/utilities/utility/pairs/pairs.pair/ctor_pair_U_V_ref.pass.cpp
@@ -0,0 +1,85 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <utility>
+
+// template <class T1, class T2> struct pair
+// template <class U1, class U2>
+//   constexpr explicit(see below) pair(pair<U1, U2>& p);
+
+#include <cassert>
+#include <utility>
+
+#include "copy_move_types.h"
+#include "test_macros.h"
+
+// Constraints:
+// is_constructible_v<T1, decltype(get<0>(FWD(p)))> is true and
+// is_constructible_v<T2, decltype(get<1>(FWD(p)))> is true.
+struct X {};
+struct Y {};
+struct NotConvertibleToXorY {};
+
+// clang-format off
+static_assert( std::is_constructible_v<std::pair<X, Y>, std::pair<X, Y>&>);
+static_assert(!std::is_constructible_v<std::pair<X, Y>, std::pair<NotConvertibleToXorY, Y>&>);
+static_assert(!std::is_constructible_v<std::pair<X, Y>, std::pair<X, NotConvertibleToXorY>&>);
+static_assert(!std::is_constructible_v<std::pair<X, Y>, std::pair<NotConvertibleToXorY, NotConvertibleToXorY>&>);
+
+// The expression inside explicit is equivalent to:
+// !is_convertible_v<decltype(get<0>(FWD(p))), first_type> ||
+// !is_convertible_v<decltype(get<1>(FWD(p))), second_type>.
+static_assert( std::is_convertible_v<std::pair<X, Y>&, std::pair<ConvertibleFrom<X>, ConvertibleFrom<Y>>>);
+static_assert(!std::is_convertible_v<std::pair<X, Y>&, std::pair<ConvertibleFrom<X>, ExplicitConstructibleFrom<Y>>>);
+static_assert(!std::is_convertible_v<std::pair<X, Y>&, std::pair<ExplicitConstructibleFrom<X>, ConvertibleFrom<Y>>>);
+static_assert(!std::is_convertible_v<std::pair<X, Y>&, std::pair<ExplicitConstructibleFrom<X>, ExplicitConstructibleFrom<Y>>>);
+// clang-format on
+
+constexpr bool test() {
+  // use case in zip. Init pair<T&, U&> from pair<T, U>&
+  {
+    std::pair<int, int> p1{1, 2};
+    std::pair<int&, int&> p2{p1};
+    assert(&(p2.first) == &(p1.first));
+    assert(&(p2.second) == &(p1.second));
+  }
+
+  // test implicit conversions.
+  {
+    std::pair<MutableCopy, int> p1{1, 2};
+    std::pair<ConvertibleFrom<MutableCopy>, ConvertibleFrom<int>> p2 = p1;
+    assert(p2.first.v.val == 1);
+    assert(p2.second.v == 2);
+  }
+
+  // test explicit conversions.
+  {
+    std::pair<MutableCopy, int> p1{1, 2};
+    std::pair<ExplicitConstructibleFrom<MutableCopy>, ExplicitConstructibleFrom<int>> p2{p1};
+    assert(p2.first.v.val == 1);
+    assert(p2.second.v == 2);
+  }
+
+  // test correct constructors of underlying types are called
+  {
+    std::pair<TracedCopyMove, TracedCopyMove> p1{};
+    std::pair<ConvertibleFrom<TracedCopyMove>, ConvertibleFrom<TracedCopyMove>> p2{p1};
+    assert(nonConstCopyCtrCalled(p2.first.v));
+    assert(nonConstCopyCtrCalled(p2.second.v));
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+  static_assert(test());
+  return 0;
+}

diff  --git a/libcxx/test/std/utilities/utility/pairs/pairs.pair/swap_member_const.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.pair/swap_member_const.pass.cpp
new file mode 100644
index 0000000000000..93dc62845d789
--- /dev/null
+++ b/libcxx/test/std/utilities/utility/pairs/pairs.pair/swap_member_const.pass.cpp
@@ -0,0 +1,81 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <utility>
+
+// template <class T1, class T2> struct pair
+// void swap(const pair& p) const;
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <cassert>
+#include <utility>
+
+#include "test_macros.h"
+
+// Remarks: The expression inside noexcept is equivalent to
+// is_nothrow_swappable_v<const first_type> && is_nothrow_swappable_v<const second_type> for the second overload.
+template <class T>
+concept ConstMemberSwapNoexcept =
+    requires(const T& t1, const T& t2) {
+      { t1.swap(t2) } noexcept;
+    };
+
+template <bool canThrow>
+struct SwapMayThrow {};
+
+template <bool canThrow>
+void swap(const SwapMayThrow<canThrow>&, const SwapMayThrow<canThrow>&) noexcept(!canThrow);
+
+static_assert(ConstMemberSwapNoexcept<std::pair<SwapMayThrow<false>, SwapMayThrow<false>>>);
+static_assert(!ConstMemberSwapNoexcept<std::pair<SwapMayThrow<true>, SwapMayThrow<false>>>);
+static_assert(!ConstMemberSwapNoexcept<std::pair<SwapMayThrow<false>, SwapMayThrow<true>>>);
+static_assert(!ConstMemberSwapNoexcept<std::pair<SwapMayThrow<true>, SwapMayThrow<true>>>);
+
+struct ConstSwappable {
+  mutable int i;
+  friend constexpr void swap(const ConstSwappable& lhs, const ConstSwappable& rhs) { std::swap(lhs.i, rhs.i); }
+};
+
+constexpr bool test() {
+  // user defined const swap
+  {
+    using P = std::pair<const ConstSwappable, const ConstSwappable>;
+    const P p1(ConstSwappable{0}, ConstSwappable{1});
+    const P p2(ConstSwappable{2}, ConstSwappable{3});
+    p1.swap(p2);
+    assert(p1.first.i == 2);
+    assert(p1.second.i == 3);
+    assert(p2.first.i == 0);
+    assert(p2.second.i == 1);
+  }
+
+  // pair of references
+  {
+    int i1 = 0, i2 = 1, i3 = 2, i4 = 3;
+    const std::pair<int&, int&> p1{i1, i2};
+    const std::pair<int&, int&> p2{i3, i4};
+    p1.swap(p2);
+    assert(p1.first == 2);
+    assert(p1.second == 3);
+    assert(p2.first == 0);
+    assert(p2.second == 1);
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+
+// gcc cannot have mutable member in constant expression
+#if !defined(TEST_COMPILER_GCC)
+  static_assert(test());
+#endif
+
+  return 0;
+}
\ No newline at end of file

diff  --git a/libcxx/test/std/utilities/utility/pairs/pairs.spec/non_member_const_swap.pass.cpp b/libcxx/test/std/utilities/utility/pairs/pairs.spec/non_member_const_swap.pass.cpp
new file mode 100644
index 0000000000000..c259afaa42609
--- /dev/null
+++ b/libcxx/test/std/utilities/utility/pairs/pairs.spec/non_member_const_swap.pass.cpp
@@ -0,0 +1,93 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <utility>
+
+// template <class T1, class T2>
+// void swap(const pair<T1, T2>& x, const pair<T1, T2>& y) const noexcept(noexcept(x.swap(y)));;
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <cassert>
+#include <utility>
+
+#include "test_macros.h"
+
+// Constraints:
+// For the second overload, is_swappable_v<const T1> is true and is_swappable_v<const T2> is true.
+struct NonConstSwappable {
+  friend constexpr void swap(NonConstSwappable&, NonConstSwappable&);
+};
+
+struct ConstSwappable {
+  mutable int i;
+  friend constexpr void swap(const ConstSwappable& lhs, const ConstSwappable& rhs) { std::swap(lhs.i, rhs.i); }
+};
+
+static_assert(std::is_swappable_v<const std::pair<ConstSwappable, ConstSwappable>>);
+static_assert(!std::is_swappable_v<const std::pair<NonConstSwappable, ConstSwappable>>);
+static_assert(!std::is_swappable_v<const std::pair<ConstSwappable, NonConstSwappable>>);
+static_assert(!std::is_swappable_v<const std::pair<NonConstSwappable, NonConstSwappable>>);
+
+// noexcept(noexcept(x.swap(y)));
+template <class T>
+concept NonMemberSwapNoexcept =
+    requires(T t1, T t2) {
+      { swap(t1, t2) } noexcept;
+    };
+
+template <bool canThrow>
+struct SwapMayThrow {};
+
+template <bool canThrow>
+void swap(const SwapMayThrow<canThrow>&, const SwapMayThrow<canThrow>&) noexcept(!canThrow);
+
+static_assert(NonMemberSwapNoexcept<const std::pair<SwapMayThrow<false>, SwapMayThrow<false>>>);
+static_assert(!NonMemberSwapNoexcept<const std::pair<SwapMayThrow<true>, SwapMayThrow<false>>>);
+static_assert(!NonMemberSwapNoexcept<const std::pair<SwapMayThrow<false>, SwapMayThrow<true>>>);
+static_assert(!NonMemberSwapNoexcept<const std::pair<SwapMayThrow<true>, SwapMayThrow<true>>>);
+
+constexpr bool test() {
+  // user defined const swap
+  {
+    using P = std::pair<const ConstSwappable, const ConstSwappable>;
+    const P p1(ConstSwappable{0}, ConstSwappable{1});
+    const P p2(ConstSwappable{2}, ConstSwappable{3});
+    using std::swap;
+    swap(p1, p2);
+    assert(p1.first.i == 2);
+    assert(p1.second.i == 3);
+    assert(p2.first.i == 0);
+    assert(p2.second.i == 1);
+  }
+
+  // pair of references
+  {
+    int i1 = 0, i2 = 1, i3 = 2, i4 = 3;
+    const std::pair<int&, int&> p1{i1, i2};
+    const std::pair<int&, int&> p2{i3, i4};
+    using std::swap;
+    swap(p1, p2);
+    assert(p1.first == 2);
+    assert(p1.second == 3);
+    assert(p2.first == 0);
+    assert(p2.second == 1);
+  }
+  return true;
+}
+
+int main(int, char**) {
+  test();
+
+// gcc cannot have mutable member in constant expression
+#if !defined(TEST_COMPILER_GCC)
+  static_assert(test());
+#endif
+
+  return 0;
+}
\ No newline at end of file

diff  --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_types.h b/libcxx/test/support/copy_move_types.h
similarity index 71%
rename from libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_types.h
rename to libcxx/test/support/copy_move_types.h
index 0bdfec3cd4e86..b8678ff1d5ad0 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_types.h
+++ b/libcxx/test/support/copy_move_types.h
@@ -10,6 +10,9 @@
 
 #include "test_allocator.h"
 #include <type_traits>
+#include <tuple>
+
+// Types that can be used to test copy/move operations
 
 struct MutableCopy {
   int val;
@@ -215,4 +218,127 @@ void conversion_test(T);
 template <class T, class... Args>
 concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test<T>({std::forward<Args>(args)...}); };
 
+struct CopyAssign {
+  int val;
+
+  constexpr CopyAssign() = default;
+  constexpr CopyAssign(int v) : val(v) {}
+
+  constexpr CopyAssign& operator=(const CopyAssign&) = default;
+
+  constexpr const CopyAssign& operator=(const CopyAssign&) const = delete;
+  constexpr CopyAssign& operator=(CopyAssign&&) = delete;
+  constexpr const CopyAssign& operator=(CopyAssign&&) const = delete;
+};
+
+struct ConstCopyAssign {
+  mutable int val;
+
+  constexpr ConstCopyAssign() = default;
+  constexpr ConstCopyAssign(int v) : val(v) {}
+
+  constexpr const ConstCopyAssign& operator=(const ConstCopyAssign& other) const {
+    val = other.val;
+    return *this;
+  }
+
+  constexpr ConstCopyAssign& operator=(const ConstCopyAssign&) = delete;
+  constexpr ConstCopyAssign& operator=(ConstCopyAssign&&) = delete;
+  constexpr const ConstCopyAssign& operator=(ConstCopyAssign&&) const = delete;
+};
+
+struct MoveAssign {
+  int val;
+
+  constexpr MoveAssign() = default;
+  constexpr MoveAssign(int v) : val(v) {}
+
+  constexpr MoveAssign& operator=(MoveAssign&&) = default;
+
+  constexpr MoveAssign& operator=(const MoveAssign&) = delete;
+  constexpr const MoveAssign& operator=(const MoveAssign&) const = delete;
+  constexpr const MoveAssign& operator=(MoveAssign&&) const = delete;
+};
+
+struct ConstMoveAssign {
+  mutable int val;
+
+  constexpr ConstMoveAssign() = default;
+  constexpr ConstMoveAssign(int v) : val(v) {}
+
+  constexpr const ConstMoveAssign& operator=(ConstMoveAssign&& other) const {
+    val = other.val;
+    return *this;
+  }
+
+  constexpr ConstMoveAssign& operator=(const ConstMoveAssign&) = delete;
+  constexpr const ConstMoveAssign& operator=(const ConstMoveAssign&) const = delete;
+  constexpr ConstMoveAssign& operator=(ConstMoveAssign&&) = delete;
+};
+
+template <class T>
+struct AssignableFrom {
+  T v;
+
+  constexpr AssignableFrom() = default;
+
+  template <class U>
+  constexpr AssignableFrom(U&& u)
+    requires std::is_constructible_v<T, U&&>
+  : v(std::forward<U>(u)) {}
+
+  constexpr AssignableFrom& operator=(const T& t)
+    requires std::is_copy_assignable_v<T>
+  {
+    v = t;
+    return *this;
+  }
+
+  constexpr AssignableFrom& operator=(T&& t)
+    requires std::is_move_assignable_v<T>
+  {
+    v = std::move(t);
+    return *this;
+  }
+
+  constexpr const AssignableFrom& operator=(const T& t) const
+    requires std::is_assignable_v<const T&, const T&>
+  {
+    v = t;
+    return *this;
+  }
+
+  constexpr const AssignableFrom& operator=(T&& t) const
+    requires std::is_assignable_v<const T&, T&&>
+  {
+    v = std::move(t);
+    return *this;
+  }
+};
+
+struct TracedAssignment {
+  int copyAssign = 0;
+  mutable int constCopyAssign = 0;
+  int moveAssign = 0;
+  mutable int constMoveAssign = 0;
+
+  constexpr TracedAssignment() = default;
+
+  constexpr TracedAssignment& operator=(const TracedAssignment&) {
+    copyAssign++;
+    return *this;
+  }
+  constexpr const TracedAssignment& operator=(const TracedAssignment&) const {
+    constCopyAssign++;
+    return *this;
+  }
+  constexpr TracedAssignment& operator=(TracedAssignment&&) {
+    moveAssign++;
+    return *this;
+  }
+  constexpr const TracedAssignment& operator=(TracedAssignment&&) const {
+    constMoveAssign++;
+    return *this;
+  }
+};
 #endif


        


More information about the libcxx-commits mailing list