[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