public inbox for libstdc++@gcc.gnu.org
 help / color / mirror / Atom feed
From: Patrick Palka <ppalka@redhat.com>
To: gcc-patches@gcc.gnu.org
Cc: libstdc++@gcc.gnu.org, Patrick Palka <ppalka@redhat.com>
Subject: [PATCH 2/3] libstdc++: Implement std::pair/tuple/misc enhancements from P2321R2
Date: Mon, 22 Aug 2022 21:34:59 -0400	[thread overview]
Message-ID: <20220823013500.1756466-2-ppalka@redhat.com> (raw)
In-Reply-To: <20220823013500.1756466-1-ppalka@redhat.com>

This implements the non-<ranges> changes from P2321R2, which primarily
consist of new converting constructors, assignment operator and swap
overloads for std::pair and std::tuple.

Tested on x86_64-pc-linux-gnu, does this look OK for trunk?

libstdc++-v3/ChangeLog:

	* include/bits/stl_bvector.h (_Bit_reference::operator=): Define
	const overload for C++23 as per P2321R2.
	* include/bits/stl_pair.h (pair::swap): Likewise.
	(pair::pair): Define additional converting constructors for
	C++23 as per P2321R2.
	(pair::operator=): Define const overloads for C++23 as per
	P2321R2.
	(swap): Define overload taking const pair& for C++23 as per
	P2321R2.
	(basic_common_reference): Define partial specialization for
	pair for C++23 as per P2321R2.
	(common_type): Likewise.
	* include/bits/uses_allocator_args.h
	(uses_allocator_construction_args): Define additional pair
	overloads for C++23 as per P2321R2.
	* include/std/tuple (_Tuple_impl::_Tuple_impl): Define
	additional converting constructors for C++23 as per P2321R2.
	(_Tuple_impl::_M_assign): Define const overloads for C++23
	as per P2321R2.
	(_Tuple_impl::_M_swap): Likewise.
	(tuple::__constructible): Define as a convenient renaming of
	_TCC<true>::__constructible.
	(tuple::__convertible): As above but for _TCC<true>::__convertible.
	(tuple::tuple): Define additional converting constructors for
	C++23 as per P2321R2.
	(tuple::operator=): Define const overloads for C++23 as per
	P2321R2.
	(tuple::swap): Likewise.
	(basic_common_reference): Define partial specialization for
	tuple for C++23 as per P2321R2.
	(common_type): Likewise.
	* testsuite/20_util/pair/p2321.cc: New test.
	* testsuite/20_util/tuple/p2321.cc: New test.
	* testsuite/23_containers/vector/bool/element_access/1.cc: New test.
---
 libstdc++-v3/include/bits/stl_bvector.h       |  12 +
 libstdc++-v3/include/bits/stl_pair.h          | 118 +++-
 .../include/bits/uses_allocator_args.h        |  41 ++
 libstdc++-v3/include/std/tuple                | 416 +++++++++++
 libstdc++-v3/testsuite/20_util/pair/p2321.cc  | 208 ++++++
 libstdc++-v3/testsuite/20_util/tuple/p2321.cc | 664 ++++++++++++++++++
 .../vector/bool/element_access/1.cc           |  26 +
 7 files changed, 1480 insertions(+), 5 deletions(-)
 create mode 100644 libstdc++-v3/testsuite/20_util/pair/p2321.cc
 create mode 100644 libstdc++-v3/testsuite/20_util/tuple/p2321.cc
 create mode 100644 libstdc++-v3/testsuite/23_containers/vector/bool/element_access/1.cc

diff --git a/libstdc++-v3/include/bits/stl_bvector.h b/libstdc++-v3/include/bits/stl_bvector.h
index d256af40f40..c5fd19e7309 100644
--- a/libstdc++-v3/include/bits/stl_bvector.h
+++ b/libstdc++-v3/include/bits/stl_bvector.h
@@ -106,6 +106,18 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       return *this;
     }
 
+#if __cplusplus > 202002L
+    constexpr const _Bit_reference&
+    operator=(bool __x) const noexcept
+    {
+      if (__x)
+	*_M_p |= _M_mask;
+      else
+	*_M_p &= ~_M_mask;
+      return *this;
+    }
+#endif
+
     _GLIBCXX20_CONSTEXPR
     _Bit_reference&
     operator=(const _Bit_reference& __x) _GLIBCXX_NOEXCEPT
diff --git a/libstdc++-v3/include/bits/stl_pair.h b/libstdc++-v3/include/bits/stl_pair.h
index 831e770d54b..d0efba635bc 100644
--- a/libstdc++-v3/include/bits/stl_pair.h
+++ b/libstdc++-v3/include/bits/stl_pair.h
@@ -212,6 +212,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	swap(second, __p.second);
       }
 
+#if __cplusplus > 202002L
+      /// Swap the first members and then the second members.
+      constexpr void
+      swap(const pair& __p) const
+      noexcept(__and_<__is_nothrow_swappable<const _T1>,
+		      __is_nothrow_swappable<const _T2>>::value)
+      {
+	using std::swap;
+	swap(first, __p.first);
+	swap(second, __p.second);
+      }
+#endif // C++23
+
     private:
       template<typename... _Args1, size_t... _Indexes1,
 	       typename... _Args2, size_t... _Indexes2>
@@ -283,7 +296,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	: first(std::forward<_U1>(__x)), second(std::forward<_U2>(__y))
 	{ }
 
-      /// Converting constructor from a `pair<U1, U2>` lvalue
+      /// Converting constructor from a const `pair<U1, U2>` lvalue
       template<typename _U1, typename _U2>
 	requires (_S_constructible<const _U1&, const _U2&>())
 	constexpr explicit(!_S_convertible<const _U1&, const _U2&>())
@@ -292,7 +305,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	: first(__p.first), second(__p.second)
 	{ }
 
-      /// Converting constructor from a `pair<U1, U2>` rvalue
+      /// Converting constructor from a non-const `pair<U1, U2>` rvalue
       template<typename _U1, typename _U2>
 	requires (_S_constructible<_U1, _U2>())
 	constexpr explicit(!_S_convertible<_U1, _U2>())
@@ -302,6 +315,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  second(std::forward<_U2>(__p.second))
 	{ }
 
+#if __cplusplus > 202002L
+      /// Converting constructor from a non-const `pair<U1, U2>` lvalue
+      template<typename _U1, typename _U2>
+	requires (_S_constructible<_U1&, _U2&>())
+	constexpr explicit(!_S_convertible<_U1&, _U2&>())
+	pair(pair<_U1, _U2>& __p)
+	noexcept(_S_nothrow_constructible<_U1&, _U2&>())
+	: first(__p.first), second(__p.second)
+	{ }
+
+      /// Converting constructor from a const `pair<U1, U2>` rvalue
+      template<typename _U1, typename _U2>
+	requires (_S_constructible<const _U1, const _U2>())
+	constexpr explicit(!_S_convertible<const _U1, const _U2>())
+	pair(const pair<_U1, _U2>&& __p)
+	noexcept(_S_nothrow_constructible<const _U1, const _U2>())
+	: first(std::forward<const _U1>(__p.first)),
+	  second(std::forward<const _U2>(__p.second))
+	{ }
+#endif
+
   private:
       /// @cond undocumented
       template<typename _U1, typename _U2>
@@ -349,7 +383,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	return *this;
       }
 
-      /// Converting assignment from a `pair<U1, U2>` lvalue
+      /// Converting assignment from a const `pair<U1, U2>` lvalue
       template<typename _U1, typename _U2>
 	constexpr pair&
 	operator=(const pair<_U1, _U2>& __p)
@@ -361,7 +395,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  return *this;
 	}
 
-      /// Converting assignment from a `pair<U1, U2>` rvalue
+      /// Converting assignment from a non-const `pair<U1, U2>` rvalue
       template<typename _U1, typename _U2>
 	constexpr pair&
 	operator=(pair<_U1, _U2>&& __p)
@@ -372,7 +406,55 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  second = std::forward<_U2>(__p.second);
 	  return *this;
 	}
