Index: libcxx/include/tuple =================================================================== --- libcxx/include/tuple +++ libcxx/include/tuple @@ -246,15 +246,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) { @@ -320,15 +311,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) @@ -421,40 +403,9 @@ 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) @@ -463,11 +414,32 @@ } }; +template +_LIBCPP_INLINE_VISIBILITY +void __memberwise_copy_assign(_Dest& __dest, _Source const& __source, __tuple_indices<_Np...>) { + __swallow(((_VSTD::get<_Np>(__dest) = _VSTD::get<_Np>(__source)), void(), int())...); +} + +template +_LIBCPP_INLINE_VISIBILITY +void __memberwise_forward_assign(_Dest& __dest, _Source&& __source, __tuple_types<_Up...>, __tuple_indices<_Np...>) { + __swallow((( + _VSTD::get<_Np>(__dest) = _VSTD::forward<_Up>(_VSTD::get<_Np>(_VSTD::forward<_Source>(__source))) + ), void(), int())...); +} + +template +struct __pack_first; +template +struct __pack_first<_Dummy, _Tp1, _Tp...> { typedef _Tp1 type; }; +template +struct __pack_second; +template +struct __pack_second<_Dummy, _Tp1, _Tp2, _Tp...> { typedef _Tp2 type; }; template -class _LIBCPP_TEMPLATE_VIS tuple -{ +class _LIBCPP_TEMPLATE_VIS tuple { typedef __tuple_impl::type, _Tp...> _BaseT; _BaseT __base_; @@ -864,39 +836,113 @@ 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=(typename conditional<_CanCopyAssign::value, tuple, __nat>::type const& __t) + tuple& operator=(typename conditional<__all::value...>::value, tuple, __nat>::type const& __tuple) _NOEXCEPT_((__all::value...>::value)) { - __base_.operator=(__t.__base_); + __memberwise_copy_assign(*this, __tuple, + typename __make_tuple_indices::type()); return *this; } _LIBCPP_INLINE_VISIBILITY - tuple& operator=(typename conditional<_CanMoveAssign::value, tuple, __nat>::type&& __t) + tuple& operator=(typename conditional<__all::value...>::value, tuple, __nat>::type&& __tuple) _NOEXCEPT_((__all::value...>::value)) { - __base_.operator=(static_cast<_BaseT&&>(__t.__base_)); + __memberwise_forward_assign(*this, _VSTD::move(__tuple), + __tuple_types<_Tp...>(), + 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::value...>::value + >::type> + _LIBCPP_INLINE_VISIBILITY + tuple& operator=(tuple<_Up...> const& __tuple) + _NOEXCEPT_((__all::value...>::value)) + { + __memberwise_copy_assign(*this, __tuple, + typename __make_tuple_indices::type()); + return *this; + } + + template::value...>::value + >::type> + _LIBCPP_INLINE_VISIBILITY + tuple& operator=(tuple<_Up...>&& __tuple) + _NOEXCEPT_((__all::value...>::value)) + { + __memberwise_forward_assign(*this, _VSTD::move(__tuple), + __tuple_types<_Up...>(), + typename __make_tuple_indices::type()); + return *this; + } + + template::type&, _Up1 const&>::value && + is_assignable::type&, _Up2 const&>::value + >::type> + _LIBCPP_INLINE_VISIBILITY + tuple& operator=(pair<_Up1, _Up2> const& __pair) + _NOEXCEPT_((is_nothrow_assignable::type&, _Up1 const&>::value && + is_nothrow_assignable::type&, _Up2 const&>::value)) + { + _VSTD::get<0>(*this) = __pair.first; + _VSTD::get<1>(*this) = __pair.second; + return *this; + } + + template::type&, _Up1&&>::value && + is_assignable::type&, _Up2&&>::value + >::type> + _LIBCPP_INLINE_VISIBILITY + tuple& operator=(pair<_Up1, _Up2>&& __pair) + _NOEXCEPT_((is_nothrow_assignable::type&, _Up1&&>::value && + is_nothrow_assignable::type&, _Up2&&>::value)) + { + _VSTD::get<0>(*this) = _VSTD::forward<_Up1>(__pair.first); + _VSTD::get<1>(*this) = _VSTD::forward<_Up2>(__pair.second); + return *this; + } + + // EXTENSION + template::value...>::value + >::type> + _LIBCPP_INLINE_VISIBILITY + tuple& operator=(array<_Up, _Np> const& __array) + _NOEXCEPT_((__all::value...>::value)) + { + __memberwise_copy_assign(*this, __array, + typename __make_tuple_indices::type()); + return *this; + } + + // EXTENSION + template::value...>::value + >::type> + _LIBCPP_INLINE_VISIBILITY + tuple& operator=(array<_Up, _Np>&& __array) + _NOEXCEPT_((__all::value...>::value)) + { + __memberwise_forward_assign(*this, _VSTD::move(__array), + __tuple_types::type...>(), + 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_);} Index: libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/const_array.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/const_array.pass.cpp @@ -0,0 +1,56 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// + +// template class tuple; + +// EXTENSION +// template +// tuple& operator=(const array& u); + +// UNSUPPORTED: c++98, 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() { + { + 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, ""); + } +} Index: libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/rvalue_array.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/libcxx/utilities/tuple/tuple.tuple/tuple.assign/rvalue_array.pass.cpp @@ -0,0 +1,56 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// + +// template class tuple; + +// EXTENSION +// template +// tuple& operator=(array&& u); + +// UNSUPPORTED: c++98, 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() { + { + 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, ""); + } +} Index: libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair.pass.cpp =================================================================== --- libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair.pass.cpp +++ libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/const_pair.pass.cpp @@ -16,9 +16,18 @@ // UNSUPPORTED: c++98, c++03 +#include #include +#include #include -#include + + +struct NothrowCopyAssignable { + NothrowCopyAssignable& operator=(NothrowCopyAssignable const&) noexcept { return *this; } +}; +struct PotentiallyThrowingCopyAssignable { + PotentiallyThrowingCopyAssignable& operator=(PotentiallyThrowingCopyAssignable const&) { return *this; } +}; int main() { @@ -31,4 +40,14 @@ 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, ""); + } } Index: libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_copy.pass.cpp =================================================================== --- libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_copy.pass.cpp +++ libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_copy.pass.cpp @@ -33,6 +33,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() { { @@ -86,4 +96,14 @@ 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, ""); + } } Index: libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_move.pass.cpp =================================================================== --- libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_move.pass.cpp +++ libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/convert_move.pass.cpp @@ -44,6 +44,16 @@ } }; +struct NothrowMoveAssignable +{ + NothrowMoveAssignable& operator=(NothrowMoveAssignable&&) noexcept { return *this; } +}; + +struct PotentiallyThrowingMoveAssignable +{ + PotentiallyThrowingMoveAssignable& operator=(PotentiallyThrowingMoveAssignable&&) { return *this; } +}; + int main() { { @@ -107,4 +117,14 @@ 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, ""); + } } Index: libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/copy.pass.cpp =================================================================== --- libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/copy.pass.cpp +++ libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/copy.pass.cpp @@ -35,6 +35,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() { @@ -101,4 +107,12 @@ 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, ""); + } } Index: libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/derived_from_tuple_like.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/derived_from_tuple_like.pass.cpp @@ -0,0 +1,119 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// + +// template class tuple; + +// template +// tuple& operator=(const tuple& u); + +// UNSUPPORTED: c++98, 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() { + do_derived_assign_test(); + do_derived_assign_test(); +} Index: libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move.pass.cpp =================================================================== --- libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move.pass.cpp +++ libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move.pass.cpp @@ -34,7 +34,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; @@ -47,7 +52,6 @@ int CountAssign::copied = 0; int CountAssign::moved = 0; - int main() { { @@ -101,15 +105,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. @@ -122,4 +117,20 @@ 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, ""); + } } Index: libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move_pair.pass.cpp =================================================================== --- libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move_pair.pass.cpp +++ libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/move_pair.pass.cpp @@ -36,6 +36,16 @@ explicit D(int i) : B(i) {} }; +struct NothrowMoveAssignable +{ + NothrowMoveAssignable& operator=(NothrowMoveAssignable&&) noexcept { return *this; } +}; + +struct PotentiallyThrowingMoveAssignable +{ + PotentiallyThrowingMoveAssignable& operator=(PotentiallyThrowingMoveAssignable&&) { return *this; } +}; + int main() { { @@ -47,4 +57,14 @@ 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, ""); + } } Index: libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/tuple_array_template_depth.pass.cpp =================================================================== --- /dev/null +++ libcxx/test/std/utilities/tuple/tuple.tuple/tuple.assign/tuple_array_template_depth.pass.cpp @@ -1,34 +0,0 @@ -//===----------------------------------------------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is dual licensed under the MIT and the University of Illinois Open -// Source Licenses. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -// UNSUPPORTED: c++98, c++03 - -// - -// template class tuple; - -// template > -// tuple & operator=(Tuple &&); - -// This test checks that we do not evaluate __make_tuple_types -// on the array when it doesn't match the size of the tuple. - -#include -#include - -// Use 1256 to try and blow the template instantiation depth for all compilers. -typedef std::array array_t; -typedef std::tuple tuple_t; - -int main() -{ - array_t arr; - tuple_t tup; - tup = arr; -} Index: libcxx/test/support/propagate_value_category.hpp =================================================================== --- /dev/null +++ libcxx/test/support/propagate_value_category.hpp @@ -0,0 +1,154 @@ +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#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