diff --git a/libcxx/include/tuple b/libcxx/include/tuple --- a/libcxx/include/tuple +++ b/libcxx/include/tuple @@ -55,8 +55,7 @@ explicit(see-below) tuple(allocator_arg_t, const Alloc& a, pair&&); tuple& operator=(const tuple&); - tuple& - operator=(tuple&&) noexcept(AND(is_nothrow_move_assignable::value ...)); + tuple& operator=(tuple&&) noexcept(AND(is_nothrow_move_assignable::value ...)); template tuple& operator=(const tuple&); template @@ -66,6 +65,11 @@ template tuple& operator=(pair&&); // iff sizeof...(T) == 2 + template + tuple& operator=(array const&) // iff sizeof...(T) == N, EXTENSION + template + tuple& operator=(array&&) // iff sizeof...(T) == N, EXTENSION + void swap(tuple&) noexcept(AND(swap(declval(), declval())...)); }; @@ -257,15 +261,6 @@ __tuple_leaf(const __tuple_leaf& __t) = default; __tuple_leaf(__tuple_leaf&& __t) = default; - template - _LIBCPP_INLINE_VISIBILITY - __tuple_leaf& - operator=(_Tp&& __t) _NOEXCEPT_((is_nothrow_assignable<_Hp&, _Tp>::value)) - { - __value_ = _VSTD::forward<_Tp>(__t); - return *this; - } - _LIBCPP_INLINE_VISIBILITY int swap(__tuple_leaf& __t) _NOEXCEPT_(__is_nothrow_swappable<__tuple_leaf>::value) { @@ -331,15 +326,6 @@ __tuple_leaf(__tuple_leaf const &) = default; __tuple_leaf(__tuple_leaf &&) = default; - template - _LIBCPP_INLINE_VISIBILITY - __tuple_leaf& - operator=(_Tp&& __t) _NOEXCEPT_((is_nothrow_assignable<_Hp&, _Tp>::value)) - { - _Hp::operator=(_VSTD::forward<_Tp>(__t)); - return *this; - } - _LIBCPP_INLINE_VISIBILITY int swap(__tuple_leaf& __t) _NOEXCEPT_(__is_nothrow_swappable<__tuple_leaf>::value) @@ -429,49 +415,30 @@ typename __make_tuple_types<_Tuple>::type>::type>(_VSTD::get<_Indx>(__t)))... {} - template - _LIBCPP_INLINE_VISIBILITY - typename enable_if - < - __tuple_assignable<_Tuple, tuple<_Tp...> >::value, - __tuple_impl& - >::type - operator=(_Tuple&& __t) _NOEXCEPT_((__all::type>::type>::value...>::value)) - { - __swallow(__tuple_leaf<_Indx, _Tp>::operator=(_VSTD::forward::type>::type>(_VSTD::get<_Indx>(__t)))...); - return *this; - } - __tuple_impl(const __tuple_impl&) = default; __tuple_impl(__tuple_impl&&) = default; - _LIBCPP_INLINE_VISIBILITY - __tuple_impl& - operator=(const __tuple_impl& __t) _NOEXCEPT_((__all::value...>::value)) - { - __swallow(__tuple_leaf<_Indx, _Tp>::operator=(static_cast&>(__t).get())...); - return *this; - } - - _LIBCPP_INLINE_VISIBILITY - __tuple_impl& - operator=(__tuple_impl&& __t) _NOEXCEPT_((__all::value...>::value)) - { - __swallow(__tuple_leaf<_Indx, _Tp>::operator=(_VSTD::forward<_Tp>(static_cast<__tuple_leaf<_Indx, _Tp>&>(__t).get()))...); - return *this; - } - _LIBCPP_INLINE_VISIBILITY void swap(__tuple_impl& __t) _NOEXCEPT_(__all<__is_nothrow_swappable<_Tp>::value...>::value) { - __swallow(__tuple_leaf<_Indx, _Tp>::swap(static_cast<__tuple_leaf<_Indx, _Tp>&>(__t))...); + _VSTD::__swallow(__tuple_leaf<_Indx, _Tp>::swap(static_cast<__tuple_leaf<_Indx, _Tp>&>(__t))...); } }; +template +_LIBCPP_INLINE_VISIBILITY +void __memberwise_copy_assign(_Dest& __dest, _Source const& __source, __tuple_indices<_Np...>) { + _VSTD::__swallow(((_VSTD::get<_Np>(__dest) = _VSTD::get<_Np>(__source)), void(), 0)...); +} +template +_LIBCPP_INLINE_VISIBILITY +void __memberwise_forward_assign(_Dest& __dest, _Source&& __source, __tuple_types<_Up...>, __tuple_indices<_Np...>) { + _VSTD::__swallow((( + _VSTD::get<_Np>(__dest) = _VSTD::forward<_Up>(_VSTD::get<_Np>(_VSTD::forward<_Source>(__source))) + ), void(), 0)...); +} template class _LIBCPP_TEMPLATE_VIS tuple @@ -916,39 +883,129 @@ tuple(allocator_arg_t, const _Alloc& __a, _Tuple&& __t) : __base_(allocator_arg_t(), __a, _VSTD::forward<_Tuple>(__t)) {} - using _CanCopyAssign = __all::value...>; - using _CanMoveAssign = __all::value...>; + // [tuple.assign] + _LIBCPP_INLINE_VISIBILITY + tuple& operator=(_If<_And...>::value, tuple, __nat> const& __tuple) + _NOEXCEPT_((_And...>::value)) + { + _VSTD::__memberwise_copy_assign(*this, __tuple, + typename __make_tuple_indices::type()); + return *this; + } + + _LIBCPP_INLINE_VISIBILITY + tuple& operator=(_If<_And...>::value, tuple, __nat>&& __tuple) + _NOEXCEPT_((_And...>::value)) + { + _VSTD::__memberwise_forward_assign(*this, _VSTD::move(__tuple), + __tuple_types<_Tp...>(), + typename __make_tuple_indices::type()); + return *this; + } + template, + is_assignable<_Tp&, _Up const&>... + >::value + ,int> = 0> _LIBCPP_INLINE_VISIBILITY - tuple& operator=(typename conditional<_CanCopyAssign::value, tuple, __nat>::type const& __t) - _NOEXCEPT_((__all::value...>::value)) + tuple& operator=(tuple<_Up...> const& __tuple) + _NOEXCEPT_((_And...>::value)) { - __base_.operator=(__t.__base_); + _VSTD::__memberwise_copy_assign(*this, __tuple, + typename __make_tuple_indices::type()); return *this; } + template, + is_assignable<_Tp&, _Up>... + >::value + ,int> = 0> _LIBCPP_INLINE_VISIBILITY - tuple& operator=(typename conditional<_CanMoveAssign::value, tuple, __nat>::type&& __t) - _NOEXCEPT_((__all::value...>::value)) + tuple& operator=(tuple<_Up...>&& __tuple) + _NOEXCEPT_((_And...>::value)) { - __base_.operator=(static_cast<_BaseT&&>(__t.__base_)); + _VSTD::__memberwise_forward_assign(*this, _VSTD::move(__tuple), + __tuple_types<_Up...>(), + typename __make_tuple_indices::type()); return *this; } - template ::value - >::type - > - _LIBCPP_INLINE_VISIBILITY - tuple& - operator=(_Tuple&& __t) _NOEXCEPT_((is_nothrow_assignable<_BaseT&, _Tuple>::value)) - { - __base_.operator=(_VSTD::forward<_Tuple>(__t)); - return *this; - } + template, + is_assignable<_FirstType<_Tp..., _Dep>&, _Up1 const&>, + is_assignable<_SecondType<_Tp..., _Dep>&, _Up2 const&> + >::value + ,int> = 0> + _LIBCPP_INLINE_VISIBILITY + tuple& operator=(pair<_Up1, _Up2> const& __pair) + _NOEXCEPT_((_And< + is_nothrow_assignable<_FirstType<_Tp...>&, _Up1 const&>, + is_nothrow_assignable<_SecondType<_Tp...>&, _Up2 const&> + >::value)) + { + _VSTD::get<0>(*this) = __pair.first; + _VSTD::get<1>(*this) = __pair.second; + return *this; + } + + template, + is_assignable<_FirstType<_Tp..., _Dep>&, _Up1>, + is_assignable<_SecondType<_Tp..., _Dep>&, _Up2> + >::value + ,int> = 0> + _LIBCPP_INLINE_VISIBILITY + tuple& operator=(pair<_Up1, _Up2>&& __pair) + _NOEXCEPT_((_And< + is_nothrow_assignable<_FirstType<_Tp...>&, _Up1>, + is_nothrow_assignable<_SecondType<_Tp...>&, _Up2> + >::value)) + { + _VSTD::get<0>(*this) = _VSTD::forward<_Up1>(__pair.first); + _VSTD::get<1>(*this) = _VSTD::forward<_Up2>(__pair.second); + return *this; + } + + // EXTENSION + template, + is_assignable<_Tp&, _Up const&>... + >::value + > > + _LIBCPP_INLINE_VISIBILITY + tuple& operator=(array<_Up, _Np> const& __array) + _NOEXCEPT_((_And...>::value)) + { + _VSTD::__memberwise_copy_assign(*this, __array, + typename __make_tuple_indices::type()); + return *this; + } + + // EXTENSION + template, + is_assignable<_Tp&, _Up>... + >::value + > > + _LIBCPP_INLINE_VISIBILITY + tuple& operator=(array<_Up, _Np>&& __array) + _NOEXCEPT_((_And...>::value)) + { + _VSTD::__memberwise_forward_assign(*this, _VSTD::move(__array), + __tuple_types<_If...>(), + typename __make_tuple_indices::type()); + return *this; + } + // [tuple.swap] _LIBCPP_INLINE_VISIBILITY void swap(tuple& __t) _NOEXCEPT_(__all<__is_nothrow_swappable<_Tp>::value...>::value) {__base_.swap(__t.__base_);} diff --git a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/const_array.pass.cpp b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/const_array.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/const_array.pass.cpp @@ -0,0 +1,57 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// + +// template class tuple; + +// EXTENSION +// template +// tuple& operator=(const array& u); + +// UNSUPPORTED: c++03 + +#include +#include +#include +#include +#include + + +template +struct NothrowAssignableFrom { + NothrowAssignableFrom& operator=(T) noexcept { return *this; } +}; + +template +struct PotentiallyThrowingAssignableFrom { + PotentiallyThrowingAssignableFrom& operator=(T) { return *this; } +}; + +int main(int, char**) { + { + std::array array = {1l, 2l, 3l}; + std::tuple tuple; + tuple = array; + assert(std::get<0>(tuple) == 1); + assert(std::get<1>(tuple) == 2); + assert(std::get<2>(tuple) == 3); + } + { + typedef std::tuple> Tuple; + typedef std::array Array; + static_assert(std::is_nothrow_assignable::value, ""); + } + { + typedef std::tuple> Tuple; + typedef std::array Array; + static_assert(!std::is_nothrow_assignable::value, ""); + } + + return 0; +} diff --git a/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/rvalue_array.pass.cpp b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/rvalue_array.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/rvalue_array.pass.cpp @@ -0,0 +1,57 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// + +// template class tuple; + +// EXTENSION +// template +// tuple& operator=(array&& u); + +// UNSUPPORTED: c++03 + +#include +#include +#include +#include +#include + + +template +struct NothrowAssignableFrom { + NothrowAssignableFrom& operator=(T) noexcept { return *this; } +}; + +template +struct PotentiallyThrowingAssignableFrom { + PotentiallyThrowingAssignableFrom& operator=(T) { return *this; } +}; + +int main(int, char**) { + { + std::array array = {1l, 2l, 3l}; + std::tuple tuple; + tuple = std::move(array); + assert(std::get<0>(tuple) == 1); + assert(std::get<1>(tuple) == 2); + assert(std::get<2>(tuple) == 3); + } + { + typedef std::tuple> Tuple; + typedef std::array Array; + static_assert(std::is_nothrow_assignable::value, ""); + } + { + typedef std::tuple> Tuple; + typedef std::array Array; + static_assert(!std::is_nothrow_assignable::value, ""); + } + + return 0; +} diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/tuple_array_template_depth.pass.cpp b/libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/tuple_array_template_depth.pass.cpp rename from libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/tuple_array_template_depth.pass.cpp rename to libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/tuple_array_template_depth.pass.cpp diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair.pass.cpp --- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair.pass.cpp +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair.pass.cpp @@ -15,9 +15,18 @@ // UNSUPPORTED: c++03 +#include #include +#include #include -#include + + +struct NothrowCopyAssignable { + NothrowCopyAssignable& operator=(NothrowCopyAssignable const&) noexcept { return *this; } +}; +struct PotentiallyThrowingCopyAssignable { + PotentiallyThrowingCopyAssignable& operator=(PotentiallyThrowingCopyAssignable const&) { return *this; } +}; #include "test_macros.h" @@ -32,6 +41,16 @@ assert(std::get<0>(t1) == 2); assert(std::get<1>(t1) == short('a')); } + { + typedef std::tuple Tuple; + typedef std::pair Pair; + static_assert(std::is_nothrow_assignable::value, ""); + } + { + typedef std::tuple Tuple; + typedef std::pair Pair; + static_assert(!std::is_nothrow_assignable::value, ""); + } - return 0; + return 0; } diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_copy.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_copy.pass.cpp --- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_copy.pass.cpp +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_copy.pass.cpp @@ -34,6 +34,16 @@ explicit D(int i = 0) : B(i) {} }; +struct NothrowCopyAssignable +{ + NothrowCopyAssignable& operator=(NothrowCopyAssignable const&) noexcept { return *this; } +}; + +struct PotentiallyThrowingCopyAssignable +{ + PotentiallyThrowingCopyAssignable& operator=(PotentiallyThrowingCopyAssignable const&) { return *this; } +}; + int main(int, char**) { { @@ -87,6 +97,16 @@ assert(std::get<0>(t) == 43); assert(&std::get<0>(t) == &x); } + { + typedef std::tuple T0; + typedef std::tuple T1; + static_assert(std::is_nothrow_assignable::value, ""); + } + { + typedef std::tuple T0; + typedef std::tuple T1; + static_assert(!std::is_nothrow_assignable::value, ""); + } - return 0; + return 0; } diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_move.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_move.pass.cpp --- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_move.pass.cpp +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_move.pass.cpp @@ -45,6 +45,16 @@ } }; +struct NothrowMoveAssignable +{ + NothrowMoveAssignable& operator=(NothrowMoveAssignable&&) noexcept { return *this; } +}; + +struct PotentiallyThrowingMoveAssignable +{ + PotentiallyThrowingMoveAssignable& operator=(PotentiallyThrowingMoveAssignable&&) { return *this; } +}; + int main(int, char**) { { @@ -108,6 +118,16 @@ assert(std::get<0>(t) == 43); assert(&std::get<0>(t) == &x); } + { + typedef std::tuple T0; + typedef std::tuple T1; + static_assert(std::is_nothrow_assignable::value, ""); + } + { + typedef std::tuple T0; + typedef std::tuple T1; + static_assert(!std::is_nothrow_assignable::value, ""); + } - return 0; + return 0; } diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/copy.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/copy.pass.cpp --- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/copy.pass.cpp +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/copy.pass.cpp @@ -34,6 +34,12 @@ MoveAssignable& operator=(MoveAssignable const&) = delete; MoveAssignable& operator=(MoveAssignable&&) = default; }; +struct NothrowCopyAssignable { + NothrowCopyAssignable& operator=(NothrowCopyAssignable const&) noexcept { return *this; } +}; +struct PotentiallyThrowingCopyAssignable { + PotentiallyThrowingCopyAssignable& operator=(PotentiallyThrowingCopyAssignable const&) { return *this; } +}; int main(int, char**) { @@ -100,6 +106,14 @@ using T = std::tuple; static_assert(!std::is_copy_assignable::value, ""); } + { + using T = std::tuple; + static_assert(std::is_nothrow_copy_assignable::value, ""); + } + { + using T = std::tuple; + static_assert(!std::is_nothrow_copy_assignable::value, ""); + } - return 0; + return 0; } diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/derived_from_tuple_like.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/derived_from_tuple_like.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/derived_from_tuple_like.pass.cpp @@ -0,0 +1,120 @@ +//===----------------------------------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +// + +// template class tuple; + +// template +// tuple& operator=(const tuple& u); + +// UNSUPPORTED: c++03 + +#include +#include +#include +#include +#include + +#include "propagate_value_category.hpp" + +struct TracksIntQuals { + TracksIntQuals() : value(-1), value_category(VC_None), assigned(false) {} + + template ::type, TracksIntQuals>::value>::type> + TracksIntQuals(Tp &&x) + : value(x), value_category(getValueCategory()), assigned(false) { + static_assert(std::is_same, int>::value, ""); + } + + template ::type, TracksIntQuals>::value>::type> + TracksIntQuals &operator=(Tp &&x) { + static_assert(std::is_same, int>::value, ""); + value = x; + value_category = getValueCategory(); + assigned = true; + return *this; + } + + void reset() { + value = -1; + value_category = VC_None; + assigned = false; + } + + bool checkConstruct(int expect, ValueCategory expect_vc) const { + return value != 1 && value == expect && value_category == expect_vc && + assigned == false; + } + + bool checkAssign(int expect, ValueCategory expect_vc) const { + return value != 1 && value == expect && value_category == expect_vc && + assigned == true; + } + + int value; + ValueCategory value_category; + bool assigned; +}; + +template +struct DerivedFromTup : Tup { + using Tup::Tup; +}; + +template +void do_derived_assign_test() { + using Tup1 = std::tuple; + Tup1 t; + auto reset = [&]() { + std::get<0>(t) = -1; + std::get<1>(t).reset(); + }; + { + DerivedFromTup> d; + std::get<0>(d) = 42; + std::get<1>(d) = 101; + + t = ValueCategoryCast(d); + assert(std::get<0>(t) == 42); + assert(std::get<1>(t).checkAssign(101, VC)); + } + reset(); + { + DerivedFromTup> d; + std::get<0>(d) = 42; + std::get<1>(d) = 101; + + t = ValueCategoryCast(d); + assert(std::get<0>(t) == 42); + assert(std::get<1>(t).checkAssign(101, VC)); + } + reset(); + { +#ifdef _LIBCPP_VERSION // assignment from std::array is a libc++ extension + DerivedFromTup> d; + std::get<0>(d) = 42; + std::get<1>(d) = 101; + + t = ValueCategoryCast(d); + assert(std::get<0>(t) == 42); + assert(std::get<1>(t).checkAssign(101, VC)); +#endif + } +} + +int main(int, char**) { + do_derived_assign_test(); + do_derived_assign_test(); + + return 0; +} diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/laziness.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/laziness.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/laziness.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 +// +//===----------------------------------------------------------------------===// + +// UNSUPPORTED: c++03 + +// This test ensures that std::tuple is lazy when it comes to checking whether +// the elements it is assigned from can be used to assign to the types in +// the tuple. + +#include +#include + +template +constexpr typename std::enable_if::type BlowUp() { + static_assert(Enable && sizeof...(Class) != sizeof...(Class), ""); + return true; +} + +template +struct Fail { + static_assert(sizeof(T) != sizeof(T), ""); + using type = void; +}; + +struct NoAssign { + NoAssign() = default; + NoAssign(NoAssign const&) = default; + template ::type> + NoAssign& operator=(T) { return *this; } +}; + +template +struct DieOnAssign { + DieOnAssign() = default; + template ::value>::type, + class = typename Fail::type> + DieOnAssign& operator=(T) { + return *this; + } +}; + +void test_arity_checks() { + { + using T = std::tuple, int>; + using P = std::pair; + static_assert(!std::is_assignable::value, ""); + } + { + using T = std::tuple >; + using A = std::array; + static_assert(!std::is_assignable::value, ""); + } +} + +void test_assignability_checks() { + { + using T1 = std::tuple >; + using T2 = std::tuple; + static_assert(!std::is_assignable::value, ""); + } + { + using T1 = std::tuple >; + using T2 = std::pair; + static_assert(!std::is_assignable::value, ""); + } +} + +int main(int, char**) { + test_arity_checks(); + test_assignability_checks(); + return 0; +} diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move.pass.cpp --- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move.pass.cpp +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move.pass.cpp @@ -35,7 +35,12 @@ MoveAssignable& operator=(MoveAssignable const&) = delete; MoveAssignable& operator=(MoveAssignable&&) = default; }; - +struct NothrowMoveAssignable { + NothrowMoveAssignable& operator=(NothrowMoveAssignable&&) noexcept { return *this; } +}; +struct PotentiallyThrowingMoveAssignable { + PotentiallyThrowingMoveAssignable& operator=(PotentiallyThrowingMoveAssignable&&) { return *this; } +}; struct CountAssign { static int copied; @@ -48,7 +53,6 @@ int CountAssign::copied = 0; int CountAssign::moved = 0; - int main(int, char**) { { @@ -102,15 +106,6 @@ using T = std::tuple>; static_assert(std::is_move_assignable::value, ""); static_assert(!std::is_copy_assignable::value, ""); - - } - { - using T = std::tuple; - static_assert(!std::is_move_assignable::value, ""); - } - { - using T = std::tuple; - static_assert(std::is_move_assignable::value, ""); } { // The move should decay to a copy. @@ -123,6 +118,22 @@ assert(CountAssign::copied == 1); assert(CountAssign::moved == 0); } + { + using T = std::tuple; + static_assert(!std::is_move_assignable::value, ""); + } + { + using T = std::tuple; + static_assert(std::is_move_assignable::value, ""); + } + { + using T = std::tuple; + static_assert(std::is_nothrow_move_assignable::value, ""); + } + { + using T = std::tuple; + static_assert(!std::is_nothrow_move_assignable::value, ""); + } - return 0; + return 0; } diff --git a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move_pair.pass.cpp b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move_pair.pass.cpp --- a/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move_pair.pass.cpp +++ b/libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move_pair.pass.cpp @@ -37,6 +37,16 @@ explicit D(int i) : B(i) {} }; +struct NothrowMoveAssignable +{ + NothrowMoveAssignable& operator=(NothrowMoveAssignable&&) noexcept { return *this; } +}; + +struct PotentiallyThrowingMoveAssignable +{ + PotentiallyThrowingMoveAssignable& operator=(PotentiallyThrowingMoveAssignable&&) { return *this; } +}; + int main(int, char**) { { @@ -48,6 +58,16 @@ assert(std::get<0>(t1) == 2); assert(std::get<1>(t1)->id_ == 3); } + { + typedef std::tuple Tuple; + typedef std::pair Pair; + static_assert(std::is_nothrow_assignable::value, ""); + } + { + typedef std::tuple Tuple; + typedef std::pair Pair; + static_assert(!std::is_nothrow_assignable::value, ""); + } - return 0; + return 0; } diff --git a/libcxx/test/support/propagate_value_category.hpp b/libcxx/test/support/propagate_value_category.hpp new file mode 100644 --- /dev/null +++ b/libcxx/test/support/propagate_value_category.hpp @@ -0,0 +1,153 @@ +//===----------------------------------------------------------------------===// +// +// 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 TEST_SUPPORT_PROPAGATE_VALUE_CATEGORY +#define TEST_SUPPORT_PROPAGATE_VALUE_CATEGORY + +#include "test_macros.h" +#include + +#if TEST_STD_VER < 11 +#error this header may only be used in C++11 +#endif + +using UnderlyingVCType = unsigned; +enum ValueCategory : UnderlyingVCType { + VC_None = 0, + VC_LVal = 1 << 0, + VC_RVal = 1 << 1, + VC_Const = 1 << 2, + VC_Volatile = 1 << 3, + VC_ConstVolatile = VC_Const | VC_Volatile +}; + +inline constexpr ValueCategory operator&(ValueCategory LHS, ValueCategory RHS) { + return ValueCategory(LHS & (UnderlyingVCType)RHS); +} + +inline constexpr ValueCategory operator|(ValueCategory LHS, ValueCategory RHS) { + return ValueCategory(LHS | (UnderlyingVCType)RHS); +} + +inline constexpr ValueCategory operator^(ValueCategory LHS, ValueCategory RHS) { + return ValueCategory(LHS ^ (UnderlyingVCType)RHS); +} + +inline constexpr bool isValidValueCategory(ValueCategory VC) { + return (VC & (VC_LVal | VC_RVal)) != (VC_LVal | VC_RVal); +} + +inline constexpr bool hasValueCategory(ValueCategory Arg, ValueCategory Key) { + return Arg == Key || ((Arg & Key) == Key); +} + +template +using UnCVRef = + typename std::remove_cv::type>::type; + +template +constexpr ValueCategory getReferenceQuals() { + return std::is_lvalue_reference::value + ? VC_LVal + : (std::is_rvalue_reference::value ? VC_RVal : VC_None); +} +static_assert(getReferenceQuals() == VC_None, ""); +static_assert(getReferenceQuals() == VC_LVal, ""); +static_assert(getReferenceQuals() == VC_RVal, ""); + +template +constexpr ValueCategory getCVQuals() { + using Vp = typename std::remove_reference::type; + return std::is_const::value && std::is_volatile::value + ? VC_ConstVolatile + : (std::is_const::value + ? VC_Const + : (std::is_volatile::value ? VC_Volatile : VC_None)); +} +static_assert(getCVQuals() == VC_None, ""); +static_assert(getCVQuals() == VC_Const, ""); +static_assert(getCVQuals() == VC_Volatile, ""); +static_assert(getCVQuals() == VC_ConstVolatile, ""); +static_assert(getCVQuals() == VC_None, ""); +static_assert(getCVQuals() == VC_Const, ""); + +template +inline constexpr ValueCategory getValueCategory() { + return getReferenceQuals() | getCVQuals(); +} +static_assert(getValueCategory() == VC_None, ""); +static_assert(getValueCategory() == (VC_LVal | VC_Const), ""); +static_assert(getValueCategory() == + (VC_RVal | VC_ConstVolatile), + ""); + +template +struct ApplyValueCategory { +private: + static_assert(isValidValueCategory(VC), ""); + + template + using CondT = typename std::conditional::type; + +public: + template > + using ApplyCVQuals = CondT< + hasValueCategory(VC, VC_ConstVolatile), typename std::add_cv::type, + CondT::type, + CondT::type, Tp>>>; + + template ::type> + using ApplyReferenceQuals = + CondT::type, + CondT::type, Vp>>; + + template + using Apply = ApplyReferenceQuals>>; + + template ::type = true> + static Apply> cast(Tp &&t) { + using ToType = Apply>; + return static_cast(t); + } + + template ::type = true> + static Apply> cast(Tp &&t) { + using ToType = Apply>; + return static_cast(std::move(t)); + } + + template < + class Tp, bool Dummy = true, + typename std::enable_if::type = true> + static Apply> cast(Tp &&t) { + return t; + } +}; + +template +using ApplyValueCategoryT = typename ApplyValueCategory::template Apply; + +template +using PropagateValueCategory = ApplyValueCategory()>; + +template +using PropagateValueCategoryT = + typename ApplyValueCategory()>::template Apply; + +template +typename ApplyValueCategory::template Apply ValueCategoryCast(Tp &&t) { + return ApplyValueCategory::cast(std::forward(t)); +}; + +#endif // TEST_SUPPORT_PROPAGATE_VALUE_CATEGORY