-#else
+
+#if __cplusplus > 202002L
+      /// Copy assignment operator
+      constexpr const pair&
+      operator=(const pair& __p) const
+      requires is_copy_assignable_v<const first_type>
+	&& is_copy_assignable_v<const second_type>
+      {
+	first = __p.first;
+	second = __p.second;
+	return *this;
+      }
+
+      /// Move assignment operator
+      constexpr const pair&
+      operator=(pair&& __p) const
+      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;
+      }
+
+      /// Converting assignment from a const `pair<U1, U2>` lvalue
+      template<typename _U1, typename _U2>
+	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;
+	}
+
+      /// Converting assignment from a non-const `pair<U1, U2>` rvalue
+      template<typename _U1, typename _U2>
+	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 // C++23
+#else // !__cpp_lib_concepts
       // C++11/14/17 implementation using enable_if, partially constexpr.
 
       /** The default constructor creates @c first and @c second using their
@@ -710,6 +792,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     noexcept(noexcept(__x.swap(__y)))
     { __x.swap(__y); }
 
+#if __cplusplus > 202002L
+  template<typename _T1, typename _T2>
+    requires is_swappable<const _T1>::value && is_swappable<const _T2>::value
+    constexpr void
+    swap(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)
+    noexcept(noexcept(__x.swap(__y)))
+    { __x.swap(__y); }
+#endif // C++23
+
 #if __cplusplus > 201402L || !defined(__STRICT_ANSI__) // c++1z or gnu++11
   template<typename _T1, typename _T2>
     typename enable_if<!__and_<__is_swappable<_T1>,
@@ -918,6 +1009,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     get(const pair<_Up, _Tp>&& __p) noexcept
     { return std::move(__p.second); }
 
+#if __cplusplus > 202002L
+  template<typename _T1, typename _T2, typename _U1, typename _U2,
+	   template<typename> class _TQual, template<typename> class _UQual>
+    requires requires { typename pair<common_reference_t<_TQual<_T1>, _UQual<_U1>>,
+				      common_reference_t<_TQual<_T2>, _UQual<_U2>>>; }
+  struct basic_common_reference<pair<_T1, _T2>, pair<_U1, _U2>, _TQual, _UQual>
+  {
+    using type = pair<common_reference_t<_TQual<_T1>, _UQual<_U1>>,
+		      common_reference_t<_TQual<_T2>, _UQual<_U2>>>;
+  };
+
+  template<typename _T1, typename _T2, typename _U1, typename _U2>
+    requires requires { typename pair<common_type_t<_T1, _U1>, common_type_t<_T2, _U2>>; }
+  struct common_type<pair<_T1, _T2>, pair<_U1, _U2>>
+  { using type = pair<common_type_t<_T1, _U1>, common_type_t<_T2, _U2>>; };
+#endif
+
 #endif // C++14
   /// @}
 #endif // C++11
diff --git a/libstdc++-v3/include/bits/uses_allocator_args.h b/libstdc++-v3/include/bits/uses_allocator_args.h
index 09cdbf1aaa8..3528e4cc4fa 100644
--- a/libstdc++-v3/include/bits/uses_allocator_args.h
+++ b/libstdc++-v3/include/bits/uses_allocator_args.h
@@ -107,6 +107,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     constexpr auto
     uses_allocator_construction_args(const _Alloc&, pair<_Up, _Vp>&&) noexcept;
 
+#if __cplusplus > 202002L
+  template<_Std_pair _Tp, typename _Alloc, typename _Up, typename _Vp>
+    constexpr auto
+    uses_allocator_construction_args(const _Alloc&,
+				     pair<_Up, _Vp>&) noexcept;
+
+  template<_Std_pair _Tp, typename _Alloc, typename _Up, typename _Vp>
+    constexpr auto
+    uses_allocator_construction_args(const _Alloc&, const pair<_Up, _Vp>&&) noexcept;
+#endif
+
   template<_Std_pair _Tp, typename _Alloc, typename _Tuple1, typename _Tuple2>
     constexpr auto
     uses_allocator_construction_args(const _Alloc& __a, piecewise_construct_t,
@@ -181,6 +192,36 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    std::move(__pr).second));
     }
 
+#if __cplusplus > 202002L
+  template<_Std_pair _Tp, typename _Alloc, typename _Up, typename _Vp>
+    constexpr auto
+    uses_allocator_construction_args(const _Alloc& __a,
+				     pair<_Up, _Vp>& __pr) noexcept
+    {
+      using _Tp1 = typename _Tp::first_type;
+      using _Tp2 = typename _Tp::second_type;
+
+      return std::make_tuple(piecewise_construct,
+	  std::uses_allocator_construction_args<_Tp1>(__a, __pr.first),
+	  std::uses_allocator_construction_args<_Tp2>(__a, __pr.second));
+    }
+
+  template<_Std_pair _Tp, typename _Alloc, typename _Up, typename _Vp>
+    constexpr auto
+    uses_allocator_construction_args(const _Alloc& __a,
+				     const pair<_Up, _Vp>&& __pr) noexcept
+    {
+      using _Tp1 = typename _Tp::first_type;
+      using _Tp2 = typename _Tp::second_type;
+
+      return std::make_tuple(piecewise_construct,
+	  std::uses_allocator_construction_args<_Tp1>(__a,
+	    std::move(__pr).first),
+	  std::uses_allocator_construction_args<_Tp2>(__a,
+	    std::move(__pr).second));
+    }
+#endif
+
   template<typename _Tp, typename _Alloc, typename... _Args>
     constexpr _Tp
     make_obj_using_allocator(const _Alloc& __a, _Args&&... __args)
diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple
index d0c168fd7e2..812e70d17be 100644
--- a/libstdc++-v3/include/std/tuple
+++ b/libstdc++-v3/include/std/tuple
@@ -316,6 +316,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 		(_Tuple_impl<_Idx, _UHead, _UTails...>::_M_head(__in)))
 	{ }
 
+#if __cplusplus > 202002L
+      template<typename... _UElements>
+	constexpr
+	_Tuple_impl(_Tuple_impl<_Idx, _UElements...>& __in)
+	: _Inherited(_Tuple_impl<_Idx, _UElements...>::_M_tail(__in)),
+	  _Base(_Tuple_impl<_Idx, _UElements...>::_M_head(__in))
+	{ }
+
+      template<typename _UHead, typename... _UTails>
+	constexpr
+	_Tuple_impl(const _Tuple_impl<_Idx, _UHead, _UTails...>&& __in)
+	: _Inherited(std::move
+		     (_Tuple_impl<_Idx, _UHead, _UTails...>::_M_tail(__in))),
+	  _Base(std::forward<const _UHead>
+		(_Tuple_impl<_Idx, _UHead, _UTails...>::_M_head(__in)))
+	{ }
+#endif
+
       template<typename _Alloc>
 	_GLIBCXX20_CONSTEXPR
 	_Tuple_impl(allocator_arg_t __tag, const _Alloc& __a)
@@ -379,6 +397,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 		(_Tuple_impl<_Idx, _UHead, _UTails...>::_M_head(__in)))
 	{ }
 
+#if __cplusplus > 202002L
+      template<typename _Alloc, typename _UHead, typename... _UTails>
+	constexpr
+	_Tuple_impl(allocator_arg_t __tag, const _Alloc& __a,
+		    _Tuple_impl<_Idx, _UHead, _UTails...>& __in)
+	: _Inherited(__tag, __a,
+		     _Tuple_impl<_Idx, _UHead, _UTails...>::_M_tail(__in)),
+	  _Base(__use_alloc<_Head, _Alloc, _UHead&>(__a),
+		_Tuple_impl<_Idx, _UHead, _UTails...>::_M_head(__in))
+	{ }
+
+      template<typename _Alloc, typename _UHead, typename... _UTails>
+	constexpr
+	_Tuple_impl(allocator_arg_t __tag, const _Alloc& __a,
+		    const _Tuple_impl<_Idx, _UHead, _UTails...>&& __in)
+	: _Inherited(__tag, __a, std::move
+		     (_Tuple_impl<_Idx, _UHead, _UTails...>::_M_tail(__in))),
+	  _Base(__use_alloc<_Head, _Alloc, const _UHead>(__a),
+		std::forward<const _UHead>
+		(_Tuple_impl<_Idx, _UHead, _UTails...>::_M_head(__in)))
+	{ }
+#endif
+
       template<typename... _UElements>
 	_GLIBCXX20_CONSTEXPR
 	void
@@ -400,6 +441,27 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	      std::move(_Tuple_impl<_Idx, _UHead, _UTails...>::_M_tail(__in)));
 	}
 
+#if __cplusplus > 202002L
+      template<typename... _UElements>
+	constexpr void
+	_M_assign(const _Tuple_impl<_Idx, _UElements...>& __in) const
+	{
+	  _M_head(*this) = _Tuple_impl<_Idx, _UElements...>::_M_head(__in);
+	  _M_tail(*this)._M_assign(
+	      _Tuple_impl<_Idx, _UElements...>::_M_tail(__in));
+	}
+
+      template<typename _UHead, typename... _UTails>
+	constexpr void
+	_M_assign(_Tuple_impl<_Idx, _UHead, _UTails...>&& __in) const
+	{
+	  _M_head(*this) = std::forward<_UHead>
+	    (_Tuple_impl<_Idx, _UHead, _UTails...>::_M_head(__in));
+	  _M_tail(*this)._M_assign(
+	      std::move(_Tuple_impl<_Idx, _UHead, _UTails...>::_M_tail(__in)));
+	}
+#endif
+
     protected:
       _GLIBCXX20_CONSTEXPR
       void
@@ -409,6 +471,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	swap(_M_head(*this), _M_head(__in));
 	_Inherited::_M_swap(_M_tail(__in));
       }
+
+#if __cplusplus > 202002L
+      constexpr void
+      _M_swap(const _Tuple_impl& __in) const
+      {
+	using std::swap;
+	swap(_M_head(*this), _M_head(__in));
+	_Inherited::_M_swap(_M_tail(__in));
+      }
+#endif
     };
 
   // Basis case of inheritance recursion.
@@ -469,6 +541,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	: _Base(std::forward<_UHead>(_Tuple_impl<_Idx, _UHead>::_M_head(__in)))
 	{ }
 
+#if __cplusplus > 202002L
+      template<typename _UHead>
+	constexpr
+	_Tuple_impl(_Tuple_impl<_Idx, _UHead>& __in)
+	: _Base(_Tuple_impl<_Idx, _UHead>::_M_head(__in))
+	{ }
+
+      template<typename _UHead>
+	constexpr
+	_Tuple_impl(const _Tuple_impl<_Idx, _UHead>&& __in)
+	: _Base(std::forward<const _UHead>(_Tuple_impl<_Idx, _UHead>::_M_head(__in)))
+	{ }
+#endif
+
       template<typename _Alloc>
 	_GLIBCXX20_CONSTEXPR
 	_Tuple_impl(allocator_arg_t __tag, const _Alloc& __a)
@@ -521,6 +607,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 		std::forward<_UHead>(_Tuple_impl<_Idx, _UHead>::_M_head(__in)))
 	{ }
 
+#if __cplusplus > 202002L
+      template<typename _Alloc, typename _UHead>
+	constexpr
+	_Tuple_impl(allocator_arg_t __tag, const _Alloc& __a,
+		    _Tuple_impl<_Idx, _UHead>& __in)
+	: _Base(__use_alloc<_Head, _Alloc, _UHead&>(__a),
+		_Tuple_impl<_Idx, _UHead>::_M_head(__in))
+	{ }
+
+      template<typename _Alloc, typename _UHead>
+	constexpr
+	_Tuple_impl(allocator_arg_t __tag, const _Alloc& __a,
+		    const _Tuple_impl<_Idx, _UHead>&& __in)
+	: _Base(__use_alloc<_Head, _Alloc, const _UHead>(__a),
+		std::forward<const _UHead>(_Tuple_impl<_Idx, _UHead>::_M_head(__in)))
+	{ }
+#endif
+
       template<typename _UHead>
 	_GLIBCXX20_CONSTEXPR
 	void
@@ -538,6 +642,23 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    = std::forward<_UHead>(_Tuple_impl<_Idx, _UHead>::_M_head(__in));
 	}
 
+#if __cplusplus > 202002L
+      template<typename _UHead>
+	constexpr void
+	_M_assign(const _Tuple_impl<_Idx, _UHead>& __in) const
+	{
+	  _M_head(*this) = _Tuple_impl<_Idx, _UHead>::_M_head(__in);
+	}
+
+      template<typename _UHead>
+	constexpr void
+	_M_assign(_Tuple_impl<_Idx, _UHead>&& __in) const
+	{
+	  _M_head(*this)
+	    = std::forward<_UHead>(_Tuple_impl<_Idx, _UHead>::_M_head(__in));
+	}
+#endif
+
     protected:
       _GLIBCXX20_CONSTEXPR
       void
@@ -546,6 +667,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	using std::swap;
 	swap(_M_head(*this), _M_head(__in));
       }
+
+#if __cplusplus > 202002L
+      constexpr void
+      _M_swap(const _Tuple_impl& __in) const
+      {
+	using std::swap;
+	swap(_M_head(*this), _M_head(__in));
+      }
+#endif
     };
 
   // Concept utility functions, reused in conditionally-explicit
@@ -728,6 +858,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	static constexpr bool __use_other_ctor()
 	{ return _UseOtherCtor<_Tuple>::value; }
 
+#if __cplusplus > 202002L
+      template<typename... _Args>
+	static constexpr bool __constructible
+	  = _TCC<true>::template __constructible<_Args...>;
+
+      template<typename... _Args>
+	static constexpr bool __convertible
+	  = _TCC<true>::template __convertible<_Args...>;
+#endif
+
     public:
       template<typename _Dummy = void,
 	       _ImplicitDefaultCtor<is_void<_Dummy>::value> = true>
@@ -815,6 +955,29 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	noexcept(__nothrow_constructible<_UElements...>())
 	: _Inherited(static_cast<_Tuple_impl<0, _UElements...>&&>(__in)) { }
 
+#if __cplusplus > 202002L
+      template<typename... _UElements>
+	requires (sizeof...(_Elements) == sizeof...(_UElements))
+	  && (!__use_other_ctor<tuple<_UElements...>&>())
+	  && __constructible<_UElements&...>
+	explicit(!__convertible<_UElements&...>)
+	constexpr
+	tuple(tuple<_UElements...>& __in)
+	noexcept(__nothrow_constructible<_UElements&...>())
+	: _Inherited(static_cast<_Tuple_impl<0, _UElements...>&>(__in))
+	{ }
+
+      template<typename... _UElements>
+	requires (sizeof...(_Elements) == sizeof...(_UElements))
+	  && (!__use_other_ctor<const tuple<_UElements...>&&>())
+	  && __constructible<const _UElements...>
+	explicit(!__convertible<const _UElements...>)
+	constexpr
+	tuple(const tuple<_UElements...>&& __in)
+	noexcept(__nothrow_constructible<const _UElements...>())
+	: _Inherited(static_cast<const _Tuple_impl<0, _UElements...>&&>(__in)) { }
+#endif
+
       // Allocator-extended constructors.
 
       template<typename _Alloc,
@@ -913,6 +1076,32 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	             static_cast<_Tuple_impl<0, _UElements...>&&>(__in))
 	{ }
 
+#if __cplusplus > 202002L
+      template<typename _Alloc, typename... _UElements>
+	requires (sizeof...(_Elements) == sizeof...(_UElements))
+	  && (!__use_other_ctor<tuple<_UElements...>&>())
+	  && __constructible<_UElements&...>
+	explicit(!__convertible<_UElements&...>)
+	constexpr
+	tuple(allocator_arg_t __tag, const _Alloc& __a,
+	      tuple<_UElements...>& __in)
+	: _Inherited(__tag, __a,
+	             static_cast<_Tuple_impl<0, _UElements...>&>(__in))
+	{ }
+
+      template<typename _Alloc, typename... _UElements>
+	requires (sizeof...(_Elements) == sizeof...(_UElements))
+	  && (!__use_other_ctor<const tuple<_UElements...>>())
+	  && __constructible<const _UElements...>
+	explicit(!__convertible<const _UElements...>)
+	constexpr
+	tuple(allocator_arg_t __tag, const _Alloc& __a,
+	      const tuple<_UElements...>&& __in)
+	: _Inherited(__tag, __a,
+	             static_cast<const _Tuple_impl<0, _UElements...>&&>(__in))
+	{ }
+#endif
+
       // tuple assignment
 
       _GLIBCXX20_CONSTEXPR
@@ -957,12 +1146,57 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  return *this;
 	}
 
+#if __cplusplus > 202002L
+      constexpr const tuple&
+      operator=(const tuple& __in) const
+      requires (is_copy_assignable_v<const _Elements> && ...)
+      {
+	this->_M_assign(__in);
+	return *this;
+      }
+
+      constexpr const tuple&
+      operator=(tuple&& __in) const
+      requires (is_assignable_v<const _Elements&, _Elements> && ...)
+      {
+	this->_M_assign(std::move(__in));
+	return *this;
+      }
+
+      template<typename... _UElements>
+	constexpr const tuple&
+	operator=(const tuple<_UElements...>& __in) const
+	requires (sizeof...(_Elements) == sizeof...(_UElements))
+	  && (is_assignable_v<const _Elements&, const _UElements&> && ...)
+	{
+	  this->_M_assign(__in);
+	  return *this;
+	}
+
+      template<typename... _UElements>
+	constexpr const tuple&
+	operator=(tuple<_UElements...>&& __in) const
+	requires (sizeof...(_Elements) == sizeof...(_UElements))
+	  && (is_assignable_v<const _Elements&, _UElements> && ...)
+	{
+	  this->_M_assign(std::move(__in));
+	  return *this;
+	}
+#endif
+
       // tuple swap
       _GLIBCXX20_CONSTEXPR
       void
       swap(tuple& __in)
       noexcept(__and_<__is_nothrow_swappable<_Elements>...>::value)
       { _Inherited::_M_swap(__in); }
+
+#if __cplusplus > 202002L
+      constexpr void
+      swap(const tuple& __in) const
+      noexcept(__and_<__is_nothrow_swappable<const _Elements>...>::value)
+      { _Inherited::_M_swap(__in); }
+#endif
     };
 
 #if __cpp_deduction_guides >= 201606
@@ -985,6 +1219,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     public:
       _GLIBCXX20_CONSTEXPR
       void swap(tuple&) noexcept { /* no-op */ }
