From ee2aad99ee0e5a0b863ddb2fcdc7eaf21f930633 Mon Sep 17 00:00:00 2001 From: Michael Schellenberger Costa Date: Thu, 29 Oct 2020 10:57:18 +0100 Subject: [PATCH] [xutility] Modernize _Ptr_meow_cat to use variable templates --- stl/inc/algorithm | 24 +- stl/inc/memory | 47 +-- stl/inc/vector | 8 +- stl/inc/xmemory | 30 +- stl/inc/xutility | 211 +++++++------ .../VSO_0180469_ptr_cat/test.compile.pass.cpp | 294 +++++++++--------- 6 files changed, 328 insertions(+), 286 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 75a4b7df91..a07cbe24e8 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -719,8 +719,8 @@ namespace ranges { template _NODISCARD static constexpr bool _Equal_count( _It1 _First1, _It2 _First2, _Size _Count, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { - if constexpr (_Equal_memcmp_is_safe<_It1, _It2, - _Pr> && same_as<_Pj1, identity> && same_as<_Pj2, identity>) { + if constexpr (_Memcmp_in_equal_is_safe<_It1, _It2, _Pr> // + && same_as<_Pj1, identity> && same_as<_Pj2, identity>) { if (!_STD is_constant_evaluated()) { return _Memcmp_count(_First1, _First2, static_cast(_Count)) == 0; } @@ -1347,10 +1347,10 @@ namespace ranges { requires indirectly_copyable<_It, _Out> constexpr copy_n_result<_It, _Out> operator()(_It _First, iter_difference_t<_It> _Count, _Out _Result) const { auto _UFirst = _Get_unwrapped_n(_STD move(_First), _Count); - if constexpr (_Ptr_copy_cat::_Trivially_copyable) { + if constexpr (_Memmove_in_copy_is_safe) { if (!_STD is_constant_evaluated()) { auto _Final = _UFirst + _Count; - _Result = _Copy_memmove(_STD move(_UFirst), _Final, _STD move(_Result)); + _Result = _Memmove_forward(_STD move(_UFirst), _Final, _STD move(_Result)); _Seek_wrapped(_First, _STD move(_Final)); return {_STD move(_First), _STD move(_Result)}; } @@ -1501,10 +1501,10 @@ namespace ranges { requires indirectly_movable<_It, _Out> constexpr move_result<_It, _Out> _Move_unchecked(_It _First, _Se _Last, _Out _Result) { // clang-format on - if constexpr (_Ptr_move_cat<_It, _Out>::_Trivially_copyable) { + if constexpr (_Memmove_in_move_is_safe<_It, _Out>) { if (!_STD is_constant_evaluated()) { auto _Final = _RANGES next(_First, _STD move(_Last)); - _Result = _Copy_memmove(_STD move(_First), _Final, _STD move(_Result)); + _Result = _Memmove_forward(_STD move(_First), _Final, _STD move(_Result)); return {_STD move(_Final), _STD move(_Result)}; } } @@ -1559,9 +1559,9 @@ namespace ranges { template requires indirectly_movable<_It1, _It2> constexpr _It2 _Move_backward_common(const _It1 _First, _It1 _Last, _It2 _Result) { - if constexpr (_Ptr_move_cat<_It1, _It2>::_Trivially_copyable) { + if constexpr (_Memmove_in_move_is_safe<_It1, _It2>) { if (!_STD is_constant_evaluated()) { - return _Copy_backward_memmove(_First, _Last, _Result); + return _Memmove_backward(_First, _Last, _Result); } } @@ -1925,7 +1925,7 @@ namespace ranges { template _NODISCARD _CONSTEXPR20 bool _Equal_rev_pred_unchecked(_InIt1 _First1, _InIt2 _First2, const _InIt2 _Last2, _Pr _Pred) { // compare [_First1, ...) to [_First2, _Last2) - if constexpr (_Equal_memcmp_is_safe<_InIt1, _InIt2, _Pr>) { + if constexpr (_Memcmp_in_equal_is_safe<_InIt1, _InIt2, _Pr>) { #ifdef __cpp_lib_is_constant_evaluated if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated @@ -2022,7 +2022,7 @@ namespace ranges { // clang-format off template concept _Equal_rev_pred_can_memcmp = is_same_v<_Pj1, identity> && is_same_v<_Pj2, identity> - && sized_sentinel_for<_Se2, _It2> && _Equal_memcmp_is_safe<_It1, _It2, _Pr>; + && sized_sentinel_for<_Se2, _It2> && _Memcmp_in_equal_is_safe<_It1, _It2, _Pr>; template _Se2, class _Pr, class _Pj1, class _Pj2> requires indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2> @@ -3399,12 +3399,12 @@ namespace ranges { const auto _ULast = _Get_unwrapped(_STD move(_Last)); if (!_STD is_constant_evaluated()) { if constexpr (sized_sentinel_for) { - if constexpr (_Fill_memset_is_safe) { + if constexpr (_Memset_in_fill_is_safe) { const auto _Distance = static_cast(_ULast - _UFirst); _Fill_memset(_UFirst, _Value, _Distance); _Seek_wrapped(_First, _UFirst + _Distance); return _First; - } else if constexpr (_Fill_zero_memset_is_safe) { + } else if constexpr (_Memset_in_fill_zero_is_safe) { if (_Is_all_bits_zero(_Value)) { const auto _Distance = static_cast(_ULast - _UFirst); _Fill_zero_memset(_UFirst, _Distance); diff --git a/stl/inc/memory b/stl/inc/memory index 9eca0290ce..740e31864a 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -78,9 +78,9 @@ namespace ranges { _STL_INTERNAL_STATIC_ASSERT(_No_throw_sentinel_for<_OSe, _Out>); _STL_INTERNAL_STATIC_ASSERT(constructible_from, iter_reference_t<_It>>); - if constexpr (_Ptr_copy_cat<_It, _Out>::_Really_trivial + if constexpr (_Memmove_in_uninitialized_copy_is_safe<_It, _Out> // && sized_sentinel_for<_Se, _It> && sized_sentinel_for<_OSe, _Out>) { - return _Copy_memcpy_common(_IFirst, _RANGES next(_IFirst, _STD move(_ILast)), _OFirst, + return _Memmove_forward_common(_IFirst, _RANGES next(_IFirst, _STD move(_ILast)), _OFirst, _RANGES next(_OFirst, _STD move(_OLast))); } else { _Uninitialized_backout _Backout{_STD move(_OFirst)}; @@ -109,8 +109,8 @@ _NoThrowFwdIt uninitialized_copy_n(const _InIt _First, const _Diff _Count_raw, _ auto _UFirst = _Get_unwrapped_n(_First, _Count); auto _UDest = _Get_unwrapped_n(_Dest, _Count); - if constexpr (_Ptr_copy_cat::_Really_trivial) { - _UDest = _Copy_memmove(_UFirst, _UFirst + _Count, _UDest); + if constexpr (_Memmove_in_uninitialized_copy_is_safe) { + _UDest = _Memmove_forward(_UFirst, _UFirst + _Count, _UDest); } else { _Uninitialized_backout _Backout{_UDest}; @@ -150,8 +150,8 @@ namespace ranges { auto _IFirst = _Get_unwrapped_n(_STD move(_First1), _Count); auto _OFirst = _Get_unwrapped(_STD move(_First2)); const auto _OLast = _Get_unwrapped(_STD move(_Last2)); - if constexpr (_Ptr_copy_cat<_It, _Out>::_Really_trivial) { - auto _UResult = _Copy_memcpy_common(_IFirst, _IFirst + _Count, _OFirst, _OLast); + if constexpr (_Memmove_in_uninitialized_copy_is_safe<_It, _Out>) { + auto _UResult = _Memmove_forward_common(_IFirst, _IFirst + _Count, _OFirst, _OLast); _IFirst = _UResult.in; _OFirst = _UResult.out; } else { @@ -241,8 +241,8 @@ pair<_InIt, _NoThrowFwdIt> uninitialized_move_n(_InIt _First, const _Diff _Count auto _UFirst = _Get_unwrapped_n(_First, _Count); auto _UDest = _Get_unwrapped_n(_Dest, _Count); - if constexpr (_Ptr_move_cat::_Really_trivial) { - _UDest = _Copy_memmove(_UFirst, _UFirst + _Count, _UDest); + if constexpr (_Memmove_in_uninitialized_move_is_safe) { + _UDest = _Memmove_forward(_UFirst, _UFirst + _Count, _UDest); _UFirst += _Count; } else { _Uninitialized_backout _Backout{_UDest}; @@ -286,8 +286,8 @@ namespace ranges { auto _IFirst = _Get_unwrapped_n(_STD move(_First1), _Count); auto _OFirst = _Get_unwrapped(_STD move(_First2)); const auto _OLast = _Get_unwrapped(_STD move(_Last2)); - if constexpr (_Ptr_move_cat<_It, _Out>::_Really_trivial) { - auto _UResult = _Copy_memcpy_common(_IFirst, _IFirst + _Count, _OFirst, _OLast); + if constexpr (_Memmove_in_uninitialized_move_is_safe<_It, _Out>) { + auto _UResult = _Memmove_forward_common(_IFirst, _IFirst + _Count, _OFirst, _OLast); _IFirst = _UResult.in; _OFirst = _UResult.out; } else { @@ -341,12 +341,12 @@ namespace ranges { _STL_INTERNAL_STATIC_ASSERT(_No_throw_sentinel_for<_Se, _It>); _STL_INTERNAL_STATIC_ASSERT(constructible_from, const _Ty&>); - if constexpr (_Fill_memset_is_safe<_It, _Ty>) { + if constexpr (_Memset_in_fill_is_safe<_It, _Ty>) { const auto _OFinal = _RANGES next(_OFirst, _STD move(_OLast)); _Fill_memset(_OFirst, _Val, static_cast(_OFinal - _OFirst)); return _OFinal; } else { - if constexpr (_Fill_zero_memset_is_safe<_It, _Ty>) { + if constexpr (_Memset_in_fill_zero_is_safe<_It, _Ty>) { if (_Is_all_bits_zero(_Val)) { const auto _OFinal = _RANGES next(_OFirst, _STD move(_OLast)); _Fill_zero_memset(_OFirst, static_cast(_OFinal - _OFirst)); @@ -379,11 +379,11 @@ _NoThrowFwdIt uninitialized_fill_n(_NoThrowFwdIt _First, const _Diff _Count_raw, } auto _UFirst = _Get_unwrapped_n(_First, _Count); - if constexpr (_Fill_memset_is_safe) { + if constexpr (_Memset_in_fill_is_safe) { _Fill_memset(_UFirst, _Val, static_cast(_Count)); _UFirst += _Count; } else { - if constexpr (_Fill_zero_memset_is_safe) { + if constexpr (_Memset_in_fill_zero_is_safe) { if (_Is_all_bits_zero(_Val)) { _Fill_zero_memset(_UFirst, static_cast(_Count)); _Seek_wrapped(_First, _UFirst + _Count); @@ -421,11 +421,11 @@ namespace ranges { } auto _UFirst = _Get_unwrapped_n(_STD move(_First), _Count); - if constexpr (_Fill_memset_is_safe) { + if constexpr (_Memset_in_fill_is_safe<_It, _Ty>) { _Fill_memset(_UFirst, _Val, static_cast(_Count)); _Seek_wrapped(_First, _UFirst + _Count); } else { - if constexpr (_Fill_zero_memset_is_safe) { + if constexpr (_Memset_in_fill_zero_is_safe) { if (_Is_all_bits_zero(_Val)) { _Fill_zero_memset(_UFirst, static_cast(_Count)); _Seek_wrapped(_First, _UFirst + _Count); @@ -2183,7 +2183,7 @@ struct _NODISCARD _Reverse_destroy_multidimensional_n_guard { template void _Uninitialized_copy_multidimensional(const _Ty (&_In)[_Size], _Ty (&_Out)[_Size]) { if constexpr (is_trivial_v<_Ty>) { - _Copy_memmove(_In, _In + _Size, _Out); + _Memmove_forward(_In, _In + _Size, _Out); } else if constexpr (is_array_v<_Ty>) { _Reverse_destroy_multidimensional_n_guard<_Ty> _Guard{_Out, 0}; for (size_t& _Idx = _Guard._Index; _Idx < _Size; ++_Idx) { @@ -2246,10 +2246,10 @@ void _Uninitialized_fill_multidimensional_n(_Ty* const _Out, const size_t _Size, _Uninitialized_copy_multidimensional(_Val, _Out[_Idx]); // intentionally copy, not fill } _Guard._Target = nullptr; - } else if constexpr (_Fill_memset_is_safe<_Ty*, _Ty>) { + } else if constexpr (_Memset_in_fill_is_safe<_Ty*, _Ty>) { _Fill_memset(_Out, _Val, _Size); } else { - if constexpr (_Fill_zero_memset_is_safe<_Ty*, _Ty>) { + if constexpr (_Memset_in_fill_zero_is_safe<_Ty*, _Ty>) { if (_Is_all_bits_zero(_Val)) { _Fill_zero_memset(_Out, _Size); return; @@ -2552,7 +2552,7 @@ template void _Uninitialized_copy_multidimensional_al(const _Ty (&_In)[_Size], _Ty (&_Out)[_Size], _Alloc& _Al) { using _Item = remove_all_extents_t<_Ty>; if constexpr (conjunction_v, _Uses_default_construct<_Alloc, _Item*, const _Item&>>) { - _Copy_memmove(_In, _In + _Size, _Out); + _Memmove_forward(_In, _In + _Size, _Out); } else if constexpr (is_array_v<_Ty>) { _Reverse_destroy_multidimensional_n_al_guard<_Ty, _Alloc> _Guard{_Out, 0, _Al}; for (size_t& _Idx = _Guard._Index; _Idx < _Size; ++_Idx) { @@ -2596,11 +2596,12 @@ void _Uninitialized_fill_multidimensional_n_al(_Ty* const _Out, const size_t _Si _Uninitialized_copy_multidimensional_al(_Val, _Out[_Idx], _Al); // intentionally copy, not fill } _Guard._Target = nullptr; - } else if constexpr (_Fill_memset_is_safe<_Ty*, _Ty> && _Uses_default_construct<_Alloc, _Ty*, const _Ty&>::value) { + } else if constexpr (_Memset_in_fill_is_safe<_Ty*, _Ty> // + && _Uses_default_construct<_Alloc, _Ty*, const _Ty&>::value) { _Fill_memset(_Out, _Val, _Size); } else { - if constexpr (_Fill_zero_memset_is_safe<_Ty*, - _Ty> && _Uses_default_construct<_Alloc, _Ty*, const _Ty&>::value) { + if constexpr (_Memset_in_fill_zero_is_safe<_Ty*, _Ty> // + && _Uses_default_construct<_Alloc, _Ty*, const _Ty&>::value) { if (_Is_all_bits_zero(_Val)) { _Fill_zero_memset(_Out, _Size); return; diff --git a/stl/inc/vector b/stl/inc/vector index 52fef59fc2..98b11b42cf 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -669,13 +669,13 @@ private: _My_data._Orphan_all(); const auto _Oldcapacity = static_cast(_My_data._Myend - _Myfirst); - if constexpr (conjunction_v::_Trivially_copyable>, + if constexpr (conjunction_v>, _Uses_default_construct<_Alty, _Ty*, _Ty>, _Uses_default_destroy<_Alty, _Ty*>>) { if (_Newsize > _Oldcapacity) { _Clear_and_reserve_geometric(_Newsize); } - _Mylast = _Refancy(_Copy_memmove(_Unfancy(_First), _Unfancy(_Last), _Unfancy(_Myfirst))); + _Mylast = _Refancy(_Memmove_forward(_Unfancy(_First), _Unfancy(_Last), _Unfancy(_Myfirst))); } else { auto _Oldsize = static_cast(_Mylast - _Myfirst); @@ -1145,7 +1145,7 @@ private: _My_data._Orphan_all(); - if constexpr (conjunction_v::_Trivially_copyable>, + if constexpr (conjunction_v>, _Uses_default_construct<_Alty, _Ty*, decltype(*_First)>, _Uses_default_destroy<_Alty, _Ty*>>) { #ifdef __cpp_lib_constexpr_dynamic_alloc @@ -1157,7 +1157,7 @@ private: _Clear_and_reserve_geometric(_Newsize); } - _Mylast = _Refancy(_Copy_memmove(_First, _Last, _Unfancy(_Myfirst))); + _Mylast = _Refancy(_Memmove_forward(_First, _Last, _Unfancy(_Myfirst))); return; } } diff --git a/stl/inc/xmemory b/stl/inc/xmemory index b4bb3212ee..e45a1c9c77 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -1486,12 +1486,12 @@ template _CONSTEXPR20_DYNALLOC _NoThrowFwdIt _Uninitialized_move_unchecked( _InIt _First, const _InIt _Last, _NoThrowFwdIt _Dest) { // move [_First, _Last) to raw [_Dest, ...) - if constexpr (_Ptr_move_cat<_InIt, _NoThrowFwdIt>::_Really_trivial) { + if constexpr (_Memmove_in_uninitialized_move_is_safe<_InIt, _NoThrowFwdIt>) { #ifdef __cpp_lib_constexpr_dynamic_alloc if (!_STD is_constant_evaluated()) #endif // __cpp_lib_constexpr_dynamic_alloc { - return _Copy_memmove(_First, _Last, _Dest); + return _Memmove_forward(_First, _Last, _Dest); } } _Uninitialized_backout<_NoThrowFwdIt> _Backout{_Dest}; @@ -1534,7 +1534,7 @@ namespace ranges { // clang-format on template - in_out_result<_InIt, _OutIt> _Copy_memcpy_common( + in_out_result<_InIt, _OutIt> _Memmove_forward_common( _InIt _IFirst, _InIt _ILast, _OutIt _OFirst, _OutIt _OLast) noexcept { const auto _IFirstPtr = _To_address(_IFirst); const auto _ILastPtr = _To_address(_ILast); @@ -1572,9 +1572,9 @@ namespace ranges { uninitialized_move_result<_It, _Out> _Uninitialized_move_unchecked( _It _IFirst, _Se _ILast, _Out _OFirst, _OSe _OLast) { // clang-format on - if constexpr (_Ptr_move_cat<_It, _Out>::_Really_trivial + if constexpr (_Memmove_in_uninitialized_move_is_safe<_It, _Out> // && sized_sentinel_for<_Se, _It> && sized_sentinel_for<_OSe, _Out>) { - return _Copy_memcpy_common( + return _Memmove_forward_common( _IFirst, _RANGES next(_IFirst, _STD move(_ILast)), _OFirst, _RANGES next(_OFirst, _STD move(_OLast))); } else { _Uninitialized_backout _Backout{_STD move(_OFirst)}; @@ -1634,13 +1634,13 @@ _CONSTEXPR20_DYNALLOC _Alloc_ptr_t<_Alloc> _Uninitialized_copy( auto _UFirst = _Get_unwrapped(_First); const auto _ULast = _Get_unwrapped(_Last); - if constexpr (conjunction_v::_Really_trivial>, + if constexpr (conjunction_v>, _Uses_default_construct<_Alloc, _Ptrval, decltype(*_UFirst)>>) { #ifdef __cpp_lib_constexpr_dynamic_alloc if (!_STD is_constant_evaluated()) #endif // __cpp_lib_constexpr_dynamic_alloc { - _Copy_memmove(_UFirst, _ULast, _Unfancy(_Dest)); + _Memmove_forward(_UFirst, _ULast, _Unfancy(_Dest)); _Dest += _ULast - _UFirst; return _Dest; } @@ -1659,12 +1659,12 @@ template _CONSTEXPR20_DYNALLOC _NoThrowFwdIt _Uninitialized_copy_unchecked( _InIt _First, const _InIt _Last, _NoThrowFwdIt _Dest) { // copy [_First, _Last) to raw [_Dest, ...) - if constexpr (_Ptr_copy_cat<_InIt, _NoThrowFwdIt>::_Really_trivial) { + if constexpr (_Memmove_in_uninitialized_copy_is_safe<_InIt, _NoThrowFwdIt>) { #ifdef __cpp_lib_constexpr_dynamic_alloc if (!_STD is_constant_evaluated()) #endif // __cpp_lib_constexpr_dynamic_alloc { - return _Copy_memmove(_First, _Last, _Dest); + return _Memmove_forward(_First, _Last, _Dest); } } @@ -1696,13 +1696,13 @@ _CONSTEXPR20_DYNALLOC _Alloc_ptr_t<_Alloc> _Uninitialized_move( using _Ptrval = typename _Alloc::value_type*; auto _UFirst = _Get_unwrapped(_First); const auto _ULast = _Get_unwrapped(_Last); - if constexpr (conjunction_v::_Really_trivial>, + if constexpr (conjunction_v>, _Uses_default_construct<_Alloc, _Ptrval, decltype(_STD move(*_UFirst))>>) { #ifdef __cpp_lib_constexpr_dynamic_alloc if (!_STD is_constant_evaluated()) #endif // __cpp_lib_constexpr_dynamic_alloc { - _Copy_memmove(_UFirst, _ULast, _Unfancy(_Dest)); + _Memmove_forward(_UFirst, _ULast, _Unfancy(_Dest)); return _Dest + (_ULast - _UFirst); } } @@ -1721,7 +1721,7 @@ _CONSTEXPR20_DYNALLOC _Alloc_ptr_t<_Alloc> _Uninitialized_fill_n( _Alloc_ptr_t<_Alloc> _First, _Alloc_size_t<_Alloc> _Count, const typename _Alloc::value_type& _Val, _Alloc& _Al) { // copy _Count copies of _Val to raw _First, using _Al using _Ty = typename _Alloc::value_type; - if constexpr (_Fill_memset_is_safe<_Ty*, _Ty> && _Uses_default_construct<_Alloc, _Ty*, _Ty>::value) { + if constexpr (_Memset_in_fill_is_safe<_Ty*, _Ty> && _Uses_default_construct<_Alloc, _Ty*, _Ty>::value) { #ifdef __cpp_lib_constexpr_dynamic_alloc if (!_STD is_constant_evaluated()) #endif // __cpp_lib_constexpr_dynamic_alloc @@ -1729,7 +1729,7 @@ _CONSTEXPR20_DYNALLOC _Alloc_ptr_t<_Alloc> _Uninitialized_fill_n( _Fill_memset(_Unfancy(_First), _Val, static_cast(_Count)); return _First + _Count; } - } else if constexpr (_Fill_zero_memset_is_safe<_Ty*, _Ty> && _Uses_default_construct<_Alloc, _Ty*, _Ty>::value) { + } else if constexpr (_Memset_in_fill_zero_is_safe<_Ty*, _Ty> && _Uses_default_construct<_Alloc, _Ty*, _Ty>::value) { #ifdef __cpp_lib_constexpr_dynamic_alloc if (!_STD is_constant_evaluated()) #endif // __cpp_lib_constexpr_dynamic_alloc @@ -1756,10 +1756,10 @@ void uninitialized_fill(const _NoThrowFwdIt _First, const _NoThrowFwdIt _Last, c _Adl_verify_range(_First, _Last); auto _UFirst = _Get_unwrapped(_First); const auto _ULast = _Get_unwrapped(_Last); - if constexpr (_Fill_memset_is_safe<_Unwrapped_t, _Tval>) { + if constexpr (_Memset_in_fill_is_safe<_Unwrapped_t, _Tval>) { _Fill_memset(_UFirst, _Val, static_cast(_ULast - _UFirst)); } else { - if constexpr (_Fill_zero_memset_is_safe<_Unwrapped_t, _Tval>) { + if constexpr (_Memset_in_fill_zero_is_safe<_Unwrapped_t, _Tval>) { if (_Is_all_bits_zero(_Val)) { _Fill_zero_memset(_UFirst, static_cast(_ULast - _UFirst)); return; diff --git a/stl/inc/xutility b/stl/inc/xutility index 0b923cfd23..7a84d0f4f5 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -4083,74 +4083,103 @@ template _INLINE_VAR constexpr bool _Iterators_are_contiguous = _Iterator_is_contiguous<_Iter1>&& _Iterator_is_contiguous<_Iter2>; -// FUNCTION TEMPLATE copy -template -struct _Ptr_cat_helper { - using _USource = _Unwrap_enum_t<_Source>; - using _UDest = _Unwrap_enum_t<_Dest>; - static constexpr bool _Really_trivial = conjunction_v< - bool_constant == is_same_v>, - is_integral<_USource>, is_integral<_UDest>>; - static constexpr bool _Trivially_copyable = _Really_trivial; +// VARIABLE TEMPLATE _Can_memop +enum class _Memop_cat { + _Copy, + _Copy_uninitialized, + _Move, + _Move_uninitialized, }; -template -struct _Ptr_cat_helper<_Elem, _Elem> { // determines _Ptr_cat's result when the types are the same - static constexpr bool _Really_trivial = is_trivial_v<_Elem>; - static constexpr bool _Trivially_copyable = is_trivially_copyable_v<_Elem>; -}; +// Integral types are eligible for memcpy in very specific cases. +// * Enumerations are treated as their underlying integral type +// * They must be the same size. (`int == long` is eligible; `int != long long` isn't.) +// * They are either both bool or both non bool +template +struct _Same_size_integral_or_bool + : bool_constant == is_same_v> {}; -template -struct _Ptr_cat_helper<_Anything*, const _Anything*> { - // determines _Ptr_cat's result when all we do is add const to a pointer - static constexpr bool _Really_trivial = true; - static constexpr bool _Trivially_copyable = true; -}; +template +_INLINE_VAR constexpr bool _Can_memop_integrals = + conjunction_v, is_integral<_Dest>, _Same_size_integral_or_bool<_Source, _Dest>>; -template -struct _Ptr_cat_helper<_Anything*, volatile _Anything*> { - // determines _Ptr_cat's result when all we do is add volatile to a pointer - static constexpr bool _Really_trivial = true; - static constexpr bool _Trivially_copyable = true; -}; +// Matching integer type are eligible for memcpy +template , _Unwrap_enum_t<_Dest>>> +_INLINE_VAR constexpr bool _Can_memop_elements = _Result; -template -struct _Ptr_cat_helper<_Anything*, const volatile _Anything*> { - // determines _Ptr_cat's result when all we do is add cv to a pointer - static constexpr bool _Really_trivial = true; - static constexpr bool _Trivially_copyable = true; -}; +// Pointer elements are eligible for memcpy when they point to the same type, ignoring cv-qualification. +// This handles pointers to object types, pointers to void, and pointers to function types. +// Performance note: This doesn't attempt to handle `object* != void*`. +template +_INLINE_VAR constexpr bool _Can_memop_elements<_Source*, _Dest*, _Cat, false> = + is_same_v, remove_cv_t<_Dest>>; -struct _False_copy_cat { - static constexpr bool _Really_trivial = false; - static constexpr bool _Trivially_copyable = false; -}; +// Types are eligible for memcpy / memmove when they are trivially copyable. +template +_INLINE_VAR constexpr bool _Can_memop_elements<_Ty, enable_if_t, _Ty>, _Memop_cat::_Copy, false> = + is_trivially_copyable_v<_Ty>; -// NOTE: pointer is not a contiguous iterator if it points to volatile type -template > -struct _Ptr_move_cat : _False_copy_cat {}; +template +_INLINE_VAR constexpr bool _Can_memop_elements<_Ty, enable_if_t, _Ty>, _Memop_cat::_Move, false> = + is_trivially_copyable_v<_Ty>; -template -struct _Ptr_move_cat<_Source, _Dest, false> - : conditional_t, remove_reference_t<_Iter_ref_t<_Source>>>, - _Ptr_cat_helper<_Iter_value_t<_Source>, _Iter_value_t<_Dest>>, _False_copy_cat> {}; +// Types are eligible for memcpy / memmove into uninitialized memory when they are trivial. +template +_INLINE_VAR constexpr bool + _Can_memop_elements<_Ty, enable_if_t, _Ty>, _Memop_cat::_Copy_uninitialized, false> = + is_trivial_v<_Ty>; -template -struct _Ptr_move_cat, _Dest, true> : _Ptr_move_cat<_Source, _Dest> {}; +template +_INLINE_VAR constexpr bool + _Can_memop_elements<_Ty, enable_if_t, _Ty>, _Memop_cat::_Move_uninitialized, false> = + is_trivial_v<_Ty>; -template > -struct _Ptr_copy_cat : _False_copy_cat {}; +// Different types are eligible for memcpy when they are trivially copy assignable. +template +_INLINE_VAR constexpr bool _Can_assign_elements = is_trivially_assignable_v<_Dest&, _Source&>; +// Different types are eligible for memmove when they are trivially move assignable. template -struct _Ptr_copy_cat<_Source, _Dest, false> - : conditional_t, _Iter_ref_t<_Source>>, - _Ptr_cat_helper<_Iter_value_t<_Source>, _Iter_value_t<_Dest>>, _False_copy_cat> {}; +_INLINE_VAR constexpr bool _Can_assign_elements<_Source, _Dest, _Memop_cat::_Move> = + is_trivially_assignable_v<_Dest&, _Source&&>; template -struct _Ptr_copy_cat, _Dest, true> : _Ptr_move_cat<_Source, _Dest> {}; +_INLINE_VAR constexpr bool _Can_assign_elements<_Source, _Dest, _Memop_cat::_Move_uninitialized> = + is_trivially_assignable_v<_Dest&, _Source&&>; + +// _Can_memop<_IterSource, _IterDest> reports whether we can activate the memory operation for +// arbitrary iterators. It ignores top-level constness on the source iterators and on the source elements. +template +_INLINE_VAR constexpr bool _Can_memop = false; + +template +_INLINE_VAR constexpr bool _Can_memop<_Source*, _Dest*, _Cat> = + conjunction_v>, + bool_constant<_Can_memop_elements, remove_volatile_t<_Dest>, _Cat>>>; + +template +_INLINE_VAR constexpr bool _Can_memop, _IterDest, _Cat> = + _Can_memop<_IterSource, _IterDest, _Cat>; + +template +_INLINE_VAR constexpr bool _Memmove_in_copy_is_safe = + _Can_memop, _IterDest, _Memop_cat::_Copy>; + +template +_INLINE_VAR constexpr bool _Memmove_in_uninitialized_copy_is_safe = + _Can_memop, _IterDest, _Memop_cat::_Copy_uninitialized>; + +template +_INLINE_VAR constexpr bool _Memmove_in_move_is_safe = + _Can_memop, _IterDest, _Memop_cat::_Move>; + +template +_INLINE_VAR constexpr bool _Memmove_in_uninitialized_move_is_safe = + _Can_memop, _IterDest, _Memop_cat::_Move_uninitialized>; template -_OutCtgIt _Copy_memmove(_CtgIt _First, _CtgIt _Last, _OutCtgIt _Dest) { +_OutCtgIt _Memmove_forward(_CtgIt _First, _CtgIt _Last, _OutCtgIt _Dest) { auto _FirstPtr = _To_address(_First); auto _LastPtr = _To_address(_Last); auto _DestPtr = _To_address(_Dest); @@ -4166,25 +4195,26 @@ _OutCtgIt _Copy_memmove(_CtgIt _First, _CtgIt _Last, _OutCtgIt _Dest) { } } -template -_OutIt _Copy_memmove(move_iterator<_InIt> _First, move_iterator<_InIt> _Last, _OutIt _Dest) { - return _Copy_memmove(_First.base(), _Last.base(), _Dest); +template +_OutCtgIt _Memmove_forward(move_iterator<_CtgIt> _First, move_iterator<_CtgIt> _Last, _OutCtgIt _Dest) { + return _Memmove_forward(_First.base(), _Last.base(), _Dest); } // VARIABLE TEMPLATE _Is_vb_iterator template _INLINE_VAR constexpr bool _Is_vb_iterator = false; +// FUNCTION TEMPLATE copy template _CONSTEXPR20 _OutIt _Copy_unchecked(_InIt _First, _InIt _Last, _OutIt _Dest) { // copy [_First, _Last) to [_Dest, ...) // note: _Copy_unchecked has callers other than the copy family - if constexpr (_Ptr_copy_cat<_InIt, _OutIt>::_Trivially_copyable) { + if constexpr (_Memmove_in_copy_is_safe<_InIt, _OutIt>) { #ifdef __cpp_lib_is_constant_evaluated if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated { - return _Copy_memmove(_First, _Last, _Dest); + return _Memmove_forward(_First, _Last, _Dest); } } @@ -4249,10 +4279,10 @@ namespace ranges { requires indirectly_copyable<_It, _Out> _NODISCARD constexpr copy_result<_It, _Out> _Copy_unchecked(_It _First, _Se _Last, _Out _Result) { // clang-format on - if constexpr (_Ptr_copy_cat<_It, _Out>::_Trivially_copyable && sized_sentinel_for<_Se, _It>) { + if constexpr (_Memmove_in_copy_is_safe<_It, _Out> && sized_sentinel_for<_Se, _It>) { if (!_STD is_constant_evaluated()) { auto _Final = _RANGES next(_First, _STD move(_Last)); - _Result = _Copy_memmove(_STD move(_First), _Final, _STD move(_Result)); + _Result = _Memmove_forward(_STD move(_First), _Final, _STD move(_Result)); return {_STD move(_Final), _STD move(_Result)}; } } @@ -4303,12 +4333,12 @@ _CONSTEXPR20 _OutIt copy_n(_InIt _First, _Diff _Count_raw, _OutIt _Dest) { if (0 < _Count) { auto _UFirst = _Get_unwrapped_n(_First, _Count); auto _UDest = _Get_unwrapped_n(_Dest, _Count); - if constexpr (_Ptr_copy_cat::_Trivially_copyable) { + if constexpr (_Memmove_in_copy_is_safe) { #ifdef __cpp_lib_is_constant_evaluated if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated { - _UDest = _Copy_memmove(_UFirst, _UFirst + _Count, _UDest); + _UDest = _Memmove_forward(_UFirst, _UFirst + _Count, _UDest); _Seek_wrapped(_Dest, _UDest); return _Dest; } @@ -4344,8 +4374,8 @@ _FwdIt2 copy_n(_ExPo&&, _FwdIt1 _First, _Diff _Count_raw, _FwdIt2 _Dest) noexcep #endif // _HAS_CXX17 // FUNCTION TEMPLATE copy_backward -template -_CtgIt2 _Copy_backward_memmove(_CtgIt1 _First, _CtgIt1 _Last, _CtgIt2 _Dest) { +template +_OutCtgIt _Memmove_backward(_CtgIt _First, _CtgIt _Last, _OutCtgIt _Dest) { // implement copy_backward-like function as memmove auto _FirstPtr = _To_address(_First); auto _LastPtr = _To_address(_Last); @@ -4355,27 +4385,27 @@ _CtgIt2 _Copy_backward_memmove(_CtgIt1 _First, _CtgIt1 _Last, _CtgIt2 _Dest) { char* const _Dest_ch = const_cast(reinterpret_cast(_DestPtr)); const auto _Count = static_cast(_Last_ch - _First_ch); auto _Result = _CSTD memmove(_Dest_ch - _Count, _First_ch, _Count); - if constexpr (is_pointer_v<_CtgIt2>) { - return static_cast<_CtgIt2>(_Result); + if constexpr (is_pointer_v<_OutCtgIt>) { + return static_cast<_OutCtgIt>(_Result); } else { return _Dest - (_LastPtr - _FirstPtr); } } -template -_BidIt2 _Copy_backward_memmove(move_iterator<_BidIt1> _First, move_iterator<_BidIt1> _Last, _BidIt2 _Dest) { - return _Copy_backward_memmove(_First.base(), _Last.base(), _Dest); +template +_OutCtgIt _Memmove_backward(move_iterator<_CtgIt> _First, move_iterator<_CtgIt> _Last, _OutCtgIt _Dest) { + return _Memmove_backward(_First.base(), _Last.base(), _Dest); } template _NODISCARD _CONSTEXPR20 _BidIt2 _Copy_backward_unchecked(_BidIt1 _First, _BidIt1 _Last, _BidIt2 _Dest) { // copy [_First, _Last) backwards to [..., _Dest) - if constexpr (_Ptr_copy_cat<_BidIt1, _BidIt2>::_Trivially_copyable) { + if constexpr (_Memmove_in_copy_is_safe<_BidIt1, _BidIt2>) { #ifdef __cpp_lib_is_constant_evaluated if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated { - return _Copy_backward_memmove(_First, _Last, _Dest); + return _Memmove_backward(_First, _Last, _Dest); } } @@ -4411,12 +4441,12 @@ template _CONSTEXPR20 _OutIt _Move_unchecked(_InIt _First, _InIt _Last, _OutIt _Dest) { // move [_First, _Last) to [_Dest, ...) // note: _Move_unchecked has callers other than the move family - if constexpr (_Ptr_move_cat<_InIt, _OutIt>::_Trivially_copyable) { + if constexpr (_Memmove_in_move_is_safe<_InIt, _OutIt>) { #ifdef __cpp_lib_is_constant_evaluated if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated { - return _Copy_memmove(_First, _Last, _Dest); + return _Memmove_forward(_First, _Last, _Dest); } } @@ -4454,12 +4484,12 @@ template _CONSTEXPR20 _BidIt2 _Move_backward_unchecked(_BidIt1 _First, _BidIt1 _Last, _BidIt2 _Dest) { // move [_First, _Last) backwards to [..., _Dest) // note: _Move_backward_unchecked has callers other than the move_backward family - if constexpr (_Ptr_move_cat<_BidIt1, _BidIt2>::_Trivially_copyable) { + if constexpr (_Memmove_in_move_is_safe<_BidIt1, _BidIt2>) { #ifdef __cpp_lib_is_constant_evaluated if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated { - return _Copy_backward_memmove(_First, _Last, _Dest); + return _Memmove_backward(_First, _Last, _Dest); } } @@ -4491,7 +4521,6 @@ _BidIt2 move_backward(_ExPo&&, _BidIt1 _First, _BidIt1 _Last, _BidIt2 _Dest) noe #endif // _HAS_CXX17 // FUNCTION TEMPLATE fill - template struct _Is_character : false_type {}; // by default, not a character type @@ -4523,23 +4552,23 @@ template <> struct _Is_character_or_byte_or_bool : true_type {}; #endif // __cpp_lib_byte -// _Fill_memset_is_safe determines if _FwdIt and _Ty are eligible for memset optimization in fill. +// _Memset_in_fill_is_safe determines if _FwdIt and _Ty are eligible for memset optimization in fill. // Need to explicitly test for volatile because _Unwrap_enum_t discards qualifiers. template > -_INLINE_VAR constexpr bool _Fill_memset_is_safe = conjunction_v, +_INLINE_VAR constexpr bool _Memset_in_fill_is_safe = conjunction_v, _Is_character_or_byte_or_bool<_Unwrap_enum_t>>>, negation>>>, is_assignable<_Iter_ref_t<_FwdIt>, const _Ty&>>; template -_INLINE_VAR constexpr bool _Fill_memset_is_safe<_FwdIt, _Ty, false> = false; +_INLINE_VAR constexpr bool _Memset_in_fill_is_safe<_FwdIt, _Ty, false> = false; template > -_INLINE_VAR constexpr bool _Fill_zero_memset_is_safe = +_INLINE_VAR constexpr bool _Memset_in_fill_zero_is_safe = conjunction_v, is_scalar<_Iter_value_t<_FwdIt>>, negation>>, negation>>>, is_assignable<_Iter_ref_t<_FwdIt>, const _Ty&>>; template -_INLINE_VAR constexpr bool _Fill_zero_memset_is_safe<_FwdIt, _Ty, false> = false; +_INLINE_VAR constexpr bool _Memset_in_fill_zero_is_safe<_FwdIt, _Ty, false> = false; template void _Fill_memset(_CtgIt _Dest, const _Ty _Val, const size_t _Count) { @@ -4574,10 +4603,10 @@ _CONSTEXPR20 void fill(const _FwdIt _First, const _FwdIt _Last, const _Ty& _Val) if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated { - if constexpr (_Fill_memset_is_safe) { + if constexpr (_Memset_in_fill_is_safe) { _Fill_memset(_UFirst, _Val, static_cast(_ULast - _UFirst)); return; - } else if constexpr (_Fill_zero_memset_is_safe) { + } else if constexpr (_Memset_in_fill_zero_is_safe) { if (_Is_all_bits_zero(_Val)) { _Fill_zero_memset(_UFirst, static_cast(_ULast - _UFirst)); return; @@ -4617,11 +4646,11 @@ _CONSTEXPR20 _OutIt fill_n(_OutIt _Dest, const _Diff _Count_raw, const _Ty& _Val if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated { - if constexpr (_Fill_memset_is_safe) { + if constexpr (_Memset_in_fill_is_safe) { _Fill_memset(_UDest, _Val, static_cast(_Count)); _Seek_wrapped(_Dest, _UDest + _Count); return _Dest; - } else if constexpr (_Fill_zero_memset_is_safe) { + } else if constexpr (_Memset_in_fill_zero_is_safe) { if (_Is_all_bits_zero(_Val)) { _Fill_zero_memset(_UDest, static_cast(_Count)); _Seek_wrapped(_Dest, _UDest + _Count); @@ -4662,11 +4691,11 @@ namespace ranges { if (_Count > 0) { auto _UFirst = _Get_unwrapped_n(_STD move(_First), _Count); if (!_STD is_constant_evaluated()) { - if constexpr (_Fill_memset_is_safe) { + if constexpr (_Memset_in_fill_is_safe) { _Fill_memset(_UFirst, _Value, static_cast(_Count)); _Seek_wrapped(_First, _UFirst + _Count); // no need to move since _UFirst is a pointer return _First; - } else if constexpr (_Fill_zero_memset_is_safe) { + } else if constexpr (_Memset_in_fill_zero_is_safe) { if (_Is_all_bits_zero(_Value)) { _Fill_zero_memset(_UFirst, static_cast(_Count)); _Seek_wrapped(_First, _UFirst + _Count); // no need to move since _UFirst is a pointer @@ -4756,17 +4785,17 @@ template _INLINE_VAR constexpr bool _Can_memcmp_elements_with_pred = _Can_memcmp_elements<_Elem1, _Elem2> // && _Pred_is_consistent_with_memcmp<_Elem1, _Elem2, _Pr>; -// _Equal_memcmp_is_safe<_Iter1, _Iter2, _Pr> reports whether we can activate the memcmp optimization +// _Memcmp_in_equal_is_safe<_Iter1, _Iter2, _Pr> reports whether we can activate the memcmp optimization // for arbitrary iterators and predicates. // It ignores top-level constness on the iterators and on the elements. template -_INLINE_VAR constexpr bool _Equal_memcmp_is_safe_helper = _Iterators_are_contiguous<_Iter1, _Iter2> // +_INLINE_VAR constexpr bool _Memcmp_in_equal_is_safe_helper = _Iterators_are_contiguous<_Iter1, _Iter2> // && _Can_memcmp_elements_with_pred>>, remove_const_t>>, _Pr>; template -_INLINE_VAR constexpr bool _Equal_memcmp_is_safe = - _Equal_memcmp_is_safe_helper, remove_const_t<_Iter2>, _Pr>; +_INLINE_VAR constexpr bool _Memcmp_in_equal_is_safe = + _Memcmp_in_equal_is_safe_helper, remove_const_t<_Iter2>, _Pr>; template _NODISCARD int _Memcmp_ranges(_CtgIt1 _First1, _CtgIt1 _Last1, _CtgIt2 _First2) { @@ -4792,7 +4821,7 @@ _NODISCARD _CONSTEXPR20 bool equal(const _InIt1 _First1, const _InIt1 _Last1, co auto _UFirst1 = _Get_unwrapped(_First1); const auto _ULast1 = _Get_unwrapped(_Last1); auto _UFirst2 = _Get_unwrapped_n(_First2, _Idl_distance<_InIt1>(_UFirst1, _ULast1)); - if constexpr (_Equal_memcmp_is_safe) { + if constexpr (_Memcmp_in_equal_is_safe) { #ifdef __cpp_lib_is_constant_evaluated if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated diff --git a/tests/std/tests/VSO_0180469_ptr_cat/test.compile.pass.cpp b/tests/std/tests/VSO_0180469_ptr_cat/test.compile.pass.cpp index 0f8716cf6b..674d35cf1f 100644 --- a/tests/std/tests/VSO_0180469_ptr_cat/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0180469_ptr_cat/test.compile.pass.cpp @@ -31,13 +31,13 @@ void assert_same() { template struct test_ptr_cat_helper { - static constexpr bool CopyReallyTrivial = _Ptr_copy_cat::_Really_trivial; - static constexpr bool CopyTriviallyCopyable = _Ptr_copy_cat::_Trivially_copyable; + static constexpr bool CopyReallyTrivial = _Memmove_in_uninitialized_copy_is_safe; + static constexpr bool CopyTriviallyCopyable = _Memmove_in_copy_is_safe; STATIC_ASSERT(Expected == CopyReallyTrivial + CopyTriviallyCopyable); STATIC_ASSERT(!CopyReallyTrivial || CopyTriviallyCopyable); - static constexpr bool MoveReallyTrivial = _Ptr_move_cat::_Really_trivial; - static constexpr bool MoveTriviallyCopyable = _Ptr_move_cat::_Trivially_copyable; + static constexpr bool MoveReallyTrivial = _Memmove_in_uninitialized_move_is_safe; + static constexpr bool MoveTriviallyCopyable = _Memmove_in_move_is_safe; STATIC_ASSERT(Expected == MoveReallyTrivial + MoveTriviallyCopyable); STATIC_ASSERT(!MoveReallyTrivial || MoveTriviallyCopyable); }; @@ -176,6 +176,17 @@ void ptr_cat_test_cases() { test_ptr_cat<2, volatile int*, const volatile int*>(); test_ptr_cat<2, const volatile int*, const volatile int*>(); + // Pointer to pointer should work + test_ptr_cat<2, int**, int**>(); + test_ptr_cat<2, pod_struct**, pod_struct**>(); + test_ptr_cat<2, trivially_copyable_struct**, trivially_copyable_struct**>(); + test_ptr_cat<2, custom_copy_struct**, custom_copy_struct**>(); + + // Pointer to pointer of different types should not work + test_ptr_cat<0, int**, pod_struct**>(); + test_ptr_cat<0, pod_struct**, trivially_copyable_struct**>(); + test_ptr_cat<0, trivially_copyable_struct**, custom_copy_struct**>(); + // Pointers to derived are implicitly convertible to pointers to base, but there // may still be code required to change an offset, so we don't want to memmove them test_ptr_cat<0, derived_class, base_class>(); @@ -211,217 +222,218 @@ void ptr_cat_test_cases() { } template -void test_case_Equal_memcmp_is_safe_comparator() { +void test_case_Memcmp_in_equal_is_safe_comparator() { // Default case - STATIC_ASSERT(_Equal_memcmp_is_safe == Expected); + STATIC_ASSERT(_Memcmp_in_equal_is_safe == Expected); // Adding const should not change the answer - STATIC_ASSERT(_Equal_memcmp_is_safe == Expected); - STATIC_ASSERT(_Equal_memcmp_is_safe == Expected); - STATIC_ASSERT(_Equal_memcmp_is_safe == Expected); + STATIC_ASSERT(_Memcmp_in_equal_is_safe == Expected); + STATIC_ASSERT(_Memcmp_in_equal_is_safe == Expected); + STATIC_ASSERT(_Memcmp_in_equal_is_safe == Expected); // Top level const should not change the answer - STATIC_ASSERT(_Equal_memcmp_is_safe == Expected); - STATIC_ASSERT(_Equal_memcmp_is_safe == Expected); - STATIC_ASSERT(_Equal_memcmp_is_safe == Expected); - STATIC_ASSERT(_Equal_memcmp_is_safe == Expected); - STATIC_ASSERT(_Equal_memcmp_is_safe == Expected); - STATIC_ASSERT(_Equal_memcmp_is_safe == Expected); - STATIC_ASSERT(_Equal_memcmp_is_safe == Expected); - STATIC_ASSERT(_Equal_memcmp_is_safe == Expected); - STATIC_ASSERT(_Equal_memcmp_is_safe == Expected); - STATIC_ASSERT(_Equal_memcmp_is_safe == Expected); - STATIC_ASSERT(_Equal_memcmp_is_safe == Expected); - STATIC_ASSERT(_Equal_memcmp_is_safe == Expected); + STATIC_ASSERT(_Memcmp_in_equal_is_safe == Expected); + STATIC_ASSERT(_Memcmp_in_equal_is_safe == Expected); + STATIC_ASSERT(_Memcmp_in_equal_is_safe == Expected); + STATIC_ASSERT(_Memcmp_in_equal_is_safe == Expected); + STATIC_ASSERT(_Memcmp_in_equal_is_safe == Expected); + STATIC_ASSERT(_Memcmp_in_equal_is_safe == Expected); + STATIC_ASSERT(_Memcmp_in_equal_is_safe == Expected); + STATIC_ASSERT(_Memcmp_in_equal_is_safe == Expected); + STATIC_ASSERT(_Memcmp_in_equal_is_safe == Expected); + STATIC_ASSERT(_Memcmp_in_equal_is_safe == Expected); + STATIC_ASSERT(_Memcmp_in_equal_is_safe == Expected); + STATIC_ASSERT(_Memcmp_in_equal_is_safe == Expected); // Adding volatile anywhere should explode - STATIC_ASSERT(_Equal_memcmp_is_safe == false); - STATIC_ASSERT(_Equal_memcmp_is_safe == false); - STATIC_ASSERT(_Equal_memcmp_is_safe == false); - STATIC_ASSERT(_Equal_memcmp_is_safe == false); - STATIC_ASSERT(_Equal_memcmp_is_safe == false); - STATIC_ASSERT(_Equal_memcmp_is_safe == false); - STATIC_ASSERT(_Equal_memcmp_is_safe == false); - STATIC_ASSERT(_Equal_memcmp_is_safe == false); - STATIC_ASSERT(_Equal_memcmp_is_safe == false); - STATIC_ASSERT(_Equal_memcmp_is_safe == false); - STATIC_ASSERT(_Equal_memcmp_is_safe == false); - STATIC_ASSERT(_Equal_memcmp_is_safe == false); + STATIC_ASSERT(_Memcmp_in_equal_is_safe == false); + STATIC_ASSERT(_Memcmp_in_equal_is_safe == false); + STATIC_ASSERT(_Memcmp_in_equal_is_safe == false); + STATIC_ASSERT(_Memcmp_in_equal_is_safe == false); + STATIC_ASSERT(_Memcmp_in_equal_is_safe == false); + STATIC_ASSERT(_Memcmp_in_equal_is_safe == false); + STATIC_ASSERT(_Memcmp_in_equal_is_safe == false); + STATIC_ASSERT(_Memcmp_in_equal_is_safe == false); + STATIC_ASSERT(_Memcmp_in_equal_is_safe == false); + STATIC_ASSERT(_Memcmp_in_equal_is_safe == false); + STATIC_ASSERT(_Memcmp_in_equal_is_safe == false); + STATIC_ASSERT(_Memcmp_in_equal_is_safe == false); #ifdef __cpp_lib_concepts // contiguous iterators should not change the answer if constexpr (!is_same_v && !is_same_v) { // vector::iterator is not contiguous - STATIC_ASSERT( - _Equal_memcmp_is_safe::iterator, typename vector::iterator, Pr> == Expected); - STATIC_ASSERT(_Equal_memcmp_is_safe::const_iterator, + STATIC_ASSERT(_Memcmp_in_equal_is_safe::iterator, typename vector::iterator, + Pr> == Expected); + STATIC_ASSERT(_Memcmp_in_equal_is_safe::const_iterator, typename vector::const_iterator, Pr> == Expected); } - STATIC_ASSERT( - _Equal_memcmp_is_safe::iterator, typename array::iterator, Pr> == Expected); - STATIC_ASSERT(_Equal_memcmp_is_safe::const_iterator, + STATIC_ASSERT(_Memcmp_in_equal_is_safe::iterator, typename array::iterator, + Pr> == Expected); + STATIC_ASSERT(_Memcmp_in_equal_is_safe::const_iterator, typename array::const_iterator, Pr> == Expected); // Mixing contiguous iterators should not change the answer if constexpr (!is_same_v && !is_same_v) { - STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, typename vector::const_iterator, + STATIC_ASSERT(_Memcmp_in_equal_is_safe::iterator, typename vector::const_iterator, Pr> == Expected); } if constexpr (!is_same_v) { - STATIC_ASSERT(_Equal_memcmp_is_safe::const_iterator, + STATIC_ASSERT(_Memcmp_in_equal_is_safe::const_iterator, typename vector::const_iterator, Pr> == Expected); } if constexpr (!is_same_v) { - STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, typename array::iterator, - Pr> == Expected); - STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, typename array::const_iterator, + STATIC_ASSERT(_Memcmp_in_equal_is_safe::iterator, typename array::iterator, Pr> == Expected); + STATIC_ASSERT(_Memcmp_in_equal_is_safe::iterator, + typename array::const_iterator, Pr> == Expected); } // span iterators are contiguous STATIC_ASSERT( - _Equal_memcmp_is_safe::iterator, typename span::iterator, Pr> == Expected); + _Memcmp_in_equal_is_safe::iterator, typename span::iterator, Pr> == Expected); STATIC_ASSERT( - _Equal_memcmp_is_safe::iterator, typename span::iterator, Pr> == Expected); + _Memcmp_in_equal_is_safe::iterator, typename span::iterator, Pr> == Expected); STATIC_ASSERT( - _Equal_memcmp_is_safe::iterator, typename span::iterator, Pr> == Expected); - STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, typename span::iterator, + _Memcmp_in_equal_is_safe::iterator, typename span::iterator, Pr> == Expected); + STATIC_ASSERT(_Memcmp_in_equal_is_safe::iterator, typename span::iterator, Pr> == Expected); // contiguous iterators to volatile should explode STATIC_ASSERT( - _Equal_memcmp_is_safe::iterator, typename span::iterator, Pr> == false); + _Memcmp_in_equal_is_safe::iterator, typename span::iterator, Pr> == false); STATIC_ASSERT( - _Equal_memcmp_is_safe::iterator, typename span::iterator, Pr> == false); - STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, + _Memcmp_in_equal_is_safe::iterator, typename span::iterator, Pr> == false); + STATIC_ASSERT(_Memcmp_in_equal_is_safe::iterator, + typename span::iterator, Pr> == false); + STATIC_ASSERT(_Memcmp_in_equal_is_safe::iterator, + typename span::iterator, Pr> == false); + STATIC_ASSERT(_Memcmp_in_equal_is_safe::iterator, typename span::iterator, Pr> == false); - STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, typename span::iterator, - Pr> == false); - STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, typename span::iterator, - Pr> == false); - STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, + STATIC_ASSERT(_Memcmp_in_equal_is_safe::iterator, typename span::iterator, Pr> == false); - STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, typename span::iterator, - Pr> == false); - STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, typename span::iterator, - Pr> == false); - STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, + STATIC_ASSERT(_Memcmp_in_equal_is_safe::iterator, + typename span::iterator, Pr> == false); + STATIC_ASSERT(_Memcmp_in_equal_is_safe::iterator, typename span::iterator, Pr> == false); - STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, + STATIC_ASSERT(_Memcmp_in_equal_is_safe::iterator, + typename span::iterator, Pr> == false); + STATIC_ASSERT(_Memcmp_in_equal_is_safe::iterator, typename span::iterator, Pr> == false); - STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, + STATIC_ASSERT(_Memcmp_in_equal_is_safe::iterator, typename span::iterator, Pr> == false); - STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, + STATIC_ASSERT(_Memcmp_in_equal_is_safe::iterator, typename span::iterator, Pr> == false); #endif // __cpp_lib_concepts // Non-contiguous iterators should explode - STATIC_ASSERT(_Equal_memcmp_is_safe::iterator, typename list::iterator, Pr> == false); + STATIC_ASSERT( + _Memcmp_in_equal_is_safe::iterator, typename list::iterator, Pr> == false); } template -void test_case_Equal_memcmp_is_safe() { - test_case_Equal_memcmp_is_safe_comparator, Elem1, Elem2, equal_to>(); - test_case_Equal_memcmp_is_safe_comparator>(); +void test_case_Memcmp_in_equal_is_safe() { + test_case_Memcmp_in_equal_is_safe_comparator, Elem1, Elem2, equal_to>(); + test_case_Memcmp_in_equal_is_safe_comparator>(); #ifdef __cpp_lib_concepts - test_case_Equal_memcmp_is_safe_comparator(); + test_case_Memcmp_in_equal_is_safe_comparator(); #endif // __cpp_lib_concepts // equal_to< some other T > should explode - STATIC_ASSERT(_Equal_memcmp_is_safe>> == false); + STATIC_ASSERT(_Memcmp_in_equal_is_safe>> == false); // Non-equal_to comparison functions should explode auto lambda = [](Elem1*, Elem2*) { return false; }; - STATIC_ASSERT(_Equal_memcmp_is_safe == false); + STATIC_ASSERT(_Memcmp_in_equal_is_safe == false); // equal_to should not explode - STATIC_ASSERT(_Equal_memcmp_is_safe> == (Expected && is_same_v) ); + STATIC_ASSERT(_Memcmp_in_equal_is_safe> == (Expected && is_same_v) ); // But again, not volatile - STATIC_ASSERT(_Equal_memcmp_is_safe> == false); - STATIC_ASSERT(_Equal_memcmp_is_safe> == false); + STATIC_ASSERT(_Memcmp_in_equal_is_safe> == false); + STATIC_ASSERT(_Memcmp_in_equal_is_safe> == false); } void equal_safe_test_cases() { // memcmp is safe for integral types - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); // unless their sizes differ - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); // signedness must be the same if usual arithmetic conversions are not bits-preserving - test_case_Equal_memcmp_is_safe, char, signed char>(); - test_case_Equal_memcmp_is_safe, signed char, char>(); - test_case_Equal_memcmp_is_safe, char, unsigned char>(); - test_case_Equal_memcmp_is_safe, unsigned char, char>(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); + test_case_Memcmp_in_equal_is_safe, char, signed char>(); + test_case_Memcmp_in_equal_is_safe, signed char, char>(); + test_case_Memcmp_in_equal_is_safe, char, unsigned char>(); + test_case_Memcmp_in_equal_is_safe, unsigned char, char>(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); // but if UACs don't change bits the signedness can differ - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); // memcmp is safe between bool and other integral types with the same size because we don't care about // representations other than 0 and 1 - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); // No enums - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); // No user-defined types - test_case_Equal_memcmp_is_safe(); + test_case_Memcmp_in_equal_is_safe(); #ifdef __cpp_lib_byte // memcmp is safe for std::byte, but it can't be compared to integral types - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); #endif // __cpp_lib_byte // Pointers to cv T are OK (they *point to* volatile stuff, they aren't volatile themselves) typedef void (*funcptr_t)(int); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); // Pointers to not-the-same-type need to go to the general algorithm - test_case_Equal_memcmp_is_safe(); + test_case_Memcmp_in_equal_is_safe(); // Technically pointers to cv-void and any other object pointer should be OK, but the // metaprogramming shouldn't attempt to handle that case because detecting function pointer // types is not worth it - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_is_safe(); + test_case_Memcmp_in_equal_is_safe(); + test_case_Memcmp_in_equal_is_safe(); } template @@ -432,7 +444,7 @@ void test_case_Lex_compare_optimize_helper() { template void test_case_Lex_compare_optimize() { - // same cv-qualifiers song and dance as test_case_Equal_memcmp_is_safe + // same cv-qualifiers song and dance as test_case_Memcmp_in_equal_is_safe test_case_Lex_compare_optimize_helper(); test_case_Lex_compare_optimize_helper(); test_case_Lex_compare_optimize_helper();