diff options
25 files changed, 2379 insertions, 159 deletions
diff --git a/libcxx/include/tuple b/libcxx/include/tuple index 251b685..d0c1592 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 7a0c225..8cc261c 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> @@ -1697,10 +1689,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 f04ae60..7357c22 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 d1dcb75..14b7873 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 0000000..d32bfe4 --- /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 0000000..41dfe54 --- /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 0000000..3844955 --- /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 0000000..aeb71a5 --- /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 0000000..67d16df --- /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 0000000..44dcb70 --- /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 0000000..b5593d9 --- /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 0000000..10b7e2a --- /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 0000000..a24fee7 --- /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 681e2e7..4ed4134 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 b823e8f..1423c61 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 0000000..5c82497 --- /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 0000000..86761b5 --- /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 0000000..5c5b20d --- /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 0000000..30c11b3 --- /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 0000000..bb2baba --- /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 0000000..0bdfec3 --- /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 0000000..18d41b6 --- /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 0000000..a342095 --- /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 e479a61..7c70321 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 0000000..5b1c550 --- /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; +} |