+#if __cplusplus > 202002L
+      constexpr void swap(const tuple&) const noexcept { /* no-op */ }
+#endif
       // We need the default since we're going to define no-op
       // allocator constructors.
       tuple() = default;
@@ -1064,6 +1301,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	static constexpr bool __is_alloc_arg()
 	{ return is_same<__remove_cvref_t<_U1>, allocator_arg_t>::value; }
 
+#if __cplusplus > 202002L
+      template<typename _U1, typename _U2>
+	static constexpr bool __constructible
+	  = _TCC<true>::template __constructible<_U1, _U2>;
+
+      template<typename _U1, typename _U2>
+	static constexpr bool __convertible
+	  = _TCC<true>::template __convertible<_U1, _U2>;
+#endif
+
     public:
       template<bool _Dummy = true,
 	       _ImplicitDefaultCtor<_Dummy, _T1, _T2> = true>
@@ -1139,6 +1386,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	noexcept(__nothrow_constructible<_U1, _U2>())
 	: _Inherited(static_cast<_Tuple_impl<0, _U1, _U2>&&>(__in)) { }
 
+#if __cplusplus > 202002L
+      template<typename _U1, typename _U2>
+	requires __constructible<_U1&, _U2&>
+	explicit(!__convertible<_U1&, _U2&>)
+	constexpr
+	tuple(tuple<_U1, _U2>& __in)
+	noexcept(__nothrow_constructible<_U1&, _U2&>())
+	: _Inherited(static_cast<_Tuple_impl<0, _U1, _U2>&>(__in)) { }
+
+      template<typename _U1, typename _U2>
+	requires __constructible<const _U1, const _U2>
+	explicit(!__convertible<const _U1, const _U2>)
+	constexpr
+	tuple(const tuple<_U1, _U2>&& __in)
+	noexcept(__nothrow_constructible<const _U1, const _U2>())
+	: _Inherited(static_cast<const _Tuple_impl<0, _U1, _U2>&&>(__in)) { }
+#endif
+
       template<typename _U1, typename _U2,
 	       _ImplicitCtor<true, const _U1&, const _U2&> = true>
 	constexpr
@@ -1169,6 +1434,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	: _Inherited(std::forward<_U1>(__in.first),
 		     std::forward<_U2>(__in.second)) { }
 
+#if __cplusplus > 202002L
+      template<typename _U1, typename _U2>
+	requires __constructible<_U1&, _U2&>
+	explicit(!__convertible<_U1&, _U2&>)
+	constexpr
+	tuple(pair<_U1, _U2>& __in)
+	noexcept(__nothrow_constructible<_U1&, _U2&>())
+	: _Inherited(__in.first, __in.second) { }
+
+      template<typename _U1, typename _U2>
+	requires __constructible<const _U1, const _U2>
+	explicit(!__convertible<const _U1, const _U2>)
+	constexpr
+	tuple(const pair<_U1, _U2>&& __in)
+	noexcept(__nothrow_constructible<const _U1, const _U2>())
+	: _Inherited(std::forward<const _U1>(__in.first),
+		     std::forward<const _U2>(__in.second)) { }
+#endif
+
       // Allocator-extended constructors.
 
       template<typename _Alloc,
@@ -1252,6 +1536,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	: _Inherited(__tag, __a, static_cast<_Tuple_impl<0, _U1, _U2>&&>(__in))
 	{ }
 
+#if __cplusplus > 202002L
+      template<typename _Alloc, typename _U1, typename _U2>
+	requires __constructible<_U1&, _U2&>
+	explicit(!__convertible<_U1&, _U2&>)
+	constexpr
+	tuple(allocator_arg_t __tag, const _Alloc& __a,
+	      tuple<_U1, _U2>& __in)
+	: _Inherited(__tag, __a,
+	             static_cast<_Tuple_impl<0, _U1, _U2>&>(__in))
+	{ }
+
+      template<typename _Alloc, typename _U1, typename _U2>
+	requires __constructible<const _U1, const _U2>
+	explicit(!__convertible<const _U1, const _U2>)
+	constexpr
+	tuple(allocator_arg_t __tag, const _Alloc& __a,
+	      const tuple<_U1, _U2>&& __in)
+	: _Inherited(__tag, __a,
+	             static_cast<const _Tuple_impl<0, _U1, _U2>&&>(__in))
+	{ }
+#endif
+
       template<typename _Alloc, typename _U1, typename _U2,
 	       _ImplicitCtor<true, const _U1&, const _U2&> = true>
 	_GLIBCXX20_CONSTEXPR
@@ -1282,6 +1588,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	: _Inherited(__tag, __a, std::forward<_U1>(__in.first),
 		     std::forward<_U2>(__in.second)) { }
 
+#if __cplusplus > 202002L
+      template<typename _Alloc, typename _U1, typename _U2>
+	requires __constructible<_U1&, _U2&>
+	explicit(!__convertible<_U1&, _U2&>)
+	constexpr
+	tuple(allocator_arg_t __tag, const _Alloc& __a,
+	      pair<_U1, _U2>& __in)
+	: _Inherited(__tag, __a, __in.first, __in.second) { }
+
+      template<typename _Alloc, typename _U1, typename _U2>
+	requires __constructible<const _U1, const _U2>
+	explicit(!__convertible<const _U1, const _U2>)
+	constexpr
+	tuple(allocator_arg_t __tag, const _Alloc& __a, const pair<_U1, _U2>&& __in)
+	: _Inherited(__tag, __a, std::forward<const _U1>(__in.first),
+		     std::forward<const _U2>(__in.second)) { }
+#endif
+
       // Tuple assignment.
 
       _GLIBCXX20_CONSTEXPR
@@ -1326,6 +1650,44 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  return *this;
 	}
 
+#if __cplusplus > 202002L
+      constexpr const tuple&
+      operator=(const tuple& __in) const
+      requires is_copy_assignable_v<const _T1> && is_copy_assignable_v<const _T2>
+      {
+	this->_M_assign(__in);
+	return *this;
+      }
+
+      constexpr const tuple&
+      operator=(tuple&& __in) const
+      requires is_assignable_v<const _T1&, _T1> && is_assignable_v<const _T2, _T2>
+      {
+	this->_M_assign(std::move(__in));
+	return *this;
+      }
+
+      template<typename _U1, typename _U2>
+	constexpr const tuple&
+	operator=(const tuple<_U1, _U2>& __in) const
+	requires is_assignable_v<const _T1&, const _U1&>
+	  && is_assignable_v<const _T2&, const _U2&>
+	{
+	  this->_M_assign(__in);
+	  return *this;
+	}
+
+      template<typename _U1, typename _U2>
+	constexpr const tuple&
+	operator=(tuple<_U1, _U2>&& __in) const
+	requires is_assignable_v<const _T1&, _U1>
+	  && is_assignable_v<const _T2&, _U2>
+	{
+	  this->_M_assign(std::move(__in));
+	  return *this;
+	}
+#endif
+
       template<typename _U1, typename _U2>
 	_GLIBCXX20_CONSTEXPR
 	__enable_if_t<__assignable<const _U1&, const _U2&>(), tuple&>
@@ -1348,12 +1710,44 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  return *this;
 	}
 
+#if __cplusplus > 202002L
+      template<typename _U1, typename _U2>
+	constexpr const tuple&
+	operator=(const pair<_U1, _U2>& __in) const
+	requires is_assignable_v<const _T1&, const _U1&>
+	  && is_assignable_v<const _T2&, const _U2&>
+	{
+	  this->_M_head(*this) = __in.first;
+	  this->_M_tail(*this)._M_head(*this) = __in.second;
+	  return *this;
+	}
+
+      template<typename _U1, typename _U2>
+	constexpr const tuple&
+	operator=(pair<_U1, _U2>&& __in) const
+	requires is_assignable_v<const _T1&, _U1>
+	  && is_assignable_v<const _T2&, _U2>
+	{
+	  this->_M_head(*this) = std::forward<_U1>(__in.first);
+	  this->_M_tail(*this)._M_head(*this) = std::forward<_U2>(__in.second);
+	  return *this;
+	}
+#endif
+
       _GLIBCXX20_CONSTEXPR
       void
       swap(tuple& __in)
       noexcept(__and_<__is_nothrow_swappable<_T1>,
 		      __is_nothrow_swappable<_T2>>::value)
       { _Inherited::_M_swap(__in); }
+
+#if __cplusplus > 202002L
+      constexpr void
+      swap(const tuple& __in) const
+      noexcept(__and_<__is_nothrow_swappable<const _T1>,
+		      __is_nothrow_swappable<const _T2>>::value)
+      { _Inherited::_M_swap(__in); }
+#endif
     };
 
 
@@ -1781,6 +2175,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     noexcept(noexcept(__x.swap(__y)))
     { __x.swap(__y); }
 
+#if __cplusplus > 202002L
+  template<typename... _Elements>
+    requires (__is_swappable<const _Elements>::value && ...)
+    constexpr void
+    swap(const tuple<_Elements...>& __x, const tuple<_Elements...>& __y)
+    noexcept(noexcept(__x.swap(__y)))
+    { __x.swap(__y); }
+#endif
+
 #if __cplusplus > 201402L || !defined(__STRICT_ANSI__) // c++1z or gnu++11
   template<typename... _Elements>
     _GLIBCXX20_CONSTEXPR
@@ -1905,6 +2308,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     }
 #endif // C++17
 
+#if __cplusplus > 202002L
+  template<typename... _TTypes, typename... _UTypes,
+	   template<typename> class TQual, template<typename> class UQual>
+    requires requires { typename tuple<common_reference_t<TQual<_TTypes>, UQual<_UTypes>>...>; }
+  struct basic_common_reference<tuple<_TTypes...>, tuple<_UTypes...>, TQual, UQual>
+  { using type = tuple<common_reference_t<TQual<_TTypes>, UQual<_UTypes>>...>; };
+
+  template<typename... _TTypes, typename... _UTypes>
+    requires requires { typename tuple<common_type_t<_TTypes, _UTypes>...>; }
+  struct common_type<tuple<_TTypes...>, tuple<_UTypes...>>
+  { using type = tuple<common_type_t<_TTypes, _UTypes>...>; };
+#endif
+
   /// @}
 
 _GLIBCXX_END_NAMESPACE_VERSION
diff --git a/libstdc++-v3/testsuite/20_util/pair/p2321.cc b/libstdc++-v3/testsuite/20_util/pair/p2321.cc
new file mode 100644
index 00000000000..4f436ee03d6
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/pair/p2321.cc
@@ -0,0 +1,208 @@
+// Verify P2321R2 "zip" enhancements to std::pair.
+// { dg-options "-std=gnu++23" }
+// { dg-do run { target c++23 } }
+
+#include <utility>
+#include <testsuite_hooks.h>
+
+using std::pair;
+
+struct A { };
+
+constexpr bool
+test01()
+{
+  struct B { bool v; constexpr B(A&) : v(true) { } };
+
+  // template<class U1, class U2>
+  //   constexpr explicit(false) pair(pair<U1, U2>&);
+
+  pair<A, int> p2a0;
+  pair<B, int> p2b0 = p2a0;
+  VERIFY( std::get<0>(p2b0).v );
+
+  pair<int, A> p2a1;
+  pair<int, B> p2b1 = p2a1;
+  VERIFY( std::get<1>(p2b1).v );
+
+  return true;
+}
+
+constexpr bool
+test02()
+{
+  struct B { bool v; explicit constexpr B(A&) : v(true) { } };
+
+  // template<class U1, class U2>
+  //   constexpr explicit(true) pair(pair<U1, U2>&);
+
+  static_assert(!std::is_convertible_v<pair<A, int>&, pair<B, int>>);
+  static_assert(!std::is_convertible_v<pair<int, A>&, pair<int, B>>);
+
+  pair<A, int> p2a0;
+  pair<B, int> p2b0(p2a0);
+  VERIFY( std::get<0>(p2b0).v );
+
+  pair<int, A> p2a1;
+  pair<int, B> p2b1(p2a1);
+  VERIFY( std::get<1>(p2b1).v );
+
+  return true;
+}
+
+constexpr bool
+test03()
+{
+  struct B { bool v; constexpr B(const A&&) : v(true) { } };
+
+  // template<class U1, class U2>
+  //   constexpr explicit(false) pair(const pair<U1, U2>&&);
+
+  const pair<A, int> p2a0;
+  pair<B, int> p2b0 = std::move(p2a0);
+  VERIFY( std::get<0>(p2b0).v );
+
+  const pair<int, A> p2a1;
+  pair<int, B> p2b1 = std::move(p2a1);
+  VERIFY( std::get<1>(p2b1).v );
+
+  return true;
+}
+
+constexpr bool
+test04()
+{
+  struct B { bool v; explicit constexpr B(const A&&) : v(true) { } };
+
+  // template<class U1, class U2>
+  //   constexpr explicit(true) pair(const pair<U1, U2>&&);
+
+  static_assert(!std::is_convertible_v<const pair<A, int>&&, pair<B, int>>);
+  static_assert(!std::is_convertible_v<const pair<int, A>&&, pair<int, B>>);
+
+  const pair<A, int> p2a0;
+  pair<B, int> p2b0(std::move(p2a0));
+  VERIFY( std::get<0>(p2b0).v );
+
+  const pair<int, A> p2a1;
+  pair<int, B> p2b1(std::move(p2a1));
+  VERIFY( std::get<1>(p2b1).v );
+
+  return true;
+}
+
+constexpr bool
+test05()
+{
+  struct B
+  {
+    mutable bool v;
+    constexpr const B& operator=(const A&) const { v = true; return *this; }
+  };
+
+  // template<class U1, class U2>
+  //   constexpr const pair& operator=(const pair<U1, U2>&) const;
+
+  const pair<A, A> p2a;
+  const pair<B, B> p2b;
+  p2b = p2a;
+
+  return true;
+}
+
+constexpr bool
+test06()
+{
+  struct B
+  {
+    mutable bool v;
+    constexpr const B& operator=(A&&) const { v = true; return *this; }
+  };
+
+  // template<class U1, class U2>
+  //   constexpr const pair& operator=(pair<U1, U2>&&) const;
+
+  pair<A, A> p2a;
+  const pair<B, B> p2b;
+  p2b = std::move(p2a);
+
+  return true;
+}
+
+constexpr bool
+test07()
+{
+  struct B
+  {
+    mutable bool v;
+    constexpr const B& operator=(const B&) const { v = true; return *this; }
+  };
+
+  // constexpr const pair& operator=(const pair&) const;
+
+  const pair<B, B> t2a;
+  const pair<B, B> t2b;
+  t2b = t2a;
+  VERIFY( std::get<0>(t2b).v );
+  VERIFY( std::get<1>(t2b).v );
+
+  return true;
+}
+
+constexpr bool
+test08()
+{
+  struct B
+  {
+    mutable bool v;
+    constexpr const B& operator=(B&&) const { v = true; return *this; }
+  };
+
+  // constexpr const pair& operator=(pair&&) const;
+
+  pair<B, B> t2a;
+  const pair<B, B> t2b;
+  t2b = std::move(t2a);
+  VERIFY( std::get<0>(t2b).v );
+  VERIFY( std::get<1>(t2b).v );
+
+  return true;
+}
+
+struct S
+{
+  mutable int v = 0;
+  friend constexpr void swap(S&& x, S&& y) = delete;
+  friend constexpr void swap(const S& x, const S& y) { ++x.v; ++y.v; }
+};
+
+constexpr bool
+test09()
+{
+  const pair<S, S> t2, u2;
+  std::swap(t2, u2);
+  VERIFY( std::get<0>(t2).v == 1 );
+  VERIFY( std::get<0>(u2).v == 1 );
+  VERIFY( std::get<1>(t2).v == 1 );
+  VERIFY( std::get<1>(u2).v == 1 );
+
+  static_assert(!std::is_swappable_v<const pair<A, int>&>);
+  static_assert(!std::is_swappable_v<const pair<int, A>&>);
+
+  return true;
+}
+
+int
+main()
+{
+  static_assert(test01());
+  static_assert(test02());
+  static_assert(test03());
+  static_assert(test04());
+  // FIXME: G++ doesn't support reading mutable members during constexpr (PR c++/92505).
+  test05();
+  test06();
+  test07();
+  test08();
+  test09();
+}
diff --git a/libstdc++-v3/testsuite/20_util/tuple/p2321.cc b/libstdc++-v3/testsuite/20_util/tuple/p2321.cc
new file mode 100644
index 00000000000..80fc23cf9d4
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/tuple/p2321.cc
@@ -0,0 +1,664 @@
+// Verify P2321R2 "zip" enhancements to std::tuple.
+// { dg-options "-std=gnu++23" }
+// { dg-do run { target c++23 } }
+
+#include <tuple>
+#include <memory>
+#include <testsuite_hooks.h>
+
+using std::tuple;
+using std::pair;
+using std::allocator;
+using std::allocator_arg_t;
+using std::allocator_arg;
+
+namespace alloc {
+  struct B01;
+  struct B02;
+  struct B03;
+  struct B04;
+}
+
+template<> struct std::uses_allocator<alloc::B01, allocator<int>> : std::true_type { };
+template<> struct std::uses_allocator<alloc::B02, allocator<int>> : std::true_type { };
+template<> struct std::uses_allocator<alloc::B03, allocator<int>> : std::true_type { };
+template<> struct std::uses_allocator<alloc::B04, allocator<int>> : std::true_type { };
+
+struct A { };
+
+constexpr bool
+test01()
+{
+  struct B { bool v; constexpr B(A&) : v(true) { } };
+
+  // template<class... UTypes>
+  //   constexpr explicit(false) tuple(tuple<UTypes...>&);
+
+  tuple<A> t1a;
+  tuple<B> t1b = t1a;
+  VERIFY( std::get<0>(t1b).v );
+
+  tuple<A, int> t2a0;
+  tuple<B, int> t2b0 = t2a0;
+  VERIFY( std::get<0>(t2b0).v );
+
+  tuple<int, A> t2a1;
+  tuple<int, B> t2b1 = t2a1;
+  VERIFY( std::get<1>(t2b1).v );
+
+  tuple<A, int, int> t3a0;
+  tuple<B, int, int> t3b0 = t3a0;
+  VERIFY( std::get<0>(t3b0).v );
+
+  tuple<int, A, int> t3a1;
+  tuple<int, B, int> t3b1 = t3a1;
+  VERIFY( std::get<1>(t3b1).v );
+
+  tuple<int, int, A> t3a2;
+  tuple<int, int, B> t3b2 = t3a2;
+  VERIFY( std::get<2>(t3b2).v );
+
+  // template<class... UTypes>
+  //   constexpr explicit(false) tuple(pair<UTypes...>&);
+
+  pair<A, int> p2a0;
+  tuple<B, int> p2b0 = p2a0;
+  VERIFY( std::get<0>(p2b0).v );
+
+  pair<int, A> p2a1;
+  tuple<int, B> p2b1 = p2a1;
+  VERIFY( std::get<1>(p2b1).v );
+
+  return true;
+}
+
+namespace alloc
+{
+  struct B01
+  {
+    bool v;
+    B01(A&);
+    constexpr B01(allocator_arg_t, allocator<int>, A&) : v(true) { }
+  };
+
+  constexpr bool
+  test01()
+  {
+    using B = B01;
+
+    // template<class Alloc, class... UTypes>
+    //   constexpr explicit(false)
+    //     tuple(allocator_arg_t, const Alloc& a, tuple<UTypes...>&);
+
+    tuple<A> t1a;
+    tuple<B> t1b = {allocator_arg, allocator<int>{}, t1a};
+    VERIFY( std::get<0>(t1b).v );
+
+    tuple<A, int> t2a0;
+    tuple<B, int> t2b0 = {allocator_arg, allocator<int>{}, t2a0};
+    VERIFY( std::get<0>(t2b0).v );
+
+    tuple<int, A> t2a1;
+    tuple<int, B> t2b1 = {allocator_arg, allocator<int>{}, t2a1};
+    VERIFY( std::get<1>(t2b1).v );
+
+    tuple<A, int, int> t3a0;
+    tuple<B, int, int> t3b0 = {allocator_arg, allocator<int>{}, t3a0};
+    VERIFY( std::get<0>(t3b0).v );
+
+    tuple<int, A, int> t3a1;
+    tuple<int, B, int> t3b1 = {allocator_arg, allocator<int>{}, t3a1};
+    VERIFY( std::get<1>(t3b1).v );
+
+    tuple<int, int, A> t3a2;
+    tuple<int, int, B> t3b2 = {allocator_arg, allocator<int>{}, t3a2};
+    VERIFY( std::get<2>(t3b2).v );
+
+    // template<class Alloc, class U1, class U2>
+    //   constexpr explicit(false)
+    //     tuple(allocator_arg_t, const Alloc& a, pair<U1, U2>&);
+
+    pair<A, int> p2a0;
+    tuple<B, int> p2b0 = {allocator_arg, allocator<int>{}, p2a0};
+    VERIFY( std::get<0>(p2b0).v );
+
+    pair<int, A> p2a1;
+    tuple<int, B> p2b1 = {allocator_arg, allocator<int>{}, p2a1};
+    VERIFY( std::get<1>(p2b1).v );
+
+    return true;
+  }
+}
+
+constexpr bool
+test02()
+{
+  struct B { bool v; explicit constexpr B(A&) : v(true) { } };
+
+  // template<class... UTypes>
+  //   constexpr explicit(true) tuple(tuple<UTypes...>&);
+
+  static_assert(!std::is_convertible_v<tuple<A>&, tuple<B>>);
+
+  tuple<A> t1a;
+  tuple<B> t1b(t1a);
+  VERIFY( std::get<0>(t1b).v );
+
+  static_assert(!std::is_convertible_v<tuple<A, int>&, tuple<B, int>>);
+  static_assert(!std::is_convertible_v<tuple<int, A>&, tuple<int, B>>);
+
+  tuple<A, int> t2a0;
+  tuple<B, int> t2b0(t2a0);
+  VERIFY( std::get<0>(t2b0).v );
+
+  tuple<int, A> t2a1;
+  tuple<int, B> t2b1(t2a1);
+  VERIFY( std::get<1>(t2b1).v );
+
+  static_assert(!std::is_convertible_v<tuple<A, int, int>&, tuple<B, int, int>>);
+  static_assert(!std::is_convertible_v<tuple<int, A, int>&, tuple<int, B, int>>);
+  static_assert(!std::is_convertible_v<tuple<int, int, A>&, tuple<int, int, B>>);
+
+  tuple<A, int, int> t3a0;
+  tuple<B, int, int> t3b0(t3a0);
+  VERIFY( std::get<0>(t3b0).v );
+
+  tuple<int, A, int> t3a1;
+  tuple<int, B, int> t3b1(t3a1);
+  VERIFY( std::get<1>(t3b1).v );
+
+  tuple<int, int, A> t3a2;
+  tuple<int, int, B> t3b2(t3a2);
+  VERIFY( std::get<2>(t3b2).v );
+
+  // template<class... UTypes>
+  //   constexpr explicit(true) tuple(pair<UTypes...>&);
+
+  static_assert(!std::is_convertible_v<pair<A, int>&, tuple<B, int>>);
+  static_assert(!std::is_convertible_v<pair<int, A>&, tuple<int, B>>);
+
+  pair<A, int> p2a0;
+  tuple<B, int> p2b0(p2a0);
+  VERIFY( std::get<0>(p2b0).v );
+
+  pair<int, A> p2a1;
+  tuple<int, B> p2b1(p2a1);
+  VERIFY( std::get<1>(p2b1).v );
+
+  return true;
+}
+
+namespace alloc
+{
+  struct B02
+  {
+    bool v;
+    explicit B02(A&);
+    explicit constexpr B02(allocator_arg_t, allocator<int>, A&) : v(true) { }
+  };
+
+  constexpr bool
+  test02()
+  {
+    using B = B02;
+
+    // template<class Alloc, class... UTypes>
+    //   constexpr explicit(true)
+    //     tuple(allocator_arg_t, const Alloc& a, tuple<UTypes...>&);
+
+    tuple<A> t1a;
+    tuple<B> t1b(allocator_arg, allocator<int>{}, t1a);
+    VERIFY( std::get<0>(t1b).v );
+
+    tuple<A, int> t2a0;
+    tuple<B, int> t2b0(allocator_arg, allocator<int>{}, t2a0);
+    VERIFY( std::get<0>(t2b0).v );
+
+    tuple<int, A> t2a1;
+    tuple<int, B> t2b1(allocator_arg, allocator<int>{}, t2a1);
+    VERIFY( std::get<1>(t2b1).v );
+
+    tuple<A, int, int> t3a0;
+    tuple<B, int, int> t3b0(allocator_arg, allocator<int>{}, t3a0);
+    VERIFY( std::get<0>(t3b0).v );
+
+    tuple<int, A, int> t3a1;
+    tuple<int, B, int> t3b1(allocator_arg, allocator<int>{}, t3a1);
+    VERIFY( std::get<1>(t3b1).v );
+
+    tuple<int, int, A> t3a2;
+    tuple<int, int, B> t3b2(allocator_arg, allocator<int>{}, t3a2);
+    VERIFY( std::get<2>(t3b2).v );
+
+    // template<class Alloc, class U1, class U2>
+    //   constexpr explicit(true)
+    //     tuple(allocator_arg_t, const Alloc& a, pair<U1, U2>&);
+
+    pair<A, int> p2a0;
+    tuple<B, int> p2b0(allocator_arg, allocator<int>{}, p2a0);
+    VERIFY( std::get<0>(p2b0).v );
+
+    pair<int, A> p2a1;
+    tuple<int, B> p2b1(allocator_arg, allocator<int>{}, p2a1);
+    VERIFY( std::get<1>(p2b1).v );
+
+    return true;
+  }
+} // namespace alloc
+
+constexpr bool
+test03()
+{
+  struct B { bool v; constexpr B(const A&&) : v(true) { } };
+
+  // template<class... UTypes>
+  //   constexpr explicit(false) tuple(const tuple<UTypes...>&&);
+
+  const tuple<A> t1a;
+  tuple<B> t1b = std::move(t1a);
+  VERIFY( std::get<0>(t1b).v );
+
+  const tuple<A, int> t2a0;
+  tuple<B, int> t2b0 = std::move(t2a0);
+  VERIFY( std::get<0>(t2b0).v );
+
+  const tuple<int, A> t2a1;
+  tuple<int, B> t2b1 = std::move(t2a1);
+  VERIFY( std::get<1>(t2b1).v );
+
+  const tuple<A, int, int> t3a0;
+  tuple<B, int, int> t3b0 = std::move(t3a0);
+  VERIFY( std::get<0>(t3b0).v );
+
+  const tuple<int, A, int> t3a1;
+  tuple<int, B, int> t3b1 = std::move(t3a1);
+  VERIFY( std::get<1>(t3b1).v );
+
+  const tuple<int, int, A> t3a2;
+  tuple<int, int, B> t3b2 = std::move(t3a2);
+  VERIFY( std::get<2>(t3b2).v );
+
+  // template<class... UTypes>
+  //   constexpr explicit(false) tuple(const pair<UTypes...>&&);
+
+  const pair<A, int> p2a0;
+  tuple<B, int> p2b0 = std::move(p2a0);
+  VERIFY( std::get<0>(p2b0).v );
+
+  const pair<int, A> p2a1;
+  tuple<int, B> p2b1 = std::move(p2a1);
+  VERIFY( std::get<1>(p2b1).v );
+
+  return true;
+}
+
+namespace alloc
+{
+  struct B03
+  {
+    bool v;
+    B03(const A&&);
+    constexpr B03(allocator_arg_t, allocator<int>, const A&&) : v(true) { }
+  };
+
+  constexpr bool
+  test03()
+  {
+    using B = B03;
+
+    // template<class Alloc, class... UTypes>
+    //   constexpr explicit(false)
+    //     tuple(allocator_arg_t, const Alloc& a, const tuple<UTypes...>&&);
+
+    const tuple<A> t1a;
+    tuple<B> t1b = {allocator_arg, allocator<int>{}, std::move(t1a)};
+    VERIFY( std::get<0>(t1b).v );
+
+    const tuple<A, int> t2a0;
+    tuple<B, int> t2b0 = {allocator_arg, allocator<int>{}, std::move(t2a0)};
+    VERIFY( std::get<0>(t2b0).v );
+
+    const tuple<int, A> t2a1;
+    tuple<int, B> t2b1 = {allocator_arg, allocator<int>{}, std::move(t2a1)};
+    VERIFY( std::get<1>(t2b1).v );
+
+    const tuple<A, int, int> t3a0;
+    tuple<B, int, int> t3b0 = {allocator_arg, allocator<int>{}, std::move(t3a0)};
+    VERIFY( std::get<0>(t3b0).v );
+
+    const tuple<int, A, int> t3a1;
+    tuple<int, B, int> t3b1 = {allocator_arg, allocator<int>{}, std::move(t3a1)};
+    VERIFY( std::get<1>(t3b1).v );
+
+    const tuple<int, int, A> t3a2;
+    tuple<int, int, B> t3b2 = {allocator_arg, allocator<int>{}, std::move(t3a2)};
+    VERIFY( std::get<2>(t3b2).v );
+
+    // template<class Alloc, class U1, class U2>
+    //   constexpr explicit(false)
+    //     tuple(allocator_arg_t, const Alloc& a, const pair<U1, U2>&&);
+
+    const pair<A, int> p2a0;
+    tuple<B, int> p2b0 = {allocator_arg, allocator<int>{}, std::move(p2a0)};
+    VERIFY( std::get<0>(p2b0).v );
+
+    const pair<int, A> p2a1;
+    tuple<int, B> p2b1 = {allocator_arg, allocator<int>{}, std::move(p2a1)};
+    VERIFY( std::get<1>(p2b1).v );
+
+    return true;
+  }
+};
+
+constexpr bool
+test04()
+{
+  struct B { bool v; explicit constexpr B(const A&&) : v(true) { } };
+
+  // template<class... UTypes>
+  //   constexpr explicit(true) tuple(const tuple<UTypes...>&&);
+
+  static_assert(!std::is_convertible_v<tuple<A>&, tuple<B>>);
+
+  const tuple<A> t1a;
+  tuple<B> t1b(std::move(t1a));
+  VERIFY( std::get<0>(t1b).v );
+
+  static_assert(!std::is_convertible_v<tuple<A, int>&, tuple<B, int>>);
+  static_assert(!std::is_convertible_v<tuple<int, A>&, tuple<int, B>>);
+
+  const tuple<A, int> t2a0;
+  tuple<B, int> t2b0(std::move(t2a0));
+  VERIFY( std::get<0>(t2b0).v );
+
+  const tuple<int, A> t2a1;
+  tuple<int, B> t2b1(std::move(t2a1));
+  VERIFY( std::get<1>(t2b1).v );
+
+  static_assert(!std::is_convertible_v<tuple<A, int, int>&, tuple<B, int, int>>);
+  static_assert(!std::is_convertible_v<tuple<int, A, int>&, tuple<int, B, int>>);
+  static_assert(!std::is_convertible_v<tuple<int, int, A>&, tuple<int, int, B>>);
+
+  const tuple<A, int, int> t3a0;
+  tuple<B, int, int> t3b0(std::move(t3a0));
+  VERIFY( std::get<0>(t3b0).v );
+
+  const tuple<int, A, int> t3a1;
+  tuple<int, B, int> t3b1(std::move(t3a1));
+  VERIFY( std::get<1>(t3b1).v );
+
+  const tuple<int, int, A> t3a2;
+  tuple<int, int, B> t3b2(std::move(t3a2));
+  VERIFY( std::get<2>(t3b2).v );
+
+  // template<class... UTypes>
+  //   constexpr explicit(true) tuple(const pair<UTypes...>&&);
+
+  static_assert(!std::is_convertible_v<pair<A, int>&, tuple<B, int>>);
+  static_assert(!std::is_convertible_v<pair<int, A>&, tuple<int, B>>);
+
+  const pair<A, int> p2a0;
+  tuple<B, int> p2b0(std::move(p2a0));
+  VERIFY( std::get<0>(p2b0).v );
+
+  const pair<int, A> p2a1;
+  tuple<int, B> p2b1(std::move(p2a1));
+  VERIFY( std::get<1>(p2b1).v );
+
+  return true;
+}
+
+namespace alloc
+{
+  struct B04
+  {
+    bool v;
+    explicit B04(const A&&);
+    explicit constexpr B04(allocator_arg_t, allocator<int>, const A&&) : v(true) { }
+  };
+
+  constexpr bool
+  test04()
+  {
+    using B = B04;
+
+    // template<class Alloc, class... UTypes>
+    //   constexpr explicit(true)
+    //     tuple(allocator_arg_t, const Alloc& a, const tuple<UTypes...>&&);
+
+    const tuple<A> t1a;
+    tuple<B> t1b(allocator_arg, allocator<int>{}, std::move(t1a));
+    VERIFY( std::get<0>(t1b).v );
+
+    const tuple<A, int> t2a0;
+    tuple<B, int> t2b0(allocator_arg, allocator<int>{}, std::move(t2a0));
+    VERIFY( std::get<0>(t2b0).v );
+
+    const tuple<int, A> t2a1;
+    tuple<int, B> t2b1(allocator_arg, allocator<int>{}, std::move(t2a1));
+    VERIFY( std::get<1>(t2b1).v );
+
+    const tuple<A, int, int> t3a0;
+    tuple<B, int, int> t3b0(allocator_arg, allocator<int>{}, std::move(t3a0));
+    VERIFY( std::get<0>(t3b0).v );
+
+    const tuple<int, A, int> t3a1;
+    tuple<int, B, int> t3b1(allocator_arg, allocator<int>{}, std::move(t3a1));
+    VERIFY( std::get<1>(t3b1).v );
+
+    const tuple<int, int, A> t3a2;
+    tuple<int, int, B> t3b2(allocator_arg, allocator<int>{}, std::move(t3a2));
+    VERIFY( std::get<2>(t3b2).v );
+
+    // template<class Alloc, class U1, class U2>
+    //   constexpr explicit(true)
+    //     tuple(allocator_arg_t, const Alloc& a, const pair<U1, U2>&&);
+
+    tuple<B, int> p2b0(allocator_arg, allocator<int>{}, std::move(t2a0));
+    VERIFY( std::get<0>(p2b0).v );
+
+    tuple<int, B> p2b1(allocator_arg, allocator<int>{}, std::move(t2a1));
+    VERIFY( std::get<1>(p2b1).v );
+
+    return true;
+  }
+};
+
+constexpr bool
+test05()
+{
+  struct B
+  {
+    mutable bool v;
+    constexpr const B& operator=(const A&) const { v = true; return *this; }
+  };
+
+  // template<class... UTypes>
+  //   constexpr const tuple& operator=(const tuple<UTypes...>&) const;
+
+  const tuple<A> t1a;
+  const tuple<B> t1b;
+  t1b = t1a;
+  VERIFY( std::get<0>(t1b).v );
+
+  const tuple<A, A> t2a;
+  const tuple<B, B> t2b;
+  t2b = t2a;
+  VERIFY( std::get<0>(t2b).v );
+  VERIFY( std::get<1>(t2b).v );
+
+  const tuple<A, A, A> t3a;
+  const tuple<B, B, B> t3b;
+  t3b = t3a;
+  VERIFY( std::get<0>(t3b).v );
+  VERIFY( std::get<1>(t3b).v );
+  VERIFY( std::get<2>(t3b).v );
+
+  // template<class U1, class U2>
+  //   constexpr const tuple& operator=(const pair<U1, U2>&) const;
+
+  const pair<A, A> p2a;
+  const tuple<B, B> p2b;
+  p2b = p2a;
+
+  return true;
+}
+
+constexpr bool
+test06()
+{
+  struct B
+  {
+    mutable bool v;
+    constexpr const B& operator=(A&&) const { v = true; return *this; }
+  };
+
+  // template<class... UTypes>
+  //   constexpr const tuple& operator=(tuple<UTypes...>&&) const;
+
+  tuple<A> t1a;
+  const tuple<B> t1b;
+  t1b = std::move(t1a);
+  VERIFY( std::get<0>(t1b).v );
+
+  tuple<A, A> t2a;
+  const tuple<B, B> t2b;
+  t2b = std::move(t2a);
+  VERIFY( std::get<0>(t2b).v );
+  VERIFY( std::get<1>(t2b).v );
+
+  tuple<A, A, A> t3a;
+  const tuple<B, B, B> t3b;
+  t3b = std::move(t3a);
+  VERIFY( std::get<0>(t3b).v );
+  VERIFY( std::get<1>(t3b).v );
+  VERIFY( std::get<2>(t3b).v );
+
+  // template<class U1, class U2>
+  //   constexpr const tuple& operator=(pair<U1, U2>&&) const;
+
+  pair<A, A> p2a;
+  const tuple<B, B> p2b;
+  p2b = std::move(p2a);
+
+  return true;
+}
+
+constexpr bool
+test07()
+{
+  struct B
+  {
+    mutable bool v;
+    constexpr const B& operator=(const B&) const { v = true; return *this; }
+  };
+
+  // constexpr const tuple& operator=(const tuple&) const;
+
+  const tuple<B> t1a;
+  const tuple<B> t1b;
+  t1b = t1a;
+  VERIFY( std::get<0>(t1b).v );
+
+  const tuple<B, B> t2a;
+  const tuple<B, B> t2b;
+  t2b = t2a;
+  VERIFY( std::get<0>(t2b).v );
+  VERIFY( std::get<1>(t2b).v );
+
+  const tuple<B, B, B> t3a;
+  const tuple<B, B, B> t3b;
+  t3b = t3a;
+  VERIFY( std::get<0>(t3b).v );
+  VERIFY( std::get<1>(t3b).v );
+  VERIFY( std::get<2>(t3b).v );
+
+  return true;
+}
+
+constexpr bool
+test08()
+{
+  struct B
+  {
+    mutable bool v;
+    constexpr const B& operator=(B&&) const { v = true; return *this; }
+  };
+
+  // constexpr const tuple& operator=(tuple&&) const;
+
+  tuple<B> t1a;
+  const tuple<B> t1b;
+  t1b = std::move(t1a);
+  VERIFY( std::get<0>(t1b).v );
+
+  tuple<B, B> t2a;
+  const tuple<B, B> t2b;
+  t2b = std::move(t2a);
+  VERIFY( std::get<0>(t2b).v );
+  VERIFY( std::get<1>(t2b).v );
+
+  tuple<B, B, B> t3a;
+  const tuple<B, B, B> t3b;
+  t3b = std::move(t3a);
+  VERIFY( std::get<0>(t3b).v );
+  VERIFY( std::get<1>(t3b).v );
+  VERIFY( std::get<2>(t3b).v );
+
+  return true;
+}
+
+struct S
+{
+  mutable int v = 0;
+  friend constexpr void swap(S&& x, S&& y) = delete;
+  friend constexpr void swap(const S& x, const S& y) { ++x.v; ++y.v; }
+};
+
+constexpr bool
+test09()
+{
+  const tuple<S> t1, u1;
+  std::swap(t1, u1);
+  VERIFY( std::get<0>(t1).v == 1 );
+  VERIFY( std::get<0>(u1).v == 1 );
+
+  const tuple<S, S> t2, u2;
+  std::swap(t2, u2);
+  VERIFY( std::get<0>(t2).v == 1 );
+  VERIFY( std::get<0>(u2).v == 1 );
+  VERIFY( std::get<1>(t2).v == 1 );
+  VERIFY( std::get<1>(u2).v == 1 );
+
+  const tuple<S, S, S> t3, u3;
+  std::swap(t3, u3);
+  VERIFY( std::get<0>(t3).v == 1 );
+  VERIFY( std::get<0>(u3).v == 1 );
+  VERIFY( std::get<1>(t3).v == 1 );
+  VERIFY( std::get<1>(u3).v == 1 );
+  VERIFY( std::get<2>(t3).v == 1 );
+  VERIFY( std::get<2>(u3).v == 1 );
+
+  static_assert(!std::is_swappable_v<const tuple<A>&>);
+
+  return true;
+}
+
+int
+main()
+{
+  static_assert(test01());
+  static_assert(alloc::test01());
+  static_assert(test02());
+  static_assert(alloc::test02());
+  static_assert(test03());
+  static_assert(alloc::test03());
+  static_assert(test04());
+  static_assert(alloc::test04());
+  // FIXME: G++ doesn't support reading mutable members during constexpr (PR c++/92505).
+  test05();
+  test06();
+  test07();
+  test08();
+  test09();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/vector/bool/element_access/1.cc b/libstdc++-v3/testsuite/23_containers/vector/bool/element_access/1.cc
new file mode 100644
index 00000000000..9016c026b33
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/vector/bool/element_access/1.cc
@@ -0,0 +1,26 @@
+// { dg-options "-std=gnu++23" }
+// { dg-do compile { target c++23 } }
+// { dg-xfail-if "not supported" { debug_mode } }
+
+#include <vector>
+#include <testsuite_hooks.h>
+
+constexpr bool
+test01()
+{
+  // P2321R2
+  // constexpr const reference& vector<bool>::operator=(bool x) const noexcept;
+
+  std::vector<bool> v(1);
+  const auto e = v[0];
+  e = true;
+  VERIFY( v[0] );
+
+  return true;
+}
+
+int
+main()
+{
+  static_assert(test01());
+}
-- 
2.37.2.382.g795ea8776b


  reply	other threads:[~2022-08-23  1:35 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-08-23  1:34 [PATCH 1/3] libstdc++: Separate construct/convertibility tests for std::tuple Patrick Palka
2022-08-23  1:34 ` Patrick Palka [this message]
2022-08-23 12:03   ` [PATCH 2/3] libstdc++: Implement std::pair/tuple/misc enhancements from P2321R2 Jonathan Wakely
2022-08-23 15:14     ` Patrick Palka
2022-08-23  1:35 ` [PATCH 3/3] libstdc++: Implement ranges::zip_view " Patrick Palka
2022-08-24 12:15   ` Jonathan Wakely
2022-08-26 20:05   ` Jonathan Wakely
2022-08-31 10:12     ` Jonathan Wakely
2022-08-23  9:15 ` [PATCH 1/3] libstdc++: Separate construct/convertibility tests for std::tuple Jonathan Wakely
2022-08-23 13:44   ` Patrick Palka
2022-08-23 14:53     ` Jonathan Wakely

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220823013500.1756466-2-ppalka@redhat.com \
    --to=ppalka@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=libstdc++@gcc.gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).