[libcxx-commits] [libcxx] 16719cd - [libc++] P2321R2 section [tuple.tuple]. Adding C++23 constructors, assignment operators and swaps to `tuple`
Hui Xie via libcxx-commits
libcxx-commits at lists.llvm.org
Thu Jun 23 23:15:33 PDT 2022
Author: Hui Xie
Date: 2022-06-24T07:12:53+01:00
New Revision: 16719cd011a4d1a856ddc24dd80703172bfcfffe
URL: https://github.com/llvm/llvm-project/commit/16719cd011a4d1a856ddc24dd80703172bfcfffe
DIFF: https://github.com/llvm/llvm-project/commit/16719cd011a4d1a856ddc24dd80703172bfcfffe.diff
LOG: [libc++] P2321R2 section [tuple.tuple]. Adding C++23 constructors, assignment operators and swaps to `tuple`
1. for constructors that takes cvref variation of tuple<UTypes...>, there
used to be two SFINAE helper _EnableCopyFromOtherTuple,
_EnableMoveFromOtherTuple. And the implementations of these two helpers
seem to slightly differ from the spec. But now, we need 4 variations.
Instead of adding another two, this change refactored it to a single one
_EnableCtrFromUTypesTuple, which directly maps to the spec without
changing the C++11 behaviour. However, we need the helper __copy_cvref_t
to get the type of std::get<i>(cvref tuple<Utypes...>) for different
cvref, so I made __copy_cvref_t to be available in C++11.
2. for constructors that takes variations of std::pair, there used to be
four helpers _EnableExplicitCopyFromPair, _EnableImplicitCopyFromPair,
_EnableImplicitMoveFromPair, _EnableExplicitMoveFromPair. Instead of
adding another four, this change refactored into two helper
_EnableCtrFromPair and _BothImplicitlyConvertible. This also removes the
need to use _nat
3. for const member assignment operator, since the requirement is very
simple, I haven't refactored the old code but instead directly adding
the new c++23 code.
4. for const swap, I pretty much copy pasted the non-const version to make
these overloads look consistent
5. while doing these change, I found two of the old constructors wasn't
marked constexpr for C++20 but they should. fixed them and added unit
tests
Reviewed By: #libc, ldionne
Differential Revision: https://reviews.llvm.org/D116621
Added:
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.assign/types.h
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/convert_types.h
libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/non_const_pair.pass.cpp
libcxx/test/std/utilities/tuple/tuple.tuple/tuple.special/non_member_swap_const.pass.cpp
libcxx/test/std/utilities/tuple/tuple.tuple/tuple.swap/member_swap_const.pass.cpp
Modified:
libcxx/include/tuple
libcxx/include/type_traits
libcxx/test/libcxx/utilities/meta/meta_base.pass.cpp
libcxx/test/std/ranges/range.adaptors/range.zip/range.concept.compile.pass.cpp
libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_copy.pass.cpp
libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_move.pass.cpp
libcxx/test/std/utilities/tuple/tuple.tuple/tuple.swap/member_swap.pass.cpp
Removed:
################################################################################
diff --git a/libcxx/include/tuple b/libcxx/include/tuple
index 251b685912b61..d0c159249ede9 100644
--- a/libcxx/include/tuple
+++ b/libcxx/include/tuple
@@ -25,14 +25,24 @@ public:
explicit(see-below) tuple(U&&...); // constexpr in C++14
tuple(const tuple&) = default;
tuple(tuple&&) = default;
+
+ template<class... UTypes>
+ constexpr explicit(see-below) tuple(tuple<UTypes...>&); // C++23
template <class... U>
explicit(see-below) tuple(const tuple<U...>&); // constexpr in C++14
template <class... U>
explicit(see-below) tuple(tuple<U...>&&); // constexpr in C++14
+ template<class... UTypes>
+ constexpr explicit(see-below) tuple(const tuple<UTypes...>&&); // C++23
+
+ template<class U1, class U2>
+ constexpr explicit(see-below) tuple(pair<U1, U2>&); // iff sizeof...(Types) == 2 // C++23
template <class U1, class U2>
explicit(see-below) tuple(const pair<U1, U2>&); // iff sizeof...(T) == 2 // constexpr in C++14
template <class U1, class U2>
explicit(see-below) tuple(pair<U1, U2>&&); // iff sizeof...(T) == 2 // constexpr in C++14
+ template<class U1, class U2>
+ constexpr explicit(see-below) tuple(const pair<U1, U2>&&); // iff sizeof...(Types) == 2 // C++23
// allocator-extended constructors
template <class Alloc>
@@ -45,25 +55,47 @@ public:
tuple(allocator_arg_t, const Alloc& a, const tuple&); // constexpr in C++20
template <class Alloc>
tuple(allocator_arg_t, const Alloc& a, tuple&&); // constexpr in C++20
+ template<class Alloc, class... UTypes>
+ constexpr explicit(see-below)
+ tuple(allocator_arg_t, const Alloc& a, tuple<UTypes...>&); // C++23
template <class Alloc, class... U>
explicit(see-below) tuple(allocator_arg_t, const Alloc& a, const tuple<U...>&); // constexpr in C++20
template <class Alloc, class... U>
explicit(see-below) tuple(allocator_arg_t, const Alloc& a, tuple<U...>&&); // constexpr in C++20
+ template<class Alloc, class... UTypes>
+ constexpr explicit(see-below)
+ tuple(allocator_arg_t, const Alloc& a, const tuple<UTypes...>&&); // C++23
+ template<class Alloc, class U1, class U2>
+ constexpr explicit(see-below)
+ tuple(allocator_arg_t, const Alloc& a, pair<U1, U2>&); // C++23
template <class Alloc, class U1, class U2>
explicit(see-below) tuple(allocator_arg_t, const Alloc& a, const pair<U1, U2>&); // constexpr in C++20
template <class Alloc, class U1, class U2>
explicit(see-below) tuple(allocator_arg_t, const Alloc& a, pair<U1, U2>&&); // constexpr in C++20
+ template<class Alloc, class U1, class U2>
+ constexpr explicit(see-below)
+ tuple(allocator_arg_t, const Alloc& a, const pair<U1, U2>&&); // C++23
tuple& operator=(const tuple&); // constexpr in C++20
+ constexpr const tuple& operator=(const tuple&) const; // C++23
tuple& operator=(tuple&&) noexcept(is_nothrow_move_assignable_v<T> && ...); // constexpr in C++20
+ constexpr const tuple& operator=(tuple&&) const; // C++23
template <class... U>
tuple& operator=(const tuple<U...>&); // constexpr in C++20
+ template<class... UTypes>
+ constexpr const tuple& operator=(const tuple<UTypes...>&) const; // C++23
template <class... U>
tuple& operator=(tuple<U...>&&); // constexpr in C++20
+ template<class... UTypes>
+ constexpr const tuple& operator=(tuple<UTypes...>&&) const; // C++23
template <class U1, class U2>
tuple& operator=(const pair<U1, U2>&); // iff sizeof...(T) == 2 // constexpr in C++20
+ template<class U1, class U2>
+ constexpr const tuple& operator=(const pair<U1, U2>&) const; // iff sizeof...(Types) == 2 // C++23
template <class U1, class U2>
tuple& operator=(pair<U1, U2>&&); // iff sizeof...(T) == 2 // constexpr in C++20
+ template<class U1, class U2>
+ constexpr const tuple& operator=(pair<U1, U2>&&) const; // iff sizeof...(Types) == 2 // C++23
template<class U, size_t N>
tuple& operator=(array<U, N> const&) // iff sizeof...(T) == N, EXTENSION
@@ -71,6 +103,7 @@ public:
tuple& operator=(array<U, N>&&) // iff sizeof...(T) == N, EXTENSION
void swap(tuple&) noexcept(AND(swap(declval<T&>(), declval<T&>())...)); // constexpr in C++20
+ constexpr void swap(const tuple&) const noexcept(see-below); // C++23
};
@@ -161,6 +194,9 @@ template <class... Types>
void
swap(tuple<Types...>& x, tuple<Types...>& y) noexcept(noexcept(x.swap(y)));
+template <class... Types>
+ constexpr void swap(const tuple<Types...>& x, const tuple<Types...>& y) noexcept(see-below); // C++23
+
} // std
*/
@@ -210,6 +246,13 @@ void swap(__tuple_leaf<_Ip, _Hp, _Ep>& __x, __tuple_leaf<_Ip, _Hp, _Ep>& __y)
swap(__x.get(), __y.get());
}
+template <size_t _Ip, class _Hp, bool _Ep>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_AFTER_CXX11
+void swap(const __tuple_leaf<_Ip, _Hp, _Ep>& __x, const __tuple_leaf<_Ip, _Hp, _Ep>& __y)
+ _NOEXCEPT_(__is_nothrow_swappable<const _Hp>::value) {
+ swap(__x.get(), __y.get());
+}
+
template <size_t _Ip, class _Hp, bool>
class __tuple_leaf
{
@@ -298,6 +341,12 @@ public:
return 0;
}
+ _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
+ int swap(const __tuple_leaf& __t) const _NOEXCEPT_(__is_nothrow_swappable<const __tuple_leaf>::value) {
+ _VSTD::swap(*this, __t);
+ return 0;
+ }
+
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11 _Hp& get() _NOEXCEPT {return __value_;}
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11 const _Hp& get() const _NOEXCEPT {return __value_;}
};
@@ -364,6 +413,12 @@ public:
return 0;
}
+ _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
+ int swap(const __tuple_leaf& __rhs) const _NOEXCEPT_(__is_nothrow_swappable<const __tuple_leaf>::value) {
+ _VSTD::swap(*this, __rhs);
+ return 0;
+ }
+
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11 _Hp& get() _NOEXCEPT {return static_cast<_Hp&>(*this);}
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11 const _Hp& get() const _NOEXCEPT {return static_cast<const _Hp&>(*this);}
};
@@ -454,6 +509,13 @@ struct _LIBCPP_DECLSPEC_EMPTY_BASES __tuple_impl<__tuple_indices<_Indx...>, _Tp.
{
_VSTD::__swallow(__tuple_leaf<_Indx, _Tp>::swap(static_cast<__tuple_leaf<_Indx, _Tp>&>(__t))...);
}
+
+ _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
+ void swap(const __tuple_impl& __t) const
+ _NOEXCEPT_(__all<__is_nothrow_swappable<const _Tp>::value...>::value)
+ {
+ _VSTD::__swallow(__tuple_leaf<_Indx, _Tp>::swap(static_cast<const __tuple_leaf<_Indx, _Tp>&>(__t))...);
+ }
};
template<class _Dest, class _Source, size_t ..._Np>
@@ -689,6 +751,7 @@ public:
template <class _Alloc, template<class...> class _And = _And, __enable_if_t<
_And<is_copy_constructible<_Tp>...>::value
, int> = 0>
+ _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
tuple(allocator_arg_t, const _Alloc& __alloc, const tuple& __t)
: __base_(allocator_arg_t(), __alloc, __t)
{ }
@@ -696,30 +759,39 @@ public:
template <class _Alloc, template<class...> class _And = _And, __enable_if_t<
_And<is_move_constructible<_Tp>...>::value
, int> = 0>
+ _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
tuple(allocator_arg_t, const _Alloc& __alloc, tuple&& __t)
: __base_(allocator_arg_t(), __alloc, _VSTD::move(__t))
{ }
// tuple(const tuple<U...>&) constructors (including allocator_arg_t variants)
- template <class ..._Up>
- struct _EnableCopyFromOtherTuple : _And<
- _Not<is_same<tuple<_Tp...>, tuple<_Up...> > >,
- _Lazy<_Or,
- _BoolConstant<sizeof...(_Tp) != 1>,
+
+ template <class _OtherTuple, class _DecayedOtherTuple = __uncvref_t<_OtherTuple>, class = void>
+ struct _EnableCtorFromUTypesTuple : false_type {};
+
+ template <class _OtherTuple, class... _Up>
+ struct _EnableCtorFromUTypesTuple<_OtherTuple, tuple<_Up...>,
+ // the length of the packs needs to checked first otherwise the 2 packs cannot be expanded simultaneously below
+ __enable_if_t<sizeof...(_Up) == sizeof...(_Tp)>> : _And<
+ // the two conditions below are not in spec. The purpose is to disable the UTypes Ctor when copy/move Ctor can work.
+ // Otherwise, is_constructible can trigger hard error in those cases https://godbolt.org/z/M94cGdKcE
+ _Not<is_same<_OtherTuple, const tuple&> >,
+ _Not<is_same<_OtherTuple, tuple&&> >,
+ is_constructible<_Tp, __copy_cvref_t<_OtherTuple, _Up> >...,
+ _Lazy<_Or, _BoolConstant<sizeof...(_Tp) != 1>,
// _Tp and _Up are 1-element packs - the pack expansions look
// weird to avoid tripping up the type traits in degenerate cases
_Lazy<_And,
- _Not<is_convertible<const tuple<_Up>&, _Tp> >...,
- _Not<is_constructible<_Tp, const tuple<_Up>&> >...
+ _Not<is_same<_Tp, _Up> >...,
+ _Not<is_convertible<_OtherTuple, _Tp> >...,
+ _Not<is_constructible<_Tp, _OtherTuple> >...
>
- >,
- is_constructible<_Tp, const _Up&>...
- > { };
+ >
+ > {};
template <class ..._Up, __enable_if_t<
_And<
- _BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>,
- _EnableCopyFromOtherTuple<_Up...>,
+ _EnableCtorFromUTypesTuple<const tuple<_Up...>&>,
is_convertible<const _Up&, _Tp>... // explicit check
>::value
, int> = 0>
@@ -731,8 +803,7 @@ public:
template <class ..._Up, __enable_if_t<
_And<
- _BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>,
- _EnableCopyFromOtherTuple<_Up...>,
+ _EnableCtorFromUTypesTuple<const tuple<_Up...>&>,
_Not<_Lazy<_And, is_convertible<const _Up&, _Tp>...> > // explicit check
>::value
, int> = 0>
@@ -744,8 +815,7 @@ public:
template <class ..._Up, class _Alloc, __enable_if_t<
_And<
- _BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>,
- _EnableCopyFromOtherTuple<_Up...>,
+ _EnableCtorFromUTypesTuple<const tuple<_Up...>&>,
is_convertible<const _Up&, _Tp>... // explicit check
>::value
, int> = 0>
@@ -756,8 +826,7 @@ public:
template <class ..._Up, class _Alloc, __enable_if_t<
_And<
- _BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>,
- _EnableCopyFromOtherTuple<_Up...>,
+ _EnableCtorFromUTypesTuple<const tuple<_Up...>&>,
_Not<_Lazy<_And, is_convertible<const _Up&, _Tp>...> > // explicit check
>::value
, int> = 0>
@@ -766,26 +835,27 @@ public:
: __base_(allocator_arg_t(), __a, __t)
{ }
+#if _LIBCPP_STD_VER > 20
+ // tuple(tuple<U...>&) constructors (including allocator_arg_t variants)
+
+ template <class... _Up, enable_if_t<
+ _EnableCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+ _LIBCPP_HIDE_FROM_ABI constexpr
+ explicit(!(is_convertible_v<_Up&, _Tp> && ...))
+ tuple(tuple<_Up...>& __t) : __base_(__t) {}
+
+ template <class _Alloc, class... _Up, enable_if_t<
+ _EnableCtorFromUTypesTuple<tuple<_Up...>&>::value>* = nullptr>
+ _LIBCPP_HIDE_FROM_ABI constexpr
+ explicit(!(is_convertible_v<_Up&, _Tp> && ...))
+ tuple(allocator_arg_t, const _Alloc& __alloc, tuple<_Up...>& __t) : __base_(allocator_arg_t(), __alloc, __t) {}
+#endif // _LIBCPP_STD_VER > 20
+
// tuple(tuple<U...>&&) constructors (including allocator_arg_t variants)
- template <class ..._Up>
- struct _EnableMoveFromOtherTuple : _And<
- _Not<is_same<tuple<_Tp...>, tuple<_Up...> > >,
- _Lazy<_Or,
- _BoolConstant<sizeof...(_Tp) != 1>,
- // _Tp and _Up are 1-element packs - the pack expansions look
- // weird to avoid tripping up the type traits in degenerate cases
- _Lazy<_And,
- _Not<is_convertible<tuple<_Up>, _Tp> >...,
- _Not<is_constructible<_Tp, tuple<_Up> > >...
- >
- >,
- is_constructible<_Tp, _Up>...
- > { };
template <class ..._Up, __enable_if_t<
_And<
- _BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>,
- _EnableMoveFromOtherTuple<_Up...>,
+ _EnableCtorFromUTypesTuple<tuple<_Up...>&&>,
is_convertible<_Up, _Tp>... // explicit check
>::value
, int> = 0>
@@ -797,8 +867,7 @@ public:
template <class ..._Up, __enable_if_t<
_And<
- _BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>,
- _EnableMoveFromOtherTuple<_Up...>,
+ _EnableCtorFromUTypesTuple<tuple<_Up...>&&>,
_Not<_Lazy<_And, is_convertible<_Up, _Tp>...> > // explicit check
>::value
, int> = 0>
@@ -810,8 +879,7 @@ public:
template <class _Alloc, class ..._Up, __enable_if_t<
_And<
- _BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>,
- _EnableMoveFromOtherTuple<_Up...>,
+ _EnableCtorFromUTypesTuple<tuple<_Up...>&&>,
is_convertible<_Up, _Tp>... // explicit check
>::value
, int> = 0>
@@ -822,8 +890,7 @@ public:
template <class _Alloc, class ..._Up, __enable_if_t<
_And<
- _BoolConstant<sizeof...(_Up) == sizeof...(_Tp)>,
- _EnableMoveFromOtherTuple<_Up...>,
+ _EnableCtorFromUTypesTuple<tuple<_Up...>&&>,
_Not<_Lazy<_And, is_convertible<_Up, _Tp>...> > // explicit check
>::value
, int> = 0>
@@ -832,57 +899,77 @@ public:
: __base_(allocator_arg_t(), __a, _VSTD::move(__t))
{ }
+#if _LIBCPP_STD_VER > 20
+ // tuple(const tuple<U...>&&) constructors (including allocator_arg_t variants)
+
+ template <class... _Up, enable_if_t<
+ _EnableCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+ _LIBCPP_HIDE_FROM_ABI constexpr
+ explicit(!(is_convertible_v<const _Up&&, _Tp> && ...))
+ tuple(const tuple<_Up...>&& __t) : __base_(std::move(__t)) {}
+
+ template <class _Alloc, class... _Up, enable_if_t<
+ _EnableCtorFromUTypesTuple<const tuple<_Up...>&&>::value>* = nullptr>
+ _LIBCPP_HIDE_FROM_ABI constexpr
+ explicit(!(is_convertible_v<const _Up&&, _Tp> && ...))
+ tuple(allocator_arg_t, const _Alloc& __alloc, const tuple<_Up...>&& __t)
+ : __base_(allocator_arg_t(), __alloc, std::move(__t)) {}
+#endif // _LIBCPP_STD_VER > 20
+
// tuple(const pair<U1, U2>&) constructors (including allocator_arg_t variants)
- template <class _Up1, class _Up2, class ..._DependentTp>
- struct _EnableImplicitCopyFromPair : _And<
- is_constructible<_FirstType<_DependentTp...>, const _Up1&>,
- is_constructible<_SecondType<_DependentTp...>, const _Up2&>,
- is_convertible<const _Up1&, _FirstType<_DependentTp...> >, // explicit check
- is_convertible<const _Up2&, _SecondType<_DependentTp...> >
- > { };
- template <class _Up1, class _Up2, class ..._DependentTp>
- struct _EnableExplicitCopyFromPair : _And<
- is_constructible<_FirstType<_DependentTp...>, const _Up1&>,
- is_constructible<_SecondType<_DependentTp...>, const _Up2&>,
- _Not<is_convertible<const _Up1&, _FirstType<_DependentTp...> > >, // explicit check
- _Not<is_convertible<const _Up2&, _SecondType<_DependentTp...> > >
- > { };
+ template <template <class...> class Pred, class _Pair, class _DecayedPair = __uncvref_t<_Pair>, class _Tuple = tuple>
+ struct _CtorPredicateFromPair : false_type{};
+
+ template <template <class...> class Pred, class _Pair, class _Up1, class _Up2, class _Tp1, class _Tp2>
+ struct _CtorPredicateFromPair<Pred, _Pair, pair<_Up1, _Up2>, tuple<_Tp1, _Tp2> > : _And<
+ Pred<_Tp1, __copy_cvref_t<_Pair, _Up1> >,
+ Pred<_Tp2, __copy_cvref_t<_Pair, _Up2> >
+ > {};
+
+ template <class _Pair>
+ struct _EnableCtorFromPair : _CtorPredicateFromPair<is_constructible, _Pair>{};
+
+ template <class _Pair>
+ struct _NothrowConstructibleFromPair : _CtorPredicateFromPair<is_nothrow_constructible, _Pair>{};
+
+ template <class _Pair, class _DecayedPair = __uncvref_t<_Pair>, class _Tuple = tuple>
+ struct _BothImplicitlyConvertible : false_type{};
+
+ template <class _Pair, class _Up1, class _Up2, class _Tp1, class _Tp2>
+ struct _BothImplicitlyConvertible<_Pair, pair<_Up1, _Up2>, tuple<_Tp1, _Tp2> > : _And<
+ is_convertible<__copy_cvref_t<_Pair, _Up1>, _Tp1>,
+ is_convertible<__copy_cvref_t<_Pair, _Up2>, _Tp2>
+ > {};
template <class _Up1, class _Up2, template<class...> class _And = _And, __enable_if_t<
_And<
- _BoolConstant<sizeof...(_Tp) == 2>,
- _EnableImplicitCopyFromPair<_Up1, _Up2, _Tp...>
+ _EnableCtorFromPair<const pair<_Up1, _Up2>&>,
+ _BothImplicitlyConvertible<const pair<_Up1, _Up2>&> // explicit check
>::value
, int> = 0>
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
tuple(const pair<_Up1, _Up2>& __p)
- _NOEXCEPT_((_And<
- is_nothrow_constructible<_FirstType<_Tp...>, const _Up1&>,
- is_nothrow_constructible<_SecondType<_Tp...>, const _Up2&>
- >::value))
+ _NOEXCEPT_((_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value))
: __base_(__p)
{ }
template <class _Up1, class _Up2, template<class...> class _And = _And, __enable_if_t<
_And<
- _BoolConstant<sizeof...(_Tp) == 2>,
- _EnableExplicitCopyFromPair<_Up1, _Up2, _Tp...>
+ _EnableCtorFromPair<const pair<_Up1, _Up2>&>,
+ _Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> > // explicit check
>::value
, int> = 0>
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
explicit tuple(const pair<_Up1, _Up2>& __p)
- _NOEXCEPT_((_And<
- is_nothrow_constructible<_FirstType<_Tp...>, const _Up1&>,
- is_nothrow_constructible<_SecondType<_Tp...>, const _Up2&>
- >::value))
+ _NOEXCEPT_((_NothrowConstructibleFromPair<const pair<_Up1, _Up2>&>::value))
: __base_(__p)
{ }
template <class _Alloc, class _Up1, class _Up2, template<class...> class _And = _And, __enable_if_t<
_And<
- _BoolConstant<sizeof...(_Tp) == 2>,
- _EnableImplicitCopyFromPair<_Up1, _Up2, _Tp...>
+ _EnableCtorFromPair<const pair<_Up1, _Up2>&>,
+ _BothImplicitlyConvertible<const pair<_Up1, _Up2>&> // explicit check
>::value
, int> = 0>
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
@@ -892,8 +979,8 @@ public:
template <class _Alloc, class _Up1, class _Up2, template<class...> class _And = _And, __enable_if_t<
_And<
- _BoolConstant<sizeof...(_Tp) == 2>,
- _EnableExplicitCopyFromPair<_Up1, _Up2, _Tp...>
+ _EnableCtorFromPair<const pair<_Up1, _Up2>&>,
+ _Not<_BothImplicitlyConvertible<const pair<_Up1, _Up2>&> > // explicit check
>::value
, int> = 0>
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
@@ -901,57 +988,52 @@ public:
: __base_(allocator_arg_t(), __a, __p)
{ }
- // tuple(pair<U1, U2>&&) constructors (including allocator_arg_t variants)
- template <class _Up1, class _Up2, class ..._DependentTp>
- struct _EnableImplicitMoveFromPair : _And<
- is_constructible<_FirstType<_DependentTp...>, _Up1>,
- is_constructible<_SecondType<_DependentTp...>, _Up2>,
- is_convertible<_Up1, _FirstType<_DependentTp...> >, // explicit check
- is_convertible<_Up2, _SecondType<_DependentTp...> >
- > { };
+#if _LIBCPP_STD_VER > 20
+ // tuple(pair<U1, U2>&) constructors (including allocator_arg_t variants)
+
+ template <class _U1, class _U2, enable_if_t<
+ _EnableCtorFromPair<pair<_U1, _U2>&>::value>* = nullptr>
+ _LIBCPP_HIDE_FROM_ABI constexpr
+ explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
+ tuple(pair<_U1, _U2>& __p) : __base_(__p) {}
+
+ template <class _Alloc, class _U1, class _U2, enable_if_t<
+ _EnableCtorFromPair<std::pair<_U1, _U2>&>::value>* = nullptr>
+ _LIBCPP_HIDE_FROM_ABI constexpr
+ explicit(!_BothImplicitlyConvertible<pair<_U1, _U2>&>::value)
+ tuple(allocator_arg_t, const _Alloc& __alloc, pair<_U1, _U2>& __p) : __base_(allocator_arg_t(), __alloc, __p) {}
+#endif
- template <class _Up1, class _Up2, class ..._DependentTp>
- struct _EnableExplicitMoveFromPair : _And<
- is_constructible<_FirstType<_DependentTp...>, _Up1>,
- is_constructible<_SecondType<_DependentTp...>, _Up2>,
- _Not<is_convertible<_Up1, _FirstType<_DependentTp...> > >, // explicit check
- _Not<is_convertible<_Up2, _SecondType<_DependentTp...> > >
- > { };
+ // tuple(pair<U1, U2>&&) constructors (including allocator_arg_t variants)
template <class _Up1, class _Up2, template<class...> class _And = _And, __enable_if_t<
_And<
- _BoolConstant<sizeof...(_Tp) == 2>,
- _EnableImplicitMoveFromPair<_Up1, _Up2, _Tp...>
+ _EnableCtorFromPair<pair<_Up1, _Up2>&&>,
+ _BothImplicitlyConvertible<pair<_Up1, _Up2>&&> // explicit check
>::value
, int> = 0>
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
tuple(pair<_Up1, _Up2>&& __p)
- _NOEXCEPT_((_And<
- is_nothrow_constructible<_FirstType<_Tp...>, _Up1>,
- is_nothrow_constructible<_SecondType<_Tp...>, _Up2>
- >::value))
+ _NOEXCEPT_((_NothrowConstructibleFromPair<pair<_Up1, _Up2>&&>::value))
: __base_(_VSTD::move(__p))
{ }
template <class _Up1, class _Up2, template<class...> class _And = _And, __enable_if_t<
_And<
- _BoolConstant<sizeof...(_Tp) == 2>,
- _EnableExplicitMoveFromPair<_Up1, _Up2, _Tp...>
+ _EnableCtorFromPair<pair<_Up1, _Up2>&&>,
+ _Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> > // explicit check
>::value
, int> = 0>
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX11
explicit tuple(pair<_Up1, _Up2>&& __p)
- _NOEXCEPT_((_And<
- is_nothrow_constructible<_FirstType<_Tp...>, _Up1>,
- is_nothrow_constructible<_SecondType<_Tp...>, _Up2>
- >::value))
+ _NOEXCEPT_((_NothrowConstructibleFromPair<pair<_Up1, _Up2>&&>::value))
: __base_(_VSTD::move(__p))
{ }
template <class _Alloc, class _Up1, class _Up2, template<class...> class _And = _And, __enable_if_t<
_And<
- _BoolConstant<sizeof...(_Tp) == 2>,
- _EnableImplicitMoveFromPair<_Up1, _Up2, _Tp...>
+ _EnableCtorFromPair<pair<_Up1, _Up2>&&>,
+ _BothImplicitlyConvertible<pair<_Up1, _Up2>&&> // explicit check
>::value
, int> = 0>
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
@@ -961,8 +1043,8 @@ public:
template <class _Alloc, class _Up1, class _Up2, template<class...> class _And = _And, __enable_if_t<
_And<
- _BoolConstant<sizeof...(_Tp) == 2>,
- _EnableExplicitMoveFromPair<_Up1, _Up2, _Tp...>
+ _EnableCtorFromPair<pair<_Up1, _Up2>&&>,
+ _Not<_BothImplicitlyConvertible<pair<_Up1, _Up2>&&> > // explicit check
>::value
, int> = 0>
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
@@ -970,6 +1052,23 @@ public:
: __base_(allocator_arg_t(), __a, _VSTD::move(__p))
{ }
+#if _LIBCPP_STD_VER > 20
+ // tuple(const pair<U1, U2>&&) constructors (including allocator_arg_t variants)
+
+ template <class _U1, class _U2, enable_if_t<
+ _EnableCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+ _LIBCPP_HIDE_FROM_ABI constexpr
+ explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
+ tuple(const pair<_U1, _U2>&& __p) : __base_(std::move(__p)) {}
+
+ template <class _Alloc, class _U1, class _U2, enable_if_t<
+ _EnableCtorFromPair<const pair<_U1, _U2>&&>::value>* = nullptr>
+ _LIBCPP_HIDE_FROM_ABI constexpr
+ explicit(!_BothImplicitlyConvertible<const pair<_U1, _U2>&&>::value)
+ tuple(allocator_arg_t, const _Alloc& __alloc, const pair<_U1, _U2>&& __p)
+ : __base_(allocator_arg_t(), __alloc, std::move(__p)) {}
+#endif // _LIBCPP_STD_VER > 20
+
// [tuple.assign]
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
tuple& operator=(_If<_And<is_copy_assignable<_Tp>...>::value, tuple, __nat> const& __tuple)
@@ -980,6 +1079,25 @@ public:
return *this;
}
+#if _LIBCPP_STD_VER > 20
+ _LIBCPP_HIDE_FROM_ABI constexpr
+ const tuple& operator=(tuple const& __tuple) const
+ requires (_And<is_copy_assignable<const _Tp>...>::value) {
+ std::__memberwise_copy_assign(*this, __tuple, typename __make_tuple_indices<sizeof...(_Tp)>::type());
+ return *this;
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr
+ const tuple& operator=(tuple&& __tuple) const
+ requires (_And<is_assignable<const _Tp&, _Tp>...>::value) {
+ std::__memberwise_forward_assign(*this,
+ std::move(__tuple),
+ __tuple_types<_Tp...>(),
+ typename __make_tuple_indices<sizeof...(_Tp)>::type());
+ return *this;
+ }
+#endif // _LIBCPP_STD_VER > 20
+
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
tuple& operator=(_If<_And<is_move_assignable<_Tp>...>::value, tuple, __nat>&& __tuple)
_NOEXCEPT_((_And<is_nothrow_move_assignable<_Tp>...>::value))
@@ -1021,38 +1139,89 @@ public:
return *this;
}
- template<class _Up1, class _Up2, class _Dep = true_type, __enable_if_t<
- _And<_Dep,
- _BoolConstant<sizeof...(_Tp) == 2>,
- is_assignable<_FirstType<_Tp..., _Dep>&, _Up1 const&>,
- is_assignable<_SecondType<_Tp..., _Dep>&, _Up2 const&>
- >::value
+
+#if _LIBCPP_STD_VER > 20
+ template <class... _UTypes, enable_if_t<
+ _And<_BoolConstant<sizeof...(_Tp) == sizeof...(_UTypes)>,
+ is_assignable<const _Tp&, const _UTypes&>...>::value>* = nullptr>
+ _LIBCPP_HIDE_FROM_ABI constexpr
+ const tuple& operator=(const tuple<_UTypes...>& __u) const {
+ std::__memberwise_copy_assign(*this,
+ __u,
+ typename __make_tuple_indices<sizeof...(_Tp)>::type());
+ return *this;
+ }
+
+ template <class... _UTypes, enable_if_t<
+ _And<_BoolConstant<sizeof...(_Tp) == sizeof...(_UTypes)>,
+ is_assignable<const _Tp&, _UTypes>...>::value>* = nullptr>
+ _LIBCPP_HIDE_FROM_ABI constexpr
+ const tuple& operator=(tuple<_UTypes...>&& __u) const {
+ std::__memberwise_forward_assign(*this,
+ __u,
+ __tuple_types<_UTypes...>(),
+ typename __make_tuple_indices<sizeof...(_Tp)>::type());
+ return *this;
+ }
+#endif // _LIBCPP_STD_VER > 20
+
+ template <template<class...> class Pred, bool _Const,
+ class _Pair, class _DecayedPair = __uncvref_t<_Pair>, class _Tuple = tuple>
+ struct _AssignPredicateFromPair : false_type {};
+
+ template <template<class...> class Pred, bool _Const,
+ class _Pair, class _Up1, class _Up2, class _Tp1, class _Tp2>
+ struct _AssignPredicateFromPair<Pred, _Const, _Pair, pair<_Up1, _Up2>, tuple<_Tp1, _Tp2> > :
+ _And<Pred<__maybe_const<_Const, _Tp1>&, __copy_cvref_t<_Pair, _Up1> >,
+ Pred<__maybe_const<_Const, _Tp2>&, __copy_cvref_t<_Pair, _Up2> >
+ > {};
+
+ template <bool _Const, class _Pair>
+ struct _EnableAssignFromPair : _AssignPredicateFromPair<is_assignable, _Const, _Pair> {};
+
+ template <bool _Const, class _Pair>
+ struct _NothrowAssignFromPair : _AssignPredicateFromPair<is_nothrow_assignable, _Const, _Pair> {};
+
+#if _LIBCPP_STD_VER > 20
+ template <class _U1, class _U2, enable_if_t<
+ _EnableAssignFromPair<true, const pair<_U1, _U2>&>::value>* = nullptr>
+ _LIBCPP_HIDE_FROM_ABI constexpr
+ const tuple& operator=(const pair<_U1, _U2>& __pair) const
+ noexcept(_NothrowAssignFromPair<true, const pair<_U1, _U2>&>::value) {
+ std::get<0>(*this) = __pair.first;
+ std::get<1>(*this) = __pair.second;
+ return *this;
+ }
+
+ template <class _U1, class _U2, enable_if_t<
+ _EnableAssignFromPair<true, pair<_U1, _U2>&&>::value>* = nullptr>
+ _LIBCPP_HIDE_FROM_ABI constexpr
+ const tuple& operator=(pair<_U1, _U2>&& __pair) const
+ noexcept(_NothrowAssignFromPair<true, pair<_U1, _U2>&&>::value) {
+ std::get<0>(*this) = std::move(__pair.first);
+ std::get<1>(*this) = std::move(__pair.second);
+ return *this;
+ }
+#endif // _LIBCPP_STD_VER > 20
+
+ template<class _Up1, class _Up2, __enable_if_t<
+ _EnableAssignFromPair<false, pair<_Up1, _Up2> const&>::value
,int> = 0>
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
tuple& operator=(pair<_Up1, _Up2> const& __pair)
- _NOEXCEPT_((_And<
- is_nothrow_assignable<_FirstType<_Tp...>&, _Up1 const&>,
- is_nothrow_assignable<_SecondType<_Tp...>&, _Up2 const&>
- >::value))
+ _NOEXCEPT_((_NothrowAssignFromPair<false, pair<_Up1, _Up2> const&>::value))
{
_VSTD::get<0>(*this) = __pair.first;
_VSTD::get<1>(*this) = __pair.second;
return *this;
}
- template<class _Up1, class _Up2, class _Dep = true_type, __enable_if_t<
- _And<_Dep,
- _BoolConstant<sizeof...(_Tp) == 2>,
- is_assignable<_FirstType<_Tp..., _Dep>&, _Up1>,
- is_assignable<_SecondType<_Tp..., _Dep>&, _Up2>
- >::value
+ template<class _Up1, class _Up2, __enable_if_t<
+ _EnableAssignFromPair<false, pair<_Up1, _Up2>&&>::value
,int> = 0>
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
tuple& operator=(pair<_Up1, _Up2>&& __pair)
- _NOEXCEPT_((_And<
- is_nothrow_assignable<_FirstType<_Tp...>&, _Up1>,
- is_nothrow_assignable<_SecondType<_Tp...>&, _Up2>
- >::value))
+ _NOEXCEPT_((_NothrowAssignFromPair<false, pair<_Up1, _Up2>&&>::value))
{
_VSTD::get<0>(*this) = _VSTD::forward<_Up1>(__pair.first);
_VSTD::get<1>(*this) = _VSTD::forward<_Up2>(__pair.second);
@@ -1096,6 +1265,13 @@ public:
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
void swap(tuple& __t) _NOEXCEPT_(__all<__is_nothrow_swappable<_Tp>::value...>::value)
{__base_.swap(__t.__base_);}
+
+#if _LIBCPP_STD_VER > 20
+ _LIBCPP_HIDE_FROM_ABI constexpr
+ void swap(const tuple& __t) const noexcept(__all<is_nothrow_swappable_v<const _Tp&>...>::value) {
+ __base_.swap(__t.__base_);
+ }
+#endif // _LIBCPP_STD_VER > 20
};
template <>
@@ -1118,6 +1294,9 @@ public:
tuple(allocator_arg_t, const _Alloc&, array<_Up, 0>) _NOEXCEPT {}
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
void swap(tuple&) _NOEXCEPT {}
+#if _LIBCPP_STD_VER > 20
+ _LIBCPP_HIDE_FROM_ABI constexpr void swap(const tuple&) const noexcept {}
+#endif
};
#if _LIBCPP_STD_VER > 20
@@ -1158,6 +1337,16 @@ swap(tuple<_Tp...>& __t, tuple<_Tp...>& __u)
_NOEXCEPT_(__all<__is_nothrow_swappable<_Tp>::value...>::value)
{__t.swap(__u);}
+#if _LIBCPP_STD_VER > 20
+template <class... _Tp>
+_LIBCPP_HIDE_FROM_ABI constexpr
+enable_if_t<__all<is_swappable_v<const _Tp>...>::value, void>
+swap(const tuple<_Tp...>& __lhs, const tuple<_Tp...>& __rhs)
+ noexcept(__all<is_nothrow_swappable_v<const _Tp>...>::value) {
+ __lhs.swap(__rhs);
+}
+#endif
+
// get
template <size_t _Ip, class ..._Tp>
diff --git a/libcxx/include/type_traits b/libcxx/include/type_traits
index 7a0c22539015c..7afbafd173976 100644
--- a/libcxx/include/type_traits
+++ b/libcxx/include/type_traits
@@ -536,10 +536,6 @@ struct _MetaBase<true> {
using _SelectImpl _LIBCPP_NODEBUG = _Tp;
template <template <class...> class _FirstFn, template <class...> class, class ..._Args>
using _SelectApplyImpl _LIBCPP_NODEBUG = _FirstFn<_Args...>;
- template <class _First, class...>
- using _FirstImpl _LIBCPP_NODEBUG = _First;
- template <class, class _Second, class...>
- using _SecondImpl _LIBCPP_NODEBUG = _Second;
template <class _Result, class _First, class ..._Rest>
using _OrImpl _LIBCPP_NODEBUG = typename _MetaBase<_First::value != true && sizeof...(_Rest) != 0>::template _OrImpl<_First, _Rest...>;
};
@@ -557,10 +553,6 @@ template <bool _Cond, class _IfRes, class _ElseRes>
using _If _LIBCPP_NODEBUG = typename _MetaBase<_Cond>::template _SelectImpl<_IfRes, _ElseRes>;
template <class ..._Rest>
using _Or _LIBCPP_NODEBUG = typename _MetaBase< sizeof...(_Rest) != 0 >::template _OrImpl<false_type, _Rest...>;
-template <class ..._Args>
-using _FirstType _LIBCPP_NODEBUG = typename _MetaBase<(sizeof...(_Args) >= 1)>::template _FirstImpl<_Args...>;
-template <class ..._Args>
-using _SecondType _LIBCPP_NODEBUG = typename _MetaBase<(sizeof...(_Args) >= 2)>::template _SecondImpl<_Args...>;
template <class ...> using __expand_to_true = true_type;
template <class ..._Pred>
@@ -1154,7 +1146,6 @@ struct _LIBCPP_TEMPLATE_VIS
template <class ..._Tp> using common_type_t = typename common_type<_Tp...>::type;
#endif
-#if _LIBCPP_STD_VER > 11
// Let COPYCV(FROM, TO) be an alias for type TO with the addition of FROM's
// top-level cv-qualifiers.
template <class _From, class _To>
@@ -1166,19 +1157,19 @@ struct __copy_cv
template <class _From, class _To>
struct __copy_cv<const _From, _To>
{
- using type = add_const_t<_To>;
+ using type = typename add_const<_To>::type;
};
template <class _From, class _To>
struct __copy_cv<volatile _From, _To>
{
- using type = add_volatile_t<_To>;
+ using type = typename add_volatile<_To>::type;
};
template <class _From, class _To>
struct __copy_cv<const volatile _From, _To>
{
- using type = add_cv_t<_To>;
+ using type = typename add_cv<_To>::type;
};
template <class _From, class _To>
@@ -1193,19 +1184,18 @@ struct __copy_cvref
template <class _From, class _To>
struct __copy_cvref<_From&, _To>
{
- using type = add_lvalue_reference_t<__copy_cv_t<_From, _To>>;
+ using type = typename add_lvalue_reference<__copy_cv_t<_From, _To> >::type;
};
template <class _From, class _To>
struct __copy_cvref<_From&&, _To>
{
- using type = add_rvalue_reference_t<__copy_cv_t<_From, _To>>;
+ using type = typename add_rvalue_reference<__copy_cv_t<_From, _To> >::type;
};
template <class _From, class _To>
using __copy_cvref_t = typename __copy_cvref<_From, _To>::type;
-#endif // _LIBCPP_STD_VER > 11
// common_reference
#if _LIBCPP_STD_VER > 17
@@ -1697,10 +1687,8 @@ using _IsCharLikeType = _And<is_standard_layout<_CharT>, is_trivial<_CharT> >;
template<class _Tp>
using __make_const_lvalue_ref = const typename remove_reference<_Tp>::type&;
-#if _LIBCPP_STD_VER > 17
template<bool _Const, class _Tp>
-using __maybe_const = conditional_t<_Const, const _Tp, _Tp>;
-#endif // _LIBCPP_STD_VER > 17
+using __maybe_const = typename conditional<_Const, const _Tp, _Tp>::type;
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/test/libcxx/utilities/meta/meta_base.pass.cpp b/libcxx/test/libcxx/utilities/meta/meta_base.pass.cpp
index f04ae608614d9..7357c222e17bc 100644
--- a/libcxx/test/libcxx/utilities/meta/meta_base.pass.cpp
+++ b/libcxx/test/libcxx/utilities/meta/meta_base.pass.cpp
@@ -79,13 +79,6 @@ void test_is_valid_trait() {
static_assert(!std::_IsValidExpansion<FuncCallable, MemberTest, void*>::value, "");
}
-void test_first_and_second_type() {
- ASSERT_SAME_TYPE(std::_FirstType<int, long, void*>, int);
- ASSERT_SAME_TYPE(std::_FirstType<char>, char);
- ASSERT_SAME_TYPE(std::_SecondType<char, long>, long);
- ASSERT_SAME_TYPE(std::_SecondType<long long, int, void*>, int);
-}
-
int main(int, char**) {
return 0;
}
diff --git a/libcxx/test/std/ranges/range.adaptors/range.zip/range.concept.compile.pass.cpp b/libcxx/test/std/ranges/range.adaptors/range.zip/range.concept.compile.pass.cpp
index d1dcb75199f46..14b787349799b 100644
--- a/libcxx/test/std/ranges/range.adaptors/range.zip/range.concept.compile.pass.cpp
+++ b/libcxx/test/std/ranges/range.adaptors/range.zip/range.concept.compile.pass.cpp
@@ -165,15 +165,11 @@ void testConceptTuple() {
int buffer2[3] = {1, 2, 3};
int buffer3[4] = {1, 2, 3, 4};
- // TODO: uncomment all the static_asserts once [tuple.tuple] section in P2321R2 is implemented
- // This is because convertible_to<tuple<int&,int&,int&>&, tuple<int,int,int>> is false without
- // the above implementation, thus the zip iterator does not model indirectly_readable
-
{
std::ranges::zip_view v{ContiguousCommonView{buffer1}, ContiguousCommonView{buffer2},
ContiguousCommonView{buffer3}};
using View = decltype(v);
- // static_assert(std::ranges::random_access_range<View>);
+ static_assert(std::ranges::random_access_range<View>);
static_assert(!std::ranges::contiguous_range<View>);
static_assert(std::ranges::common_range<View>);
static_assert(std::ranges::sized_range<View>);
@@ -183,7 +179,7 @@ void testConceptTuple() {
std::ranges::zip_view v{ContiguousNonCommonView{buffer1}, ContiguousNonCommonView{buffer2},
ContiguousNonCommonView{buffer3}};
using View = decltype(v);
- // static_assert(std::ranges::random_access_range<View>);
+ static_assert(std::ranges::random_access_range<View>);
static_assert(!std::ranges::contiguous_range<View>);
static_assert(!std::ranges::common_range<View>);
static_assert(!std::ranges::sized_range<View>);
@@ -193,7 +189,7 @@ void testConceptTuple() {
std::ranges::zip_view v{ContiguousNonCommonSized{buffer1}, ContiguousNonCommonSized{buffer2},
ContiguousNonCommonSized{buffer3}};
using View = decltype(v);
- // static_assert(std::ranges::random_access_range<View>);
+ static_assert(std::ranges::random_access_range<View>);
static_assert(!std::ranges::contiguous_range<View>);
static_assert(std::ranges::common_range<View>);
static_assert(std::ranges::sized_range<View>);
@@ -203,7 +199,7 @@ void testConceptTuple() {
std::ranges::zip_view v{SizedRandomAccessView{buffer1}, ContiguousCommonView{buffer2},
ContiguousCommonView{buffer3}};
using View = decltype(v);
- // static_assert(std::ranges::random_access_range<View>);
+ static_assert(std::ranges::random_access_range<View>);
static_assert(!std::ranges::contiguous_range<View>);
static_assert(std::ranges::common_range<View>);
static_assert(std::ranges::sized_range<View>);
@@ -213,7 +209,7 @@ void testConceptTuple() {
std::ranges::zip_view v{SizedRandomAccessView{buffer1}, SizedRandomAccessView{buffer2},
SizedRandomAccessView{buffer3}};
using View = decltype(v);
- // static_assert(std::ranges::random_access_range<View>);
+ static_assert(std::ranges::random_access_range<View>);
static_assert(!std::ranges::contiguous_range<View>);
static_assert(std::ranges::common_range<View>);
static_assert(std::ranges::sized_range<View>);
@@ -223,7 +219,7 @@ void testConceptTuple() {
std::ranges::zip_view v{NonSizedRandomAccessView{buffer1}, NonSizedRandomAccessView{buffer2},
NonSizedRandomAccessView{buffer3}};
using View = decltype(v);
- // static_assert(std::ranges::random_access_range<View>);
+ static_assert(std::ranges::random_access_range<View>);
static_assert(!std::ranges::contiguous_range<View>);
static_assert(!std::ranges::common_range<View>);
static_assert(!std::ranges::sized_range<View>);
@@ -232,7 +228,7 @@ void testConceptTuple() {
{
std::ranges::zip_view v{BidiCommonView{buffer1}, SizedRandomAccessView{buffer2}, SizedRandomAccessView{buffer3}};
using View = decltype(v);
- // static_assert(std::ranges::bidirectional_range<View>);
+ static_assert(std::ranges::bidirectional_range<View>);
static_assert(!std::ranges::random_access_range<View>);
static_assert(!std::ranges::common_range<View>);
static_assert(!std::ranges::sized_range<View>);
@@ -241,7 +237,7 @@ void testConceptTuple() {
{
std::ranges::zip_view v{BidiCommonView{buffer1}, BidiCommonView{buffer2}, BidiCommonView{buffer3}};
using View = decltype(v);
- // static_assert(std::ranges::bidirectional_range<View>);
+ static_assert(std::ranges::bidirectional_range<View>);
static_assert(!std::ranges::random_access_range<View>);
static_assert(!std::ranges::common_range<View>);
static_assert(!std::ranges::sized_range<View>);
@@ -250,7 +246,7 @@ void testConceptTuple() {
{
std::ranges::zip_view v{BidiCommonView{buffer1}, ForwardSizedView{buffer2}, ForwardSizedView{buffer3}};
using View = decltype(v);
- // static_assert(std::ranges::forward_range<View>);
+ static_assert(std::ranges::forward_range<View>);
static_assert(!std::ranges::bidirectional_range<View>);
static_assert(std::ranges::common_range<View>);
static_assert(!std::ranges::sized_range<View>);
@@ -259,7 +255,7 @@ void testConceptTuple() {
{
std::ranges::zip_view v{BidiNonCommonView{buffer1}, ForwardSizedView{buffer2}, ForwardSizedView{buffer3}};
using View = decltype(v);
- // static_assert(std::ranges::forward_range<View>);
+ static_assert(std::ranges::forward_range<View>);
static_assert(!std::ranges::bidirectional_range<View>);
static_assert(!std::ranges::common_range<View>);
static_assert(!std::ranges::sized_range<View>);
@@ -268,7 +264,7 @@ void testConceptTuple() {
{
std::ranges::zip_view v{ForwardSizedView{buffer1}, ForwardSizedView{buffer2}, ForwardSizedView{buffer3}};
using View = decltype(v);
- // static_assert(std::ranges::forward_range<View>);
+ static_assert(std::ranges::forward_range<View>);
static_assert(!std::ranges::bidirectional_range<View>);
static_assert(std::ranges::common_range<View>);
static_assert(std::ranges::sized_range<View>);
@@ -277,7 +273,7 @@ void testConceptTuple() {
{
std::ranges::zip_view v{ForwardSizedNonCommon{buffer1}, ForwardSizedView{buffer2}, ForwardSizedView{buffer3}};
using View = decltype(v);
- // static_assert(std::ranges::forward_range<View>);
+ static_assert(std::ranges::forward_range<View>);
static_assert(!std::ranges::bidirectional_range<View>);
static_assert(!std::ranges::common_range<View>);
static_assert(std::ranges::sized_range<View>);
@@ -286,7 +282,7 @@ void testConceptTuple() {
{
std::ranges::zip_view v{InputCommonView{buffer1}, ForwardSizedView{buffer2}, ForwardSizedView{buffer3}};
using View = decltype(v);
- // static_assert(std::ranges::input_range<View>);
+ static_assert(std::ranges::input_range<View>);
static_assert(!std::ranges::forward_range<View>);
static_assert(std::ranges::common_range<View>);
static_assert(!std::ranges::sized_range<View>);
@@ -295,7 +291,7 @@ void testConceptTuple() {
{
std::ranges::zip_view v{InputCommonView{buffer1}, InputCommonView{buffer2}, InputCommonView{buffer3}};
using View = decltype(v);
- // static_assert(std::ranges::input_range<View>);
+ static_assert(std::ranges::input_range<View>);
static_assert(!std::ranges::forward_range<View>);
static_assert(std::ranges::common_range<View>);
static_assert(!std::ranges::sized_range<View>);
@@ -304,7 +300,7 @@ void testConceptTuple() {
{
std::ranges::zip_view v{InputNonCommonView{buffer1}, InputCommonView{buffer2}, InputCommonView{buffer3}};
using View = decltype(v);
- // static_assert(std::ranges::input_range<View>);
+ static_assert(std::ranges::input_range<View>);
static_assert(!std::ranges::forward_range<View>);
static_assert(!std::ranges::common_range<View>);
static_assert(!std::ranges::sized_range<View>);
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
new file mode 100644
index 0000000000000..d32bfe45a7784
--- /dev/null
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_convert_copy.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
+//
+//===----------------------------------------------------------------------===//
+
+// <tuple>
+
+// template <class... UTypes>
+// constexpr const tuple& operator=(const tuple<UTypes...>& u) const;
+//
+// Constraints:
+// - sizeof...(Types) equals sizeof...(UTypes) and
+// - (is_assignable_v<const Types&, const UTypes&> && ...) is true.
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <cassert>
+#include <tuple>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "types.h"
+
+// test constraints
+
+// sizeof...(Types) equals sizeof...(UTypes)
+static_assert(std::is_assignable_v<const std::tuple<int&>&, const std::tuple<long&>&>);
+static_assert(!std::is_assignable_v<const std::tuple<int&, int&>&, const std::tuple<long&>&>);
+static_assert(!std::is_assignable_v<const std::tuple<int&>&, const std::tuple<long&, long&>&>);
+
+// (is_assignable_v<const Types&, const UTypes&> && ...) is true
+static_assert(std::is_assignable_v<const std::tuple<AssignableFrom<ConstCopyAssign>>&,
+ const std::tuple<ConstCopyAssign>&>);
+
+static_assert(std::is_assignable_v<const std::tuple<AssignableFrom<ConstCopyAssign>, ConstCopyAssign>&,
+ const std::tuple<ConstCopyAssign, ConstCopyAssign>&>);
+
+static_assert(!std::is_assignable_v<const std::tuple<AssignableFrom<ConstCopyAssign>, CopyAssign>&,
+ const std::tuple<ConstCopyAssign, CopyAssign>&>);
+
+constexpr bool test() {
+ // reference types
+ {
+ int i1 = 1;
+ int i2 = 2;
+ long j1 = 3;
+ long j2 = 4;
+ const std::tuple<int&, int&> t1{i1, i2};
+ const std::tuple<long&, long&> t2{j1, j2};
+ t2 = t1;
+ assert(std::get<0>(t2) == 1);
+ assert(std::get<1>(t2) == 2);
+ }
+
+ // user defined const copy assignment
+ {
+ const std::tuple<ConstCopyAssign> t1{1};
+ const std::tuple<AssignableFrom<ConstCopyAssign>> t2{2};
+ t2 = t1;
+ assert(std::get<0>(t2).v.val == 1);
+ }
+
+ // make sure the right assignment operator of the type in the tuple is used
+ {
+ std::tuple<TracedAssignment> t1{};
+ const std::tuple<AssignableFrom<TracedAssignment>> t2{};
+ t2 = t1;
+ assert(std::get<0>(t2).v.constCopyAssign == 1);
+ }
+
+ return true;
+}
+
+int main() {
+ test();
+
+// gcc cannot have mutable member in constant expression
+#if !defined(TEST_COMPILER_GCC)
+ static_assert(test());
+#endif
+ return 0;
+}
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
new file mode 100644
index 0000000000000..41dfe5424cbe5
--- /dev/null
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_convert_move.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
+//
+//===----------------------------------------------------------------------===//
+
+// <tuple>
+
+// template <class... UTypes>
+// constexpr const tuple& operator=(tuple<UTypes...>&& u) const;
+//
+// Constraints:
+// - sizeof...(Types) equals sizeof...(UTypes) and
+// - (is_assignable_v<const Types&, UTypes> && ...) is true.
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <cassert>
+#include <tuple>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "types.h"
+
+// test constraints
+
+// sizeof...(Types) equals sizeof...(UTypes)
+static_assert(std::is_assignable_v<const std::tuple<int&>&, std::tuple<long&>&&>);
+static_assert(!std::is_assignable_v<const std::tuple<int&, int&>&, std::tuple<long&>&&>);
+static_assert(!std::is_assignable_v<const std::tuple<int&>&, std::tuple<long&, long&>&&>);
+
+// (is_assignable_v<const Types&, UTypes&&> && ...) is true
+static_assert(std::is_assignable_v<const std::tuple<AssignableFrom<ConstMoveAssign>>&, std::tuple<ConstMoveAssign>&&>);
+
+static_assert(std::is_assignable_v<const std::tuple<AssignableFrom<ConstMoveAssign>, ConstMoveAssign>&,
+ std::tuple<ConstMoveAssign, ConstMoveAssign>&&>);
+
+static_assert(!std::is_assignable_v<const std::tuple<AssignableFrom<ConstMoveAssign>, AssignableFrom<MoveAssign>>&,
+ std::tuple<ConstMoveAssign, MoveAssign>&&>);
+
+constexpr bool test() {
+ // reference types
+ {
+ int i1 = 1;
+ int i2 = 2;
+ long j1 = 3;
+ long j2 = 4;
+ std::tuple<int&, int&> t1{i1, i2};
+ const std::tuple<long&, long&> t2{j1, j2};
+ t2 = std::move(t1);
+ assert(std::get<0>(t2) == 1);
+ assert(std::get<1>(t2) == 2);
+ }
+
+ // user defined const copy assignment
+ {
+ std::tuple<ConstMoveAssign> t1{1};
+ const std::tuple<AssignableFrom<ConstMoveAssign>> t2{2};
+ t2 = std::move(t1);
+ assert(std::get<0>(t2).v.val == 1);
+ }
+
+ // make sure the right assignment operator of the type in the tuple is used
+ {
+ std::tuple<TracedAssignment> t1{};
+ const std::tuple<AssignableFrom<TracedAssignment>> t2{};
+ t2 = std::move(t1);
+ assert(std::get<0>(t2).v.constMoveAssign == 1);
+ }
+
+ return true;
+}
+
+int main() {
+ test();
+
+// gcc cannot have mutable member in constant expression
+#if !defined(TEST_COMPILER_GCC)
+ static_assert(test());
+#endif
+ return 0;
+}
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
new file mode 100644
index 0000000000000..38449553bd591
--- /dev/null
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_copy.pass.cpp
@@ -0,0 +1,77 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <tuple>
+
+// constexpr const tuple& operator=(const tuple&) const;
+//
+// Constraints: (is_copy_assignable_v<const Types> && ...) is true.
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// test constraints
+
+#include <cassert>
+#include <tuple>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "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&>&>);
+static_assert(std::is_assignable_v<const std::tuple<int&, int&>&, const std::tuple<int&, int&>&>);
+static_assert(!std::is_assignable_v<const std::tuple<int&, int>&, const std::tuple<int&, int>&>);
+static_assert(std::is_assignable_v<const std::tuple<ConstCopyAssign>&, const std::tuple<ConstCopyAssign>&>);
+static_assert(!std::is_assignable_v<const std::tuple<CopyAssign>&, const std::tuple<CopyAssign>&>);
+static_assert(!std::is_assignable_v<const std::tuple<ConstMoveAssign>&, const std::tuple<ConstMoveAssign>&>);
+static_assert(!std::is_assignable_v<const std::tuple<MoveAssign>&, const std::tuple<MoveAssign>&>);
+
+constexpr bool test() {
+ // reference types
+ {
+ int i1 = 1;
+ int i2 = 2;
+ double d1 = 3.0;
+ double d2 = 5.0;
+ const std::tuple<int&, double&> t1{i1, d1};
+ const std::tuple<int&, double&> t2{i2, d2};
+ t2 = t1;
+ assert(std::get<0>(t2) == 1);
+ assert(std::get<1>(t2) == 3.0);
+ }
+
+ // user defined const copy assignment
+ {
+ const std::tuple<ConstCopyAssign> t1{1};
+ const std::tuple<ConstCopyAssign> t2{2};
+ t2 = t1;
+ assert(std::get<0>(t2).val == 1);
+ }
+
+ // make sure the right assignment operator of the type in the tuple is used
+ {
+ std::tuple<TracedAssignment, const TracedAssignment> t1{};
+ const std::tuple<TracedAssignment, const TracedAssignment> t2{};
+ t2 = t1;
+ assert(std::get<0>(t2).constCopyAssign == 1);
+ assert(std::get<1>(t2).constCopyAssign == 1);
+ }
+
+ return true;
+}
+
+int main() {
+ test();
+
+// gcc cannot have mutable member in constant expression
+#if !defined(TEST_COMPILER_GCC)
+ static_assert(test());
+#endif
+ return 0;
+}
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
new file mode 100644
index 0000000000000..aeb71a56c2c43
--- /dev/null
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_move.pass.cpp
@@ -0,0 +1,79 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <tuple>
+
+// constexpr const tuple& operator=(tuple&&) const;
+//
+// Constraints: (is_assignable_v<const Types&, Types> && ...) is true.
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// test constraints
+
+#include <cassert>
+#include <tuple>
+#include <type_traits>
+
+#include "test_macros.h"
+#include "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&>&&>);
+static_assert(std::is_assignable_v<const std::tuple<int&, int&>&, std::tuple<int&, int&>&&>);
+static_assert(!std::is_assignable_v<const std::tuple<int&, int>&, std::tuple<int&, int>&&>);
+
+// this is fallback to tuple's const copy assignment
+static_assert(std::is_assignable_v<const std::tuple<ConstCopyAssign>&, std::tuple<ConstCopyAssign>&&>);
+
+static_assert(!std::is_assignable_v<const std::tuple<CopyAssign>&, std::tuple<CopyAssign>&&>);
+static_assert(std::is_assignable_v<const std::tuple<ConstMoveAssign>&, std::tuple<ConstMoveAssign>&&>);
+static_assert(!std::is_assignable_v<const std::tuple<MoveAssign>&, std::tuple<MoveAssign>&&>);
+
+constexpr bool test() {
+ // reference types
+ {
+ int i1 = 1;
+ int i2 = 2;
+ double d1 = 3.0;
+ double d2 = 5.0;
+ std::tuple<int&, double&> t1{i1, d1};
+ const std::tuple<int&, double&> t2{i2, d2};
+ t2 = std::move(t1);
+ assert(std::get<0>(t2) == 1);
+ assert(std::get<1>(t2) == 3.0);
+ }
+
+ // user defined const move assignment
+ {
+ std::tuple<ConstMoveAssign> t1{1};
+ const std::tuple<ConstMoveAssign> t2{2};
+ t2 = std::move(t1);
+ assert(std::get<0>(t2).val == 1);
+ }
+
+ // make sure the right assignment operator of the type in the tuple is used
+ {
+ std::tuple<TracedAssignment, const TracedAssignment> t1{};
+ const std::tuple<TracedAssignment, const TracedAssignment> t2{};
+ t2 = std::move(t1);
+ assert(std::get<0>(t2).constMoveAssign == 1);
+ assert(std::get<1>(t2).constCopyAssign == 1);
+ }
+
+ return true;
+}
+
+int main() {
+ test();
+// gcc cannot have mutable member in constant expression
+#if !defined(TEST_COMPILER_GCC)
+ static_assert(test());
+#endif
+ return 0;
+}
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
new file mode 100644
index 0000000000000..67d16df26fbbc
--- /dev/null
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair_copy.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
+//
+//===----------------------------------------------------------------------===//
+
+// <tuple>
+
+// template<class U1, class U2>
+// constexpr const tuple& operator=(const pair<U1, U2>& u) const;
+//
+// - sizeof...(Types) is 2,
+// - is_assignable_v<const T1&, const U1&> is true, and
+// - is_assignable_v<const T2&, const U2&> is true
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <cassert>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#include "test_macros.h"
+#include "types.h"
+
+// test constraints
+
+// sizeof...(Types) != 2,
+static_assert(std::is_assignable_v<const std::tuple<int&, int&>&, const std::pair<int&, int&>&>);
+static_assert(!std::is_assignable_v<const std::tuple<int&>&, const std::pair<int&, int&>&>);
+static_assert(!std::is_assignable_v<const std::tuple<int&, int&, int&>&, const std::pair<int&, int&>&>);
+
+static_assert(std::is_assignable_v<const std::tuple<AssignableFrom<ConstCopyAssign>, ConstCopyAssign>&,
+ const std::pair<ConstCopyAssign, ConstCopyAssign>&>);
+
+// is_assignable_v<const T1&, const U1&> is false
+static_assert(!std::is_assignable_v<const std::tuple<AssignableFrom<CopyAssign>, ConstCopyAssign>&,
+ const std::pair<CopyAssign, ConstCopyAssign>&>);
+
+// is_assignable_v<const T2&, const U2&> is false
+static_assert(!std::is_assignable_v<const std::tuple<AssignableFrom<ConstCopyAssign>, AssignableFrom<CopyAssign>>&,
+ const std::tuple<ConstCopyAssign, CopyAssign>&>);
+
+constexpr bool test() {
+ // reference types
+ {
+ int i1 = 1;
+ int i2 = 2;
+ long j1 = 3;
+ long j2 = 4;
+ const std::pair<int&, int&> t1{i1, i2};
+ const std::tuple<long&, long&> t2{j1, j2};
+ t2 = t1;
+ assert(std::get<0>(t2) == 1);
+ assert(std::get<1>(t2) == 2);
+ }
+
+ // user defined const copy assignment
+ {
+ const std::pair<ConstCopyAssign, ConstCopyAssign> t1{1, 2};
+ const std::tuple<AssignableFrom<ConstCopyAssign>, ConstCopyAssign> t2{3, 4};
+ t2 = t1;
+ assert(std::get<0>(t2).v.val == 1);
+ assert(std::get<1>(t2).val == 2);
+ }
+
+ // make sure the right assignment operator of the type in the tuple is used
+ {
+ std::pair<TracedAssignment, TracedAssignment> t1{};
+ const std::tuple<AssignableFrom<TracedAssignment>, AssignableFrom<TracedAssignment>> t2{};
+ t2 = t1;
+ assert(std::get<0>(t2).v.constCopyAssign == 1);
+ assert(std::get<1>(t2).v.constCopyAssign == 1);
+ }
+
+ return true;
+}
+
+int main() {
+ test();
+
+// gcc cannot have mutable member in constant expression
+#if !defined(TEST_COMPILER_GCC)
+ static_assert(test());
+#endif
+ return 0;
+}
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
new file mode 100644
index 0000000000000..44dcb7022973b
--- /dev/null
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair_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
+//
+//===----------------------------------------------------------------------===//
+
+// <tuple>
+
+// template<class U1, class U2>
+// constexpr const tuple& operator=(pair<U1, U2>&& u) const;
+//
+// - sizeof...(Types) is 2,
+// - is_assignable_v<const T1&, U1> is true, and
+// - is_assignable_v<const T2&, U2> is true
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <cassert>
+#include <tuple>
+#include <type_traits>
+#include <utility>
+
+#include "test_macros.h"
+#include "types.h"
+
+// test constraints
+
+// sizeof...(Types) != 2,
+static_assert(std::is_assignable_v<const std::tuple<int&, int&>&, std::pair<int&, int&>&&>);
+static_assert(!std::is_assignable_v<const std::tuple<int&>&, std::pair<int&, int&>&&>);
+static_assert(!std::is_assignable_v<const std::tuple<int&, int&, int&>&, std::pair<int&, int&>&&>);
+
+static_assert(std::is_assignable_v<const std::tuple<AssignableFrom<ConstMoveAssign>, ConstMoveAssign>&,
+ std::pair<ConstMoveAssign, ConstMoveAssign>&&>);
+
+// is_assignable_v<const T1&, U1> is false
+static_assert(!std::is_assignable_v<const std::tuple<AssignableFrom<MoveAssign>, ConstMoveAssign>&,
+ std::pair<MoveAssign, ConstMoveAssign>&&>);
+
+// is_assignable_v<const T2&, U2> is false
+static_assert(!std::is_assignable_v<const std::tuple<AssignableFrom<ConstMoveAssign>, AssignableFrom<MoveAssign>>&,
+ std::tuple<ConstMoveAssign, MoveAssign>&&>);
+
+constexpr bool test() {
+ // reference types
+ {
+ int i1 = 1;
+ int i2 = 2;
+ long j1 = 3;
+ long j2 = 4;
+ std::pair<int&, int&> t1{i1, i2};
+ const std::tuple<long&, long&> t2{j1, j2};
+ t2 = std::move(t1);
+ assert(std::get<0>(t2) == 1);
+ assert(std::get<1>(t2) == 2);
+ }
+
+ // user defined const copy assignment
+ {
+ std::pair<ConstMoveAssign, ConstMoveAssign> t1{1, 2};
+ const std::tuple<AssignableFrom<ConstMoveAssign>, ConstMoveAssign> t2{3, 4};
+ t2 = std::move(t1);
+ assert(std::get<0>(t2).v.val == 1);
+ assert(std::get<1>(t2).val == 2);
+ }
+
+ // make sure the right assignment operator of the type in the tuple is used
+ {
+ std::pair<TracedAssignment, TracedAssignment> t1{};
+ const std::tuple<AssignableFrom<TracedAssignment>, AssignableFrom<TracedAssignment>> t2{};
+ t2 = std::move(t1);
+ assert(std::get<0>(t2).v.constMoveAssign == 1);
+ assert(std::get<1>(t2).v.constMoveAssign == 1);
+ }
+
+ return true;
+}
+
+int main() {
+ test();
+
+// gcc cannot have mutable member in constant expression
+#if !defined(TEST_COMPILER_GCC)
+ static_assert(test());
+#endif
+ return 0;
+}
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
new file mode 100644
index 0000000000000..b5593d9e3723b
--- /dev/null
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/types.h
@@ -0,0 +1,138 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
new file mode 100644
index 0000000000000..10b7e2a5dfe13
--- /dev/null
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_const_move_pair.pass.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <tuple>
+
+// template <class... Types>
+// template <class Alloc, class U1, class U2>
+// constexpr explicit(see below)
+// tuple<Types...>::tuple(allocator_arg_t, const Alloc& a, const pair<U1,
+// U2>&& u);
+
+// Constraints:
+// - sizeof...(Types) is 2 and
+// - is_constructible_v<T0, decltype(get<0>(FWD(u)))> is true and
+// - is_constructible_v<T1, decltype(get<1>(FWD(u)))> is true.
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <cassert>
+#include <tuple>
+#include <utility>
+
+#include "convert_types.h"
+#include "test_allocator.h"
+
+// test constraints
+// sizeof...(Types) == 2
+static_assert(std::is_constructible_v<std::tuple<ConstMove, int>, std::allocator_arg_t, test_allocator<int>,
+ const std::pair<ConstMove, int>&&>);
+
+static_assert(!std::is_constructible_v< std::tuple<ConstMove>, std::allocator_arg_t, test_allocator<int>,
+ const std::pair<ConstMove, int>&&>);
+
+static_assert(!std::is_constructible_v< std::tuple<ConstMove, int, int>, std::allocator_arg_t, test_allocator<int>,
+ const std::pair<ConstMove, int>&&>);
+
+// test constraints
+// is_constructible_v<T0, decltype(get<0>(FWD(u)))> is true and
+// is_constructible_v<T1, decltype(get<1>(FWD(u)))> is true.
+static_assert(std::is_constructible_v<std::tuple<int, int>, std::allocator_arg_t, test_allocator<int>,
+ const std::pair<int, int>&&>);
+
+static_assert(!std::is_constructible_v< std::tuple<NoConstructorFromInt, int>, std::allocator_arg_t,
+ test_allocator<int>, const std::pair<int, int>&&>);
+
+static_assert(!std::is_constructible_v< std::tuple<int, NoConstructorFromInt>, std::allocator_arg_t,
+ test_allocator<int>, const std::pair<int, int>&&>);
+
+static_assert(!std::is_constructible_v< std::tuple<NoConstructorFromInt, NoConstructorFromInt>, std::allocator_arg_t,
+ test_allocator<int>, const std::pair<int, int>&&>);
+
+// test: The expression inside explicit is equivalent to:
+// !is_convertible_v<decltype(get<0>(FWD(u))), T0> ||
+// !is_convertible_v<decltype(get<1>(FWD(u))), T1>
+static_assert(
+ ImplicitlyConstructible< std::tuple<ConvertibleFrom<ConstMove>, ConvertibleFrom<ConstMove>>, std::allocator_arg_t,
+ test_allocator<int>, const std::pair<ConstMove, ConstMove>&&>);
+
+static_assert(
+ !ImplicitlyConstructible<std::tuple<ConvertibleFrom<ConstMove>, ExplicitConstructibleFrom<ConstMove>>,
+ std::allocator_arg_t, test_allocator<int>, const std::pair<ConstMove, ConstMove>&&>);
+
+static_assert(
+ !ImplicitlyConstructible<std::tuple<ExplicitConstructibleFrom<ConstMove>, ConvertibleFrom<ConstMove>>,
+ std::allocator_arg_t, test_allocator<int>, const std::pair<ConstMove, ConstMove>&&>);
+
+constexpr bool test() {
+ // test implicit conversions.
+ {
+ const std::pair<ConstMove, int> p{1, 2};
+ std::tuple<ConvertibleFrom<ConstMove>, ConvertibleFrom<int>> t = {std::allocator_arg, test_allocator<int>{},
+ std::move(p)};
+ assert(std::get<0>(t).v.val == 1);
+ assert(std::get<1>(t).v == 2);
+ assert(std::get<0>(t).alloc_constructed);
+ assert(std::get<1>(t).alloc_constructed);
+ }
+
+ // test explicit conversions.
+ {
+ const std::pair<ConstMove, int> p{1, 2};
+ std::tuple<ExplicitConstructibleFrom<ConstMove>, ExplicitConstructibleFrom<int>> t{
+ std::allocator_arg, test_allocator<int>{}, std::move(p)};
+ assert(std::get<0>(t).v.val == 1);
+ assert(std::get<1>(t).v == 2);
+ assert(std::get<0>(t).alloc_constructed);
+ assert(std::get<1>(t).alloc_constructed);
+ }
+
+ // non const overload should be called
+ {
+ const std::pair<TracedCopyMove, TracedCopyMove> p;
+ std::tuple<ConvertibleFrom<TracedCopyMove>, TracedCopyMove> t = {std::allocator_arg, test_allocator<int>{},
+ std::move(p)};
+ assert(constMoveCtrCalled(std::get<0>(t).v));
+ assert(constMoveCtrCalled(std::get<1>(t)));
+ assert(std::get<0>(t).alloc_constructed);
+ assert(std::get<1>(t).alloc_constructed);
+ }
+
+ return true;
+}
+
+int main() {
+ test();
+ static_assert(test());
+
+ return 0;
+}
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
new file mode 100644
index 0000000000000..a24fee7e79b58
--- /dev/null
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_const_move.pass.cpp
@@ -0,0 +1,156 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <tuple>
+
+// template <class... Types>
+// template<class Alloc, class... UTypes>
+// constexpr explicit(see below)
+// tuple<Types...>::tuple(allocator_arg_t, const Alloc& a,
+// const tuple<UTypes...>&&);
+//
+// Constraints:
+// sizeof...(Types) equals sizeof...(UTypes) &&
+// (is_constructible_v<Types, decltype(get<I>(FWD(u)))> && ...) is true &&
+// (
+// sizeof...(Types) is not 1 ||
+// (
+// !is_convertible_v<decltype(u), T> &&
+// !is_constructible_v<T, decltype(u)> &&
+// !is_same_v<T, U>
+// )
+// )
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <cassert>
+#include <tuple>
+
+#include "convert_types.h"
+#include "test_allocator.h"
+#include "test_macros.h"
+
+// test: The expression inside explicit is equivalent to:
+// !(is_convertible_v<decltype(get<I>(FWD(u))), Types> && ...)
+static_assert(ImplicitlyConstructible< std::tuple<ConvertibleFrom<ConstMove>>, std::allocator_arg_t,
+ const test_allocator<int>&, const std::tuple<ConstMove>&&>);
+
+static_assert(
+ ImplicitlyConstructible< std::tuple<ConvertibleFrom<ConstMove>, ConvertibleFrom<ConstMove>>, std::allocator_arg_t,
+ const test_allocator<int>&, const std::tuple<ConstMove, ConstMove>&&>);
+
+static_assert(!ImplicitlyConstructible<std::tuple<ExplicitConstructibleFrom<ConstMove>>, std::allocator_arg_t,
+ const test_allocator<int>&, const std::tuple<ConstMove>&&>);
+
+static_assert(!ImplicitlyConstructible<std::tuple<ExplicitConstructibleFrom<ConstMove>, ConvertibleFrom<ConstMove>>,
+ std::allocator_arg_t, const test_allocator<int>&,
+ const std::tuple<ConstMove, ConstMove>&&>);
+
+constexpr bool test() {
+ // test implicit conversions.
+ // sizeof...(Types) == 1
+ {
+ const std::tuple<ConstMove> t1{1};
+ std::tuple<ConvertibleFrom<ConstMove>> t2 = {std::allocator_arg, test_allocator<int>{}, std::move(t1)};
+ assert(std::get<0>(t2).v.val == 1);
+ assert(std::get<0>(t2).alloc_constructed);
+ }
+
+ // test implicit conversions.
+ // sizeof...(Types) > 1
+ {
+ const std::tuple<ConstMove, int> t1{1, 2};
+ std::tuple<ConvertibleFrom<ConstMove>, int> t2 = {std::allocator_arg_t{}, test_allocator<int>{}, std::move(t1)};
+ assert(std::get<0>(t2).v.val == 1);
+ assert(std::get<1>(t2) == 2);
+ assert(std::get<0>(t2).alloc_constructed);
+ }
+
+ // test explicit conversions.
+ // sizeof...(Types) == 1
+ {
+ const std::tuple<ConstMove> t1{1};
+ std::tuple<ExplicitConstructibleFrom<ConstMove>> t2{std::allocator_arg_t{}, test_allocator<int>{}, std::move(t1)};
+ assert(std::get<0>(t2).v.val == 1);
+ assert(std::get<0>(t2).alloc_constructed);
+ }
+
+ // test explicit conversions.
+ // sizeof...(Types) > 1
+ {
+ const std::tuple<ConstMove, int> t1{1, 2};
+ std::tuple<ExplicitConstructibleFrom<ConstMove>, int> t2{std::allocator_arg_t{}, test_allocator<int>{},
+ std::move(t1)};
+ assert(std::get<0>(t2).v.val == 1);
+ assert(std::get<1>(t2) == 2);
+ assert(std::get<0>(t2).alloc_constructed);
+ }
+
+ // test constraints
+
+ // sizeof...(Types) != sizeof...(UTypes)
+ static_assert(!std::is_constructible_v<std::tuple<int, int>, std::allocator_arg_t, const test_allocator<int>&,
+ const std::tuple<int>&&>);
+ static_assert(!std::is_constructible_v<std::tuple<int, int, int>, std::allocator_arg_t, const test_allocator<int>&,
+ const std::tuple<int, int>&&>);
+
+ // !(is_constructible_v<Types, decltype(get<I>(FWD(u)))> && ...)
+ static_assert(!std::is_constructible_v< std::tuple<int, NoConstructorFromInt>, std::allocator_arg_t,
+ const test_allocator<int>&, const std::tuple<int, int>&&>);
+
+ // sizeof...(Types) == 1 && other branch of "||" satisfied
+ {
+ const std::tuple<TracedCopyMove> t1{};
+ std::tuple<ConvertibleFrom<TracedCopyMove>> t2{std::allocator_arg_t{}, test_allocator<int>{}, std::move(t1)};
+ assert(constMoveCtrCalled(std::get<0>(t2).v));
+ assert(std::get<0>(t2).alloc_constructed);
+ }
+
+ // sizeof...(Types) == 1 && is_same_v<T, U>
+ {
+ const std::tuple<TracedCopyMove> t1{};
+ std::tuple<TracedCopyMove> t2{std::allocator_arg_t{}, test_allocator<int>{}, std::move(t1)};
+ assert(!constMoveCtrCalled(std::get<0>(t2)));
+ assert(std::get<0>(t2).alloc_constructed);
+ }
+
+ // sizeof...(Types) != 1
+ {
+ const std::tuple<TracedCopyMove, TracedCopyMove> t1{};
+ std::tuple<TracedCopyMove, TracedCopyMove> t2{std::allocator_arg_t{}, test_allocator<int>{}, std::move(t1)};
+ assert(constMoveCtrCalled(std::get<0>(std::move(t2))));
+ assert(std::get<0>(t2).alloc_constructed);
+ }
+
+ // These two test points cause gcc to ICE
+#if !defined(TEST_COMPILER_GCC)
+ // sizeof...(Types) == 1 && is_convertible_v<decltype(u), T>
+ {
+ const std::tuple<CvtFromTupleRef> t1{};
+ std::tuple<ConvertibleFrom<CvtFromTupleRef>> t2{std::allocator_arg_t{}, test_allocator<int>{}, std::move(t1)};
+ assert(!constMoveCtrCalled(std::get<0>(t2).v));
+ assert(std::get<0>(t2).alloc_constructed);
+ }
+
+ // sizeof...(Types) == 1 && is_constructible_v<decltype(u), T>
+ {
+ const std::tuple<ExplicitCtrFromTupleRef> t1{};
+ std::tuple<ConvertibleFrom<ExplicitCtrFromTupleRef>> t2{std::allocator_arg_t{}, test_allocator<int>{},
+ std::move(t1)};
+ assert(!constMoveCtrCalled(std::get<0>(t2).v));
+ assert(std::get<0>(t2).alloc_constructed);
+ }
+#endif
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_copy.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_copy.pass.cpp
index 681e2e70c5f1d..4ed413477a8c2 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_copy.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_copy.pass.cpp
@@ -11,6 +11,7 @@
// template <class... Types> class tuple;
// template <class Alloc, class... UTypes>
+// constexpr // since c++20
// tuple(allocator_arg_t, const Alloc& a, const tuple<UTypes...>&);
// UNSUPPORTED: c++03
@@ -21,6 +22,7 @@
#include "test_macros.h"
#include "allocators.h"
+#include "test_allocator.h"
#include "../alloc_first.h"
#include "../alloc_last.h"
@@ -34,6 +36,15 @@ struct Implicit {
Implicit(int x) : value(x) {}
};
+#if _LIBCPP_STD_VER > 17
+constexpr bool alloc_copy_constructor_is_constexpr() {
+ const std::tuple<int> t1 = 1;
+ std::tuple<int> t2 = {std::allocator_arg, test_allocator<int>{}, t1};
+ assert(std::get<0>(t2) == 1);
+ return true;
+}
+#endif
+
int main(int, char**)
{
{
@@ -95,5 +106,8 @@ int main(int, char**)
std::tuple<long long> t0(derived, A1<int>(), from);
}
+#if _LIBCPP_STD_VER > 17
+ static_assert(alloc_copy_constructor_is_constexpr());
+#endif
return 0;
}
diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_move.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_move.pass.cpp
index b823e8f2afe91..1423c6174339d 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_move.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_move.pass.cpp
@@ -22,6 +22,7 @@
#include "test_macros.h"
#include "allocators.h"
+#include "test_allocator.h"
#include "../alloc_first.h"
#include "../alloc_last.h"
@@ -50,6 +51,15 @@ struct Implicit {
Implicit(int x) : value(x) {}
};
+#if _LIBCPP_STD_VER > 17
+constexpr bool alloc_move_constructor_is_constexpr() {
+ std::tuple<int> t1 = 1;
+ std::tuple<int> t2 = {std::allocator_arg, test_allocator<int>{}, std::move(t1)};
+ assert(std::get<0>(t2) == 1);
+ return true;
+}
+#endif
+
int main(int, char**)
{
{
@@ -109,5 +119,9 @@ int main(int, char**)
std::tuple<long long> t0(derived, A1<int>(), std::move(from));
}
+#if _LIBCPP_STD_VER > 17
+ static_assert(alloc_move_constructor_is_constexpr());
+#endif
+
return 0;
}
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
new file mode 100644
index 0000000000000..5c8249710f274
--- /dev/null
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_convert_non_const_copy.pass.cpp
@@ -0,0 +1,154 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <tuple>
+
+// template <class... Types>
+// template<class Alloc, class... UTypes>
+// constexpr explicit(see below)
+// tuple<Types...>::tuple(allocator_arg_t, const Alloc& a,
+// tuple<UTypes...>&);
+//
+// Constraints:
+// sizeof...(Types) equals sizeof...(UTypes) &&
+// (is_constructible_v<Types, decltype(get<I>(FWD(u)))> && ...) is true &&
+// (
+// sizeof...(Types) is not 1 ||
+// (
+// !is_convertible_v<decltype(u), T> &&
+// !is_constructible_v<T, decltype(u)> &&
+// !is_same_v<T, U>
+// )
+// )
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <cassert>
+#include <tuple>
+
+#include "convert_types.h"
+#include "test_allocator.h"
+#include "test_macros.h"
+
+// test: The expression inside explicit is equivalent to:
+// !(is_convertible_v<decltype(get<I>(FWD(u))), Types> && ...)
+static_assert(ImplicitlyConstructible< std::tuple<ConvertibleFrom<MutableCopy>>, std::allocator_arg_t,
+ const test_allocator<int>&, std::tuple<MutableCopy>&>);
+
+static_assert(
+ ImplicitlyConstructible< std::tuple<ConvertibleFrom<MutableCopy>, ConvertibleFrom<MutableCopy>>,
+ std::allocator_arg_t, const test_allocator<int>&, std::tuple<MutableCopy, MutableCopy>&>);
+
+static_assert(!ImplicitlyConstructible<std::tuple<ExplicitConstructibleFrom<MutableCopy>>, std::allocator_arg_t,
+ const test_allocator<int>&, std::tuple<MutableCopy>&>);
+
+static_assert(
+ !ImplicitlyConstructible<std::tuple<ExplicitConstructibleFrom<MutableCopy>, ConvertibleFrom<MutableCopy>>,
+ std::allocator_arg_t, const test_allocator<int>&, std::tuple<MutableCopy, MutableCopy>&>);
+
+constexpr bool test() {
+ // test implicit conversions.
+ // sizeof...(Types) == 1
+ {
+ std::tuple<MutableCopy> t1{1};
+ std::tuple<ConvertibleFrom<MutableCopy>> t2 = {std::allocator_arg, test_allocator<int>{}, t1};
+ assert(std::get<0>(t2).v.val == 1);
+ assert(std::get<0>(t2).alloc_constructed);
+ }
+
+ // test implicit conversions.
+ // sizeof...(Types) > 1
+ {
+ std::tuple<MutableCopy, int> t1{1, 2};
+ std::tuple<ConvertibleFrom<MutableCopy>, int> t2 = {std::allocator_arg_t{}, test_allocator<int>{}, t1};
+ assert(std::get<0>(t2).v.val == 1);
+ assert(std::get<1>(t2) == 2);
+ assert(std::get<0>(t2).alloc_constructed);
+ }
+
+ // test explicit conversions.
+ // sizeof...(Types) == 1
+ {
+ std::tuple<MutableCopy> t1{1};
+ std::tuple<ExplicitConstructibleFrom<MutableCopy>> t2{std::allocator_arg_t{}, test_allocator<int>{}, t1};
+ assert(std::get<0>(t2).v.val == 1);
+ assert(std::get<0>(t2).alloc_constructed);
+ }
+
+ // test explicit conversions.
+ // sizeof...(Types) > 1
+ {
+ std::tuple<MutableCopy, int> t1{1, 2};
+ std::tuple<ExplicitConstructibleFrom<MutableCopy>, int> t2{std::allocator_arg_t{}, test_allocator<int>{}, t1};
+ assert(std::get<0>(t2).v.val == 1);
+ assert(std::get<1>(t2) == 2);
+ assert(std::get<0>(t2).alloc_constructed);
+ }
+
+ // test constraints
+
+ // sizeof...(Types) != sizeof...(UTypes)
+ static_assert(!std::is_constructible_v<std::tuple<int, int>, std::allocator_arg_t, const test_allocator<int>&,
+ std::tuple<int>&>);
+ static_assert(!std::is_constructible_v<std::tuple<int, int, int>, std::allocator_arg_t, const test_allocator<int>&,
+ std::tuple<int, int>&>);
+
+ // !(is_constructible_v<Types, decltype(get<I>(FWD(u)))> && ...)
+ static_assert(!std::is_constructible_v< std::tuple<int, NoConstructorFromInt>, std::allocator_arg_t,
+ const test_allocator<int>&, std::tuple<int, int>&>);
+
+ // sizeof...(Types) == 1 && other branch of "||" satisfied
+ {
+ std::tuple<TracedCopyMove> t1{};
+ std::tuple<ConvertibleFrom<TracedCopyMove>> t2{std::allocator_arg_t{}, test_allocator<int>{}, t1};
+ assert(nonConstCopyCtrCalled(std::get<0>(t2).v));
+ assert(std::get<0>(t2).alloc_constructed);
+ }
+
+ // sizeof...(Types) == 1 && is_same_v<T, U>
+ {
+ std::tuple<TracedCopyMove> t1{};
+ std::tuple<TracedCopyMove> t2{std::allocator_arg_t{}, test_allocator<int>{}, t1};
+ assert(!nonConstCopyCtrCalled(std::get<0>(t2)));
+ assert(std::get<0>(t2).alloc_constructed);
+ }
+
+ // sizeof...(Types) != 1
+ {
+ std::tuple<TracedCopyMove, TracedCopyMove> t1{};
+ std::tuple<TracedCopyMove, TracedCopyMove> t2{std::allocator_arg_t{}, test_allocator<int>{}, t1};
+ assert(nonConstCopyCtrCalled(std::get<0>(t2)));
+ assert(std::get<0>(t2).alloc_constructed);
+ }
+
+ // These two test points cause gcc to ICE
+#if !defined(TEST_COMPILER_GCC)
+ // sizeof...(Types) == 1 && is_convertible_v<decltype(u), T>
+ {
+ std::tuple<CvtFromTupleRef> t1{};
+ std::tuple<ConvertibleFrom<CvtFromTupleRef>> t2{std::allocator_arg_t{}, test_allocator<int>{}, t1};
+ assert(!nonConstCopyCtrCalled(std::get<0>(t2).v));
+ assert(std::get<0>(t2).alloc_constructed);
+ }
+
+ // sizeof...(Types) == 1 && is_constructible_v<decltype(u), T>
+ {
+ std::tuple<ExplicitCtrFromTupleRef> t1{};
+ std::tuple<ConvertibleFrom<ExplicitCtrFromTupleRef>> t2{std::allocator_arg_t{}, test_allocator<int>{}, t1};
+ assert(!nonConstCopyCtrCalled(std::get<0>(t2).v));
+ assert(std::get<0>(t2).alloc_constructed);
+ }
+#endif
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
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
new file mode 100644
index 0000000000000..86761b5044fb4
--- /dev/null
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/alloc_non_const_pair.pass.cpp
@@ -0,0 +1,111 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <tuple>
+
+// template <class... Types>
+// template <class Alloc, class U1, class U2>
+// constexpr explicit(see below)
+// tuple<Types...>::tuple(allocator_arg_t, const Alloc& a, pair<U1, U2>&
+// u);
+
+// Constraints:
+// - sizeof...(Types) is 2 and
+// - is_constructible_v<T0, decltype(get<0>(FWD(u)))> is true and
+// - is_constructible_v<T1, decltype(get<1>(FWD(u)))> is true.
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <cassert>
+#include <tuple>
+#include <utility>
+
+#include "convert_types.h"
+#include "test_allocator.h"
+
+// test constraints
+// sizeof...(Types) == 2
+static_assert(std::is_constructible_v<std::tuple<MutableCopy, int>, std::allocator_arg_t, test_allocator<int>,
+ std::pair<MutableCopy, int>&>);
+
+static_assert(!std::is_constructible_v< std::tuple<MutableCopy>, std::allocator_arg_t, test_allocator<int>,
+ std::pair<MutableCopy, int>&>);
+
+static_assert(!std::is_constructible_v< std::tuple<MutableCopy, int, int>, std::allocator_arg_t, test_allocator<int>,
+ std::pair<MutableCopy, int>&>);
+
+// test constraints
+// is_constructible_v<T0, decltype(get<0>(FWD(u)))> is true and
+// is_constructible_v<T1, decltype(get<1>(FWD(u)))> is true.
+static_assert(
+ std::is_constructible_v<std::tuple<int, int>, std::allocator_arg_t, test_allocator<int>, std::pair<int, int>&>);
+
+static_assert(!std::is_constructible_v< std::tuple<NoConstructorFromInt, int>, std::allocator_arg_t,
+ test_allocator<int>, std::pair<int, int>&>);
+
+static_assert(!std::is_constructible_v< std::tuple<int, NoConstructorFromInt>, std::allocator_arg_t,
+ test_allocator<int>, std::pair<int, int>&>);
+
+static_assert(!std::is_constructible_v< std::tuple<NoConstructorFromInt, NoConstructorFromInt>, std::allocator_arg_t,
+ test_allocator<int>, std::pair<int, int>&>);
+
+// test: The expression inside explicit is equivalent to:
+// !is_convertible_v<decltype(get<0>(FWD(u))), T0> ||
+// !is_convertible_v<decltype(get<1>(FWD(u))), T1>
+static_assert(ImplicitlyConstructible<std::tuple<ConvertibleFrom<MutableCopy>, ConvertibleFrom<MutableCopy>>,
+ std::allocator_arg_t, test_allocator<int>, std::pair<MutableCopy, MutableCopy>&>);
+
+static_assert(
+ !ImplicitlyConstructible<std::tuple<ConvertibleFrom<MutableCopy>, ExplicitConstructibleFrom<MutableCopy>>,
+ std::allocator_arg_t, test_allocator<int>, std::pair<MutableCopy, MutableCopy>&>);
+
+static_assert(
+ !ImplicitlyConstructible<std::tuple<ExplicitConstructibleFrom<MutableCopy>, ConvertibleFrom<MutableCopy>>,
+ std::allocator_arg_t, test_allocator<int>, std::pair<MutableCopy, MutableCopy>&>);
+
+constexpr bool test() {
+ // test implicit conversions.
+ {
+ std::pair<MutableCopy, int> p{1, 2};
+ std::tuple<ConvertibleFrom<MutableCopy>, ConvertibleFrom<int>> t = {std::allocator_arg, test_allocator<int>{}, p};
+ assert(std::get<0>(t).v.val == 1);
+ assert(std::get<1>(t).v == 2);
+ assert(std::get<0>(t).alloc_constructed);
+ assert(std::get<1>(t).alloc_constructed);
+ }
+
+ // test explicit conversions.
+ {
+ std::pair<MutableCopy, int> p{1, 2};
+ std::tuple<ExplicitConstructibleFrom<MutableCopy>, ExplicitConstructibleFrom<int>> t{std::allocator_arg,
+ test_allocator<int>{}, p};
+ assert(std::get<0>(t).v.val == 1);
+ assert(std::get<1>(t).v == 2);
+ assert(std::get<0>(t).alloc_constructed);
+ assert(std::get<1>(t).alloc_constructed);
+ }
+
+ // non const overload should be called
+ {
+ std::pair<TracedCopyMove, TracedCopyMove> p;
+ std::tuple<ConvertibleFrom<TracedCopyMove>, TracedCopyMove> t = {std::allocator_arg, test_allocator<int>{}, p};
+ assert(nonConstCopyCtrCalled(std::get<0>(t).v));
+ assert(nonConstCopyCtrCalled(std::get<1>(t)));
+ assert(std::get<0>(t).alloc_constructed);
+ assert(std::get<1>(t).alloc_constructed);
+ }
+
+ return true;
+}
+
+int main() {
+ test();
+ static_assert(test());
+
+ return 0;
+}
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
new file mode 100644
index 0000000000000..5c5b20d6d755d
--- /dev/null
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/const_move_pair.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
+//
+//===----------------------------------------------------------------------===//
+
+// <tuple>
+
+// template <class... Types>
+// template <class U1, class U2>
+// constexpr explicit(see below) tuple<Types...>::tuple(const pair<U1, U2>&& u);
+
+// Constraints:
+// - sizeof...(Types) is 2 and
+// - is_constructible_v<T0, decltype(get<0>(FWD(u)))> is true and
+// - is_constructible_v<T1, decltype(get<1>(FWD(u)))> is true.
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <cassert>
+#include <tuple>
+#include <utility>
+
+#include "convert_types.h"
+
+// test constraints
+// sizeof...(Types) == 2
+static_assert(std::is_constructible_v<std::tuple<ConstMove, int>, const std::pair<ConstMove, int>&&>);
+
+static_assert(!std::is_constructible_v<std::tuple<ConstMove>, const std::pair<ConstMove, int>&&>);
+
+static_assert(!std::is_constructible_v<std::tuple<ConstMove, int, int>, const std::pair<ConstMove, int>&&>);
+
+// test constraints
+// is_constructible_v<T0, decltype(get<0>(FWD(u)))> is true and
+// is_constructible_v<T1, decltype(get<1>(FWD(u)))> is true.
+static_assert(std::is_constructible_v<std::tuple<int, int>, const std::pair<int, int>&&>);
+
+static_assert(!std::is_constructible_v<std::tuple<NoConstructorFromInt, int>, const std::pair<int, int>&&>);
+
+static_assert(!std::is_constructible_v<std::tuple<int, NoConstructorFromInt>, const std::pair<int, int>&&>);
+
+static_assert(
+ !std::is_constructible_v< std::tuple<NoConstructorFromInt, NoConstructorFromInt>, const std::pair<int, int>&&>);
+
+// test: The expression inside explicit is equivalent to:
+// !is_convertible_v<decltype(get<0>(FWD(u))), T0> ||
+// !is_convertible_v<decltype(get<1>(FWD(u))), T1>
+static_assert(std::is_convertible_v<const std::pair<ConstMove, ConstMove>&&,
+ std::tuple<ConvertibleFrom<ConstMove>, ConvertibleFrom<ConstMove>>>);
+
+static_assert(!std::is_convertible_v<const std::pair<ConstMove, ConstMove>&&,
+ std::tuple<ExplicitConstructibleFrom<ConstMove>, ConvertibleFrom<ConstMove>>>);
+
+static_assert(!std::is_convertible_v<const std::pair<ConstMove, ConstMove>&&,
+ std::tuple<ConvertibleFrom<ConstMove>, ExplicitConstructibleFrom<ConstMove>>>);
+
+constexpr bool test() {
+ // test implicit conversions.
+ {
+ const std::pair<ConstMove, int> p{1, 2};
+ std::tuple<ConvertibleFrom<ConstMove>, ConvertibleFrom<int>> t = std::move(p);
+ assert(std::get<0>(t).v.val == 1);
+ assert(std::get<1>(t).v == 2);
+ }
+
+ // test explicit conversions.
+ {
+ const std::pair<ConstMove, int> p{1, 2};
+ std::tuple<ExplicitConstructibleFrom<ConstMove>, ExplicitConstructibleFrom<int>> t{std::move(p)};
+ assert(std::get<0>(t).v.val == 1);
+ assert(std::get<1>(t).v == 2);
+ }
+
+ // non const overload should be called
+ {
+ const std::pair<TracedCopyMove, TracedCopyMove> p;
+ std::tuple<ConvertibleFrom<TracedCopyMove>, TracedCopyMove> t = std::move(p);
+ assert(constMoveCtrCalled(std::get<0>(t).v));
+ assert(constMoveCtrCalled(std::get<1>(t)));
+ }
+
+ return true;
+}
+
+int main() {
+ test();
+ static_assert(test());
+
+ return 0;
+}
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
new file mode 100644
index 0000000000000..30c11b3e22333
--- /dev/null
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_const_move.pass.cpp
@@ -0,0 +1,138 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <tuple>
+
+// template <class... Types>
+// template <class... UTypes>
+// constexpr explicit(see below) tuple<Types>::tuple(const
+// tuple<UTypes...>&&);
+//
+// Constraints:
+// sizeof...(Types) equals sizeof...(UTypes) &&
+// (is_constructible_v<Types, decltype(get<I>(FWD(u)))> && ...) is true &&
+// (
+// sizeof...(Types) is not 1 ||
+// (
+// !is_convertible_v<decltype(u), T> &&
+// !is_constructible_v<T, decltype(u)> &&
+// !is_same_v<T, U>
+// )
+// )
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <cassert>
+#include <tuple>
+
+#include "convert_types.h"
+#include "test_macros.h"
+
+// test: The expression inside explicit is equivalent to:
+// !(is_convertible_v<decltype(get<I>(FWD(u))), Types> && ...)
+static_assert(std::is_convertible_v<const std::tuple<ConstMove>&&, std::tuple<ConvertibleFrom<ConstMove>>>);
+
+static_assert(std::is_convertible_v<const std::tuple<ConstMove, ConstMove>&&,
+ std::tuple<ConvertibleFrom<ConstMove>, ConvertibleFrom<ConstMove>>>);
+
+static_assert(
+ !std::is_convertible_v<const std::tuple<MutableCopy>&&, std::tuple<ExplicitConstructibleFrom<ConstMove>>>);
+
+static_assert(!std::is_convertible_v<const std::tuple<ConstMove, ConstMove>&&,
+ std::tuple<ConvertibleFrom<ConstMove>, ExplicitConstructibleFrom<ConstMove>>>);
+
+constexpr bool test() {
+ // test implicit conversions.
+ // sizeof...(Types) == 1
+ {
+ const std::tuple<ConstMove> t1{1};
+ std::tuple<ConvertibleFrom<ConstMove>> t2 = std::move(t1);
+ assert(std::get<0>(t2).v.val == 1);
+ }
+
+ // test implicit conversions.
+ // sizeof...(Types) > 1
+ {
+ const std::tuple<ConstMove, int> t1{1, 2};
+ std::tuple<ConvertibleFrom<ConstMove>, int> t2 = std::move(t1);
+ assert(std::get<0>(t2).v.val == 1);
+ assert(std::get<1>(t2) == 2);
+ }
+
+ // test explicit conversions.
+ // sizeof...(Types) == 1
+ {
+ const std::tuple<ConstMove> t1{1};
+ std::tuple<ExplicitConstructibleFrom<ConstMove>> t2{std::move(t1)};
+ assert(std::get<0>(t2).v.val == 1);
+ }
+
+ // test explicit conversions.
+ // sizeof...(Types) > 1
+ {
+ const std::tuple<ConstMove, int> t1{1, 2};
+ std::tuple<ExplicitConstructibleFrom<ConstMove>, int> t2{std::move(t1)};
+ assert(std::get<0>(t2).v.val == 1);
+ assert(std::get<1>(t2) == 2);
+ }
+
+ // test constraints
+
+ // sizeof...(Types) != sizeof...(UTypes)
+ static_assert(!std::is_constructible_v<std::tuple<int, int>, const std::tuple<int>&&>);
+ static_assert(!std::is_constructible_v<std::tuple<int, int, int>, const std::tuple<int, int>&&>);
+
+ // !(is_constructible_v<Types, decltype(get<I>(FWD(u)))> && ...)
+ static_assert(!std::is_constructible_v<std::tuple<int, NoConstructorFromInt>, const std::tuple<int, int>&&>);
+
+ // sizeof...(Types) == 1 && other branch of "||" satisfied
+ {
+ const std::tuple<TracedCopyMove> t1{};
+ std::tuple<ConvertibleFrom<TracedCopyMove>> t2{std::move(t1)};
+ assert(constMoveCtrCalled(std::get<0>(t2).v));
+ }
+
+ // sizeof...(Types) == 1 && is_same_v<T, U>
+ {
+ const std::tuple<TracedCopyMove> t1{};
+ std::tuple<TracedCopyMove> t2{t1};
+ assert(!constMoveCtrCalled(std::get<0>(t2)));
+ }
+
+ // sizeof...(Types) != 1
+ {
+ const std::tuple<TracedCopyMove, TracedCopyMove> t1{};
+ std::tuple<TracedCopyMove, TracedCopyMove> t2{std::move(t1)};
+ assert(constMoveCtrCalled(std::get<0>(t2)));
+ }
+
+ // These two test points cause gcc to ICE
+#if !defined(TEST_COMPILER_GCC)
+ // sizeof...(Types) == 1 && is_convertible_v<decltype(u), T>
+ {
+ const std::tuple<CvtFromConstTupleRefRef> t1{};
+ std::tuple<ConvertibleFrom<CvtFromConstTupleRefRef>> t2{std::move(t1)};
+ assert(!constMoveCtrCalled(std::get<0>(t2).v));
+ }
+
+ // sizeof...(Types) == 1 && is_constructible_v<decltype(u), T>
+ {
+ const std::tuple<ExplicitCtrFromConstTupleRefRef> t1{};
+ std::tuple<ConvertibleFrom<ExplicitCtrFromConstTupleRefRef>> t2{std::move(t1)};
+ assert(!constMoveCtrCalled(std::get<0>(t2).v));
+ }
+#endif
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
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
new file mode 100644
index 0000000000000..bb2baba578704
--- /dev/null
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_non_const_copy.pass.cpp
@@ -0,0 +1,136 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <tuple>
+
+// template <class... Types>
+// template <class... UTypes>
+// constexpr explicit(see below) tuple<Types>::tuple(tuple<UTypes...>&);
+//
+// Constraints:
+// sizeof...(Types) equals sizeof...(UTypes) &&
+// (is_constructible_v<Types, decltype(get<I>(FWD(u)))> && ...) is true &&
+// (
+// sizeof...(Types) is not 1 ||
+// (
+// !is_convertible_v<decltype(u), T> &&
+// !is_constructible_v<T, decltype(u)> &&
+// !is_same_v<T, U>
+// )
+// )
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <cassert>
+#include <tuple>
+
+#include "convert_types.h"
+#include "test_macros.h"
+
+// test: The expression inside explicit is equivalent to:
+// !(is_convertible_v<decltype(get<I>(FWD(u))), Types> && ...)
+static_assert(std::is_convertible_v<std::tuple<MutableCopy>&, std::tuple<ConvertibleFrom<MutableCopy>>>);
+
+static_assert(std::is_convertible_v<std::tuple<MutableCopy, MutableCopy>&,
+ std::tuple<ConvertibleFrom<MutableCopy>, ConvertibleFrom<MutableCopy>>>);
+
+static_assert(!std::is_convertible_v<std::tuple<MutableCopy>&, std::tuple<ExplicitConstructibleFrom<MutableCopy>>>);
+
+static_assert(!std::is_convertible_v<std::tuple<MutableCopy, MutableCopy>&,
+ std::tuple<ConvertibleFrom<MutableCopy>, ExplicitConstructibleFrom<MutableCopy>>>);
+
+constexpr bool test() {
+ // test implicit conversions.
+ // sizeof...(Types) == 1
+ {
+ std::tuple<MutableCopy> t1{1};
+ std::tuple<ConvertibleFrom<MutableCopy>> t2 = t1;
+ assert(std::get<0>(t2).v.val == 1);
+ }
+
+ // test implicit conversions.
+ // sizeof...(Types) > 1
+ {
+ std::tuple<MutableCopy, int> t1{1, 2};
+ std::tuple<ConvertibleFrom<MutableCopy>, int> t2 = t1;
+ assert(std::get<0>(t2).v.val == 1);
+ assert(std::get<1>(t2) == 2);
+ }
+
+ // test explicit conversions.
+ // sizeof...(Types) == 1
+ {
+ std::tuple<MutableCopy> t1{1};
+ std::tuple<ExplicitConstructibleFrom<MutableCopy>> t2{t1};
+ assert(std::get<0>(t2).v.val == 1);
+ }
+
+ // test explicit conversions.
+ // sizeof...(Types) > 1
+ {
+ std::tuple<MutableCopy, int> t1{1, 2};
+ std::tuple<ExplicitConstructibleFrom<MutableCopy>, int> t2{t1};
+ assert(std::get<0>(t2).v.val == 1);
+ assert(std::get<1>(t2) == 2);
+ }
+
+ // test constraints
+
+ // sizeof...(Types) != sizeof...(UTypes)
+ static_assert(!std::is_constructible_v<std::tuple<int, int>, std::tuple<int>&>);
+ static_assert(!std::is_constructible_v<std::tuple<int>, std::tuple<int, int>&>);
+ static_assert(!std::is_constructible_v<std::tuple<int, int, int>, std::tuple<int, int>&>);
+
+ // !(is_constructible_v<Types, decltype(get<I>(FWD(u)))> && ...)
+ static_assert(!std::is_constructible_v<std::tuple<int, NoConstructorFromInt>, std::tuple<int, int>&>);
+
+ // sizeof...(Types) == 1 && other branch of "||" satisfied
+ {
+ std::tuple<TracedCopyMove> t1{};
+ std::tuple<ConvertibleFrom<TracedCopyMove>> t2{t1};
+ assert(nonConstCopyCtrCalled(std::get<0>(t2).v));
+ }
+
+ // sizeof...(Types) == 1 && is_same_v<T, U>
+ {
+ std::tuple<TracedCopyMove> t1{};
+ std::tuple<TracedCopyMove> t2{t1};
+ assert(!nonConstCopyCtrCalled(std::get<0>(t2)));
+ }
+
+ // sizeof...(Types) != 1
+ {
+ std::tuple<TracedCopyMove, TracedCopyMove> t1{};
+ std::tuple<TracedCopyMove, TracedCopyMove> t2{t1};
+ assert(nonConstCopyCtrCalled(std::get<0>(t2)));
+ }
+
+ // These two test points cause gcc to ICE
+#if !defined(TEST_COMPILER_GCC)
+ // sizeof...(Types) == 1 && is_convertible_v<decltype(u), T>
+ {
+ std::tuple<CvtFromTupleRef> t1{};
+ std::tuple<ConvertibleFrom<CvtFromTupleRef>> t2{t1};
+ assert(!nonConstCopyCtrCalled(std::get<0>(t2).v));
+ }
+
+ // sizeof...(Types) == 1 && is_constructible_v<decltype(u), T>
+ {
+ std::tuple<ExplicitCtrFromTupleRef> t1{};
+ std::tuple<ConvertibleFrom<ExplicitCtrFromTupleRef>> t2{t1};
+ assert(!nonConstCopyCtrCalled(std::get<0>(t2).v));
+ }
+#endif
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_types.h b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_types.h
new file mode 100644
index 0000000000000..0bdfec3cd4e86
--- /dev/null
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/convert_types.h
@@ -0,0 +1,218 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_CNSTR_TYPES_H
+#define LIBCXX_TEST_STD_UTILITIES_TUPLE_CNSTR_TYPES_H
+
+#include "test_allocator.h"
+#include <type_traits>
+
+struct MutableCopy {
+ int val;
+ bool alloc_constructed{false};
+
+ constexpr MutableCopy() = default;
+ constexpr MutableCopy(int _val) : val(_val) {}
+ constexpr MutableCopy(MutableCopy&) = default;
+ constexpr MutableCopy(const MutableCopy&) = delete;
+
+ constexpr MutableCopy(std::allocator_arg_t, const test_allocator<int>&, MutableCopy& o)
+ : val(o.val), alloc_constructed(true) {}
+};
+
+template <>
+struct std::uses_allocator<MutableCopy, test_allocator<int>> : std::true_type {};
+
+struct ConstCopy {
+ int val;
+ bool alloc_constructed{false};
+
+ constexpr ConstCopy() = default;
+ constexpr ConstCopy(int _val) : val(_val) {}
+ constexpr ConstCopy(const ConstCopy&) = default;
+ constexpr ConstCopy(ConstCopy&) = delete;
+
+ constexpr ConstCopy(std::allocator_arg_t, const test_allocator<int>&, const ConstCopy& o)
+ : val(o.val), alloc_constructed(true) {}
+};
+
+template <>
+struct std::uses_allocator<ConstCopy, test_allocator<int>> : std::true_type {};
+
+struct MutableMove {
+ int val;
+ bool alloc_constructed{false};
+
+ constexpr MutableMove() = default;
+ constexpr MutableMove(int _val) : val(_val) {}
+ constexpr MutableMove(MutableMove&&) = default;
+ constexpr MutableMove(const MutableMove&&) = delete;
+
+ constexpr MutableMove(std::allocator_arg_t, const test_allocator<int>&, MutableMove&& o)
+ : val(o.val), alloc_constructed(true) {}
+};
+
+template <>
+struct std::uses_allocator<MutableMove, test_allocator<int>> : std::true_type {};
+
+struct ConstMove {
+ int val;
+ bool alloc_constructed{false};
+
+ constexpr ConstMove() = default;
+ constexpr ConstMove(int _val) : val(_val) {}
+ constexpr ConstMove(const ConstMove&& o) : val(o.val) {}
+ constexpr ConstMove(ConstMove&&) = delete;
+
+ constexpr ConstMove(std::allocator_arg_t, const test_allocator<int>&, const ConstMove&& o)
+ : val(o.val), alloc_constructed(true) {}
+};
+
+template <>
+struct std::uses_allocator<ConstMove, test_allocator<int>> : std::true_type {};
+
+template <class T>
+struct ConvertibleFrom {
+ T v;
+ bool alloc_constructed{false};
+
+ constexpr ConvertibleFrom() = default;
+ constexpr ConvertibleFrom(T& _v)
+ requires(std::is_constructible_v<T, T&>)
+ : v(_v) {}
+ constexpr ConvertibleFrom(const T& _v)
+ requires(std::is_constructible_v<T, const T&> && !std::is_const_v<T>)
+ : v(_v) {}
+ constexpr ConvertibleFrom(T&& _v)
+ requires(std::is_constructible_v<T, T &&>)
+ : v(std::move(_v)) {}
+ constexpr ConvertibleFrom(const T&& _v)
+ requires(std::is_constructible_v<T, const T &&> && !std::is_const_v<T>)
+ : v(std::move(_v)) {}
+
+ template <class U>
+ requires std::is_constructible_v<ConvertibleFrom, U&&>
+ constexpr ConvertibleFrom(std::allocator_arg_t, const test_allocator<int>&, U&& _u)
+ : ConvertibleFrom{std::forward<U>(_u)} {
+ alloc_constructed = true;
+ }
+};
+
+template <class T>
+struct std::uses_allocator<ConvertibleFrom<T>, test_allocator<int>> : std::true_type {};
+
+template <class T>
+struct ExplicitConstructibleFrom {
+ T v;
+ bool alloc_constructed{false};
+
+ constexpr explicit ExplicitConstructibleFrom() = default;
+ constexpr explicit ExplicitConstructibleFrom(T& _v)
+ requires(std::is_constructible_v<T, T&>)
+ : v(_v) {}
+ constexpr explicit ExplicitConstructibleFrom(const T& _v)
+ requires(std::is_constructible_v<T, const T&> && !std::is_const_v<T>)
+ : v(_v) {}
+ constexpr explicit ExplicitConstructibleFrom(T&& _v)
+ requires(std::is_constructible_v<T, T &&>)
+ : v(std::move(_v)) {}
+ constexpr explicit ExplicitConstructibleFrom(const T&& _v)
+ requires(std::is_constructible_v<T, const T &&> && !std::is_const_v<T>)
+ : v(std::move(_v)) {}
+
+ template <class U>
+ requires std::is_constructible_v<ExplicitConstructibleFrom, U&&>
+ constexpr ExplicitConstructibleFrom(std::allocator_arg_t, const test_allocator<int>&, U&& _u)
+ : ExplicitConstructibleFrom{std::forward<U>(_u)} {
+ alloc_constructed = true;
+ }
+};
+
+template <class T>
+struct std::uses_allocator<ExplicitConstructibleFrom<T>, test_allocator<int>> : std::true_type {};
+
+struct TracedCopyMove {
+ int nonConstCopy = 0;
+ int constCopy = 0;
+ int nonConstMove = 0;
+ int constMove = 0;
+ bool alloc_constructed = false;
+
+ constexpr TracedCopyMove() = default;
+ constexpr TracedCopyMove(const TracedCopyMove& other)
+ : nonConstCopy(other.nonConstCopy), constCopy(other.constCopy + 1), nonConstMove(other.nonConstMove),
+ constMove(other.constMove) {}
+ constexpr TracedCopyMove(TracedCopyMove& other)
+ : nonConstCopy(other.nonConstCopy + 1), constCopy(other.constCopy), nonConstMove(other.nonConstMove),
+ constMove(other.constMove) {}
+
+ constexpr TracedCopyMove(TracedCopyMove&& other)
+ : nonConstCopy(other.nonConstCopy), constCopy(other.constCopy), nonConstMove(other.nonConstMove + 1),
+ constMove(other.constMove) {}
+
+ constexpr TracedCopyMove(const TracedCopyMove&& other)
+ : nonConstCopy(other.nonConstCopy), constCopy(other.constCopy), nonConstMove(other.nonConstMove),
+ constMove(other.constMove + 1) {}
+
+ template <class U>
+ requires std::is_constructible_v<TracedCopyMove, U&&>
+ constexpr TracedCopyMove(std::allocator_arg_t, const test_allocator<int>&, U&& _u)
+ : TracedCopyMove{std::forward<U>(_u)} {
+ alloc_constructed = true;
+ }
+};
+
+template <>
+struct std::uses_allocator<TracedCopyMove, test_allocator<int>> : std::true_type {};
+
+// If the constructor tuple(tuple<UTyles...>&) is not available,
+// the fallback call to `tuple(const tuple&) = default;` or any other
+// constructor that takes const ref would increment the constCopy.
+inline constexpr bool nonConstCopyCtrCalled(const TracedCopyMove& obj) {
+ return obj.nonConstCopy == 1 && obj.constCopy == 0 && obj.constMove == 0 && obj.nonConstMove == 0;
+}
+
+// If the constructor tuple(const tuple<UTyles...>&&) is not available,
+// the fallback call to `tuple(const tuple&) = default;` or any other
+// constructor that takes const ref would increment the constCopy.
+inline constexpr bool constMoveCtrCalled(const TracedCopyMove& obj) {
+ return obj.nonConstMove == 0 && obj.constMove == 1 && obj.constCopy == 0 && obj.nonConstCopy == 0;
+}
+
+struct NoConstructorFromInt {};
+
+struct CvtFromTupleRef : TracedCopyMove {
+ constexpr CvtFromTupleRef() = default;
+ constexpr CvtFromTupleRef(std::tuple<CvtFromTupleRef>& other)
+ : TracedCopyMove(static_cast<TracedCopyMove&>(std::get<0>(other))) {}
+};
+
+struct ExplicitCtrFromTupleRef : TracedCopyMove {
+ constexpr explicit ExplicitCtrFromTupleRef() = default;
+ constexpr explicit ExplicitCtrFromTupleRef(std::tuple<ExplicitCtrFromTupleRef>& other)
+ : TracedCopyMove(static_cast<TracedCopyMove&>(std::get<0>(other))) {}
+};
+
+struct CvtFromConstTupleRefRef : TracedCopyMove {
+ constexpr CvtFromConstTupleRefRef() = default;
+ constexpr CvtFromConstTupleRefRef(const std::tuple<CvtFromConstTupleRefRef>&& other)
+ : TracedCopyMove(static_cast<const TracedCopyMove&&>(std::get<0>(other))) {}
+};
+
+struct ExplicitCtrFromConstTupleRefRef : TracedCopyMove {
+ constexpr explicit ExplicitCtrFromConstTupleRefRef() = default;
+ constexpr explicit ExplicitCtrFromConstTupleRefRef(std::tuple<const ExplicitCtrFromConstTupleRefRef>&& other)
+ : TracedCopyMove(static_cast<const TracedCopyMove&&>(std::get<0>(other))) {}
+};
+
+template <class T>
+void conversion_test(T);
+
+template <class T, class... Args>
+concept ImplicitlyConstructible = requires(Args&&... args) { conversion_test<T>({std::forward<Args>(args)...}); };
+
+#endif
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
new file mode 100644
index 0000000000000..18d41b6376e26
--- /dev/null
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.cnstr/non_const_pair.pass.cpp
@@ -0,0 +1,92 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <tuple>
+
+// template <class... Types>
+// template <class U1, class U2>
+// constexpr explicit(see below) tuple<Types...>::tuple(pair<U1, U2>& u);
+
+// Constraints:
+// - sizeof...(Types) is 2 and
+// - is_constructible_v<T0, decltype(get<0>(FWD(u)))> is true and
+// - is_constructible_v<T1, decltype(get<1>(FWD(u)))> is true.
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <cassert>
+#include <tuple>
+#include <utility>
+
+#include "convert_types.h"
+
+// test constraints
+// sizeof...(Types) == 2
+static_assert(std::is_constructible_v<std::tuple<MutableCopy, int>, std::pair<MutableCopy, int>&>);
+
+static_assert(!std::is_constructible_v<std::tuple<MutableCopy>, std::pair<MutableCopy, int>&>);
+
+static_assert(!std::is_constructible_v<std::tuple<MutableCopy, int, int>, std::pair<MutableCopy, int>&>);
+
+// test constraints
+// is_constructible_v<T0, decltype(get<0>(FWD(u)))> is true and
+// is_constructible_v<T1, decltype(get<1>(FWD(u)))> is true.
+static_assert(std::is_constructible_v<std::tuple<int, int>, std::pair<int, int>&>);
+
+static_assert(!std::is_constructible_v<std::tuple<NoConstructorFromInt, int>, std::pair<int, int>&>);
+
+static_assert(!std::is_constructible_v<std::tuple<int, NoConstructorFromInt>, std::pair<int, int>&>);
+
+static_assert(!std::is_constructible_v< std::tuple<NoConstructorFromInt, NoConstructorFromInt>, std::pair<int, int>&>);
+
+// test: The expression inside explicit is equivalent to:
+// !is_convertible_v<decltype(get<0>(FWD(u))), T0> ||
+// !is_convertible_v<decltype(get<1>(FWD(u))), T1>
+static_assert(std::is_convertible_v<std::pair<MutableCopy, MutableCopy>&,
+ std::tuple<ConvertibleFrom<MutableCopy>, ConvertibleFrom<MutableCopy>>>);
+
+static_assert(!std::is_convertible_v<std::pair<MutableCopy, MutableCopy>&,
+ std::tuple<ExplicitConstructibleFrom<MutableCopy>, ConvertibleFrom<MutableCopy>>>);
+
+static_assert(!std::is_convertible_v<std::pair<MutableCopy, MutableCopy>&,
+ std::tuple<ConvertibleFrom<MutableCopy>, ExplicitConstructibleFrom<MutableCopy>>>);
+
+constexpr bool test() {
+ // test implicit conversions.
+ {
+ std::pair<MutableCopy, int> p{1, 2};
+ std::tuple<ConvertibleFrom<MutableCopy>, ConvertibleFrom<int>> t = p;
+ assert(std::get<0>(t).v.val == 1);
+ assert(std::get<1>(t).v == 2);
+ }
+
+ // test explicit conversions.
+ {
+ std::pair<MutableCopy, int> p{1, 2};
+ std::tuple<ExplicitConstructibleFrom<MutableCopy>, ExplicitConstructibleFrom<int>> t{p};
+ assert(std::get<0>(t).v.val == 1);
+ assert(std::get<1>(t).v == 2);
+ }
+
+ // non const overload should be called
+ {
+ std::pair<TracedCopyMove, TracedCopyMove> p;
+ std::tuple<ConvertibleFrom<TracedCopyMove>, TracedCopyMove> t = p;
+ assert(nonConstCopyCtrCalled(std::get<0>(t).v));
+ assert(nonConstCopyCtrCalled(std::get<1>(t)));
+ }
+
+ return true;
+}
+
+int main() {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.special/non_member_swap_const.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.special/non_member_swap_const.pass.cpp
new file mode 100644
index 0000000000000..a342095a0e5c8
--- /dev/null
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.special/non_member_swap_const.pass.cpp
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <tuple>
+
+// template <class... Types> class tuple;
+
+// template <class... Types>
+// void swap(const tuple<Types...>& x, const tuple<Types...>& y);
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <tuple>
+#include <cassert>
+
+struct S {
+ int* calls;
+ friend constexpr void swap(S& a, S& b) {
+ *a.calls += 1;
+ *b.calls += 1;
+ }
+};
+struct CS {
+ int* calls;
+ friend constexpr void swap(const CS& a, const CS& b) {
+ *a.calls += 1;
+ *b.calls += 1;
+ }
+};
+
+static_assert(std::is_swappable_v<std::tuple<>>);
+static_assert(std::is_swappable_v<std::tuple<S>>);
+static_assert(std::is_swappable_v<std::tuple<CS>>);
+static_assert(std::is_swappable_v<std::tuple<S&>>);
+static_assert(std::is_swappable_v<std::tuple<CS, S>>);
+static_assert(std::is_swappable_v<std::tuple<CS, S&>>);
+static_assert(std::is_swappable_v<const std::tuple<>>);
+static_assert(!std::is_swappable_v<const std::tuple<S>>);
+static_assert(std::is_swappable_v<const std::tuple<CS>>);
+static_assert(std::is_swappable_v<const std::tuple<S&>>);
+static_assert(!std::is_swappable_v<const std::tuple<CS, S>>);
+static_assert(std::is_swappable_v<const std::tuple<CS, S&>>);
+
+constexpr bool test() {
+ int cs_calls = 0;
+ int s_calls = 0;
+ S s1{&s_calls};
+ S s2{&s_calls};
+ const std::tuple<CS, S&> t1 = {CS{&cs_calls}, s1};
+ const std::tuple<CS, S&> t2 = {CS{&cs_calls}, s2};
+ swap(t1, t2);
+ assert(cs_calls == 2);
+ assert(s_calls == 2);
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.swap/member_swap.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.swap/member_swap.pass.cpp
index e479a61e49f21..7c70321692321 100644
--- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.swap/member_swap.pass.cpp
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.swap/member_swap.pass.cpp
@@ -65,7 +65,7 @@ bool test()
int main(int, char**)
{
test();
-#if TEST_STD_VER >= 20
+#if TEST_STD_VER > 17
static_assert(test());
#endif
diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.swap/member_swap_const.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.swap/member_swap_const.pass.cpp
new file mode 100644
index 0000000000000..5b1c5500247cf
--- /dev/null
+++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.swap/member_swap_const.pass.cpp
@@ -0,0 +1,103 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// <tuple>
+
+// void swap(const tuple& rhs);
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+#include <cassert>
+#include <tuple>
+#include <utility>
+
+#include "test_macros.h"
+
+#ifndef TEST_HAS_NO_EXCEPTIONS
+class SwapThrower {
+ void swap(SwapThrower&) = delete;
+ void swap(const SwapThrower&) const = delete;
+};
+
+void swap(const SwapThrower&, const SwapThrower&) { throw 0.f; }
+
+static_assert(std::is_swappable_v<const SwapThrower>);
+static_assert(std::is_swappable_with_v<const SwapThrower&, const SwapThrower&>);
+
+void test_noexcept() {
+ const std::tuple<SwapThrower> t1;
+ const std::tuple<SwapThrower> t2;
+
+ try {
+ t1.swap(t2);
+ std::swap(t1, t2);
+ assert(false);
+ } catch (float) {
+ }
+
+ try {
+ std::swap(std::as_const(t1), std::as_const(t2));
+ assert(false);
+ } catch (float) {
+ }
+}
+#endif // TEST_HAS_NO_EXCEPTIONS
+
+struct ConstSwappable {
+ mutable int i;
+};
+
+constexpr void swap(const ConstSwappable& lhs, const ConstSwappable& rhs) { std::swap(lhs.i, rhs.i); }
+
+constexpr bool test() {
+ {
+ typedef std::tuple<const ConstSwappable> T;
+ const T t0(ConstSwappable{0});
+ T t1(ConstSwappable{1});
+ t0.swap(t1);
+ assert(std::get<0>(t0).i == 1);
+ assert(std::get<0>(t1).i == 0);
+ }
+ {
+ typedef std::tuple<ConstSwappable, ConstSwappable> T;
+ const T t0({0}, {1});
+ const T t1({2}, {3});
+ t0.swap(t1);
+ assert(std::get<0>(t0).i == 2);
+ assert(std::get<1>(t0).i == 3);
+ assert(std::get<0>(t1).i == 0);
+ assert(std::get<1>(t1).i == 1);
+ }
+ {
+ typedef std::tuple<ConstSwappable, const ConstSwappable, const ConstSwappable> T;
+ const T t0({0}, {1}, {2});
+ const T t1({3}, {4}, {5});
+ t0.swap(t1);
+ assert(std::get<0>(t0).i == 3);
+ assert(std::get<1>(t0).i == 4);
+ assert(std::get<2>(t0).i == 5);
+ assert(std::get<0>(t1).i == 0);
+ assert(std::get<1>(t1).i == 1);
+ assert(std::get<2>(t1).i == 2);
+ }
+ return true;
+}
+
+int main(int, char**) {
+#ifndef TEST_HAS_NO_EXCEPTIONS
+ test_noexcept();
+#endif
+ test();
+
+// gcc cannot have mutable member in constant expression
+#if !defined(TEST_COMPILER_GCC)
+ static_assert(test());
+#endif
+
+ return 0;
+}
More information about the libcxx-commits
mailing list