From 63b5a60afd9c34ccd9a195ddb760a13e1a6f1396 Mon Sep 17 00:00:00 2001 From: Adam Bucior <35536269+AdamBucior@users.noreply.github.com> Date: Wed, 25 Aug 2021 12:54:20 +0200 Subject: [PATCH 01/15] Optimize algorithms --- stl/inc/algorithm | 9 +- stl/inc/memory | 10 +- stl/inc/vector | 4 +- stl/inc/xmemory | 11 +- stl/inc/xstring | 11 +- stl/inc/xutility | 453 ++++++----- tests/std/test.lst | 7 +- .../env.lst | 0 .../tests/GH_000431_copy_move_family/test.cpp | 718 +++++++++++++++++ .../std/tests/GH_000431_equal_family/env.lst | 4 + .../std/tests/GH_000431_equal_family/test.cpp | 755 ++++++++++++++++++ .../GH_000431_equal_memcmp_is_safe/env.lst | 4 + .../test.compile.pass.cpp | 510 ++++++++++++ .../GH_000431_iter_copy_move_cat/env.lst | 4 + .../test.compile.pass.cpp | 483 +++++++++++ .../GH_000431_lex_compare_family/env.lst | 4 + .../GH_000431_lex_compare_family/test.cpp | 569 +++++++++++++ .../env.lst | 4 + .../test.compile.pass.cpp | 340 ++++++++ .../test.cpp | 4 + .../VSO_0180469_ptr_cat/test.compile.pass.cpp | 561 ------------- 21 files changed, 3678 insertions(+), 787 deletions(-) rename tests/std/tests/{VSO_0180469_ptr_cat => GH_000431_copy_move_family}/env.lst (100%) create mode 100644 tests/std/tests/GH_000431_copy_move_family/test.cpp create mode 100644 tests/std/tests/GH_000431_equal_family/env.lst create mode 100644 tests/std/tests/GH_000431_equal_family/test.cpp create mode 100644 tests/std/tests/GH_000431_equal_memcmp_is_safe/env.lst create mode 100644 tests/std/tests/GH_000431_equal_memcmp_is_safe/test.compile.pass.cpp create mode 100644 tests/std/tests/GH_000431_iter_copy_move_cat/env.lst create mode 100644 tests/std/tests/GH_000431_iter_copy_move_cat/test.compile.pass.cpp create mode 100644 tests/std/tests/GH_000431_lex_compare_family/env.lst create mode 100644 tests/std/tests/GH_000431_lex_compare_family/test.cpp create mode 100644 tests/std/tests/GH_000431_lex_compare_memcmp_classify/env.lst create mode 100644 tests/std/tests/GH_000431_lex_compare_memcmp_classify/test.compile.pass.cpp delete mode 100644 tests/std/tests/VSO_0180469_ptr_cat/test.compile.pass.cpp diff --git a/stl/inc/algorithm b/stl/inc/algorithm index cb6147d51fa..2bc67382ffe 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -1313,7 +1313,7 @@ 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 (_Iter_copy_cat::_Trivially_assignable) { if (!_STD is_constant_evaluated()) { auto _Final = _UFirst + _Count; _Result = _Copy_memmove(_STD move(_UFirst), _Final, _STD move(_Result)); @@ -1460,7 +1460,7 @@ 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 (_Iter_move_cat<_It, _Out>::_Trivially_assignable) { if (!_STD is_constant_evaluated()) { auto _Final = _RANGES next(_First, _STD move(_Last)); _Result = _Copy_memmove(_STD move(_First), _Final, _STD move(_Result)); @@ -1516,7 +1516,7 @@ 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 (_Iter_move_cat<_It1, _It2>::_Trivially_assignable) { if (!_STD is_constant_evaluated()) { return _Copy_backward_memmove(_First, _Last, _Result); } @@ -10101,8 +10101,7 @@ namespace ranges { _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); _STL_INTERNAL_STATIC_ASSERT(indirect_strict_weak_order<_Pr, projected<_It1, _Pj1>, projected<_It2, _Pj2>>); - using _Memcmp_classification_pred = - typename decltype(_Lex_compare_memcmp_classify(_First1, _First2, _Pred))::_Pred; + using _Memcmp_classification_pred = _Lex_compare_memcmp_classify<_It1, _It2, _Pr>; constexpr bool _Is_sized1 = sized_sentinel_for<_Se1, _It1>; constexpr bool _Is_sized2 = sized_sentinel_for<_Se2, _It2>; if constexpr (!is_void_v<_Memcmp_classification_pred> && _Sized_or_unreachable_sentinel_for<_Se1, _It1> // diff --git a/stl/inc/memory b/stl/inc/memory index 88726d5ee67..119d4f588a8 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -78,7 +78,7 @@ namespace ranges { constexpr bool _Is_sized1 = sized_sentinel_for<_Se, _It>; constexpr bool _Is_sized2 = sized_sentinel_for<_OSe, _Out>; - if constexpr (_Ptr_copy_cat<_It, _Out>::_Really_trivial && _Sized_or_unreachable_sentinel_for<_Se, _It> // + if constexpr (_Iter_copy_cat<_It, _Out>::_Trivially_constructible && _Sized_or_unreachable_sentinel_for<_Se, _It> // && _Sized_or_unreachable_sentinel_for<_OSe, _Out>) { if constexpr (_Is_sized1 && _Is_sized2) { return _Copy_memcpy_common(_IFirst, _RANGES next(_IFirst, _STD move(_ILast)), _OFirst, @@ -116,7 +116,7 @@ _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) { + if constexpr (_Iter_copy_cat::_Trivially_constructible) { _UDest = _Copy_memmove(_UFirst, _UFirst + _Count, _UDest); } else { _Uninitialized_backout _Backout{_UDest}; @@ -155,7 +155,7 @@ namespace ranges { auto _IFirst = _Get_unwrapped_n(_STD move(_First1), _Count); auto _OFirst = _Get_unwrapped(_STD move(_First2)); auto _OLast = _Get_unwrapped(_STD move(_Last2)); - if constexpr (_Ptr_copy_cat<_It, _Out>::_Really_trivial && _Sized_or_unreachable_sentinel_for<_OSe, _Out>) { + if constexpr (_Iter_copy_cat<_It, _Out>::_Trivially_constructible && _Sized_or_unreachable_sentinel_for<_OSe, _Out>) { if constexpr (sized_sentinel_for<_OSe, _Out>) { auto _UResult = _Copy_memcpy_common( _IFirst, _IFirst + _Count, _OFirst, _RANGES next(_OFirst, _STD move(_OLast))); @@ -250,7 +250,7 @@ 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) { + if constexpr (_Iter_move_cat::_Trivially_constructible) { _UDest = _Copy_memmove(_UFirst, _UFirst + _Count, _UDest); _UFirst += _Count; } else { @@ -293,7 +293,7 @@ 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 && _Sized_or_unreachable_sentinel_for<_OSe, _Out>) { + if constexpr (_Iter_move_cat<_It, _Out>::_Trivially_constructible && _Sized_or_unreachable_sentinel_for<_OSe, _Out>) { if constexpr (sized_sentinel_for<_OSe, _Out>) { auto _UResult = _Copy_memcpy_common( _IFirst, _IFirst + _Count, _OFirst, _RANGES next(_OFirst, _STD move(_OLast))); diff --git a/stl/inc/vector b/stl/inc/vector index 503a506f83f..c8b626a0872 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -1031,7 +1031,7 @@ private: _My_data._Orphan_all(); - if constexpr (conjunction_v::_Trivially_copyable>, + if constexpr (conjunction_v::_Trivially_constructible_and_assignable>, _Uses_default_construct<_Alty, _Ty*, decltype(*_First)>, _Uses_default_destroy<_Alty, _Ty*>>) { #if _HAS_CXX20 @@ -1691,7 +1691,7 @@ private: _My_data._Orphan_all(); const auto _Oldcapacity = static_cast(_My_data._Myend - _Myfirst); - if constexpr (conjunction_v::_Trivially_copyable>, + if constexpr (conjunction_v::_Trivially_constructible_and_assignable>, _Uses_default_construct<_Alty, _Ty*, _Ty>, _Uses_default_destroy<_Alty, _Ty*>>) { if (_Newsize > _Oldcapacity) { _Clear_and_reserve_geometric(_Newsize); diff --git a/stl/inc/xmemory b/stl/inc/xmemory index 29f2c2afb3a..cee027c8a68 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -1477,7 +1477,7 @@ struct _NODISCARD _Uninitialized_backout { template _CONSTEXPR20 _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 (_Iter_move_cat<_InIt, _NoThrowFwdIt>::_Trivially_constructible) { #if _HAS_CXX20 if (!_STD is_constant_evaluated()) #endif // _HAS_CXX20 @@ -1608,7 +1608,8 @@ namespace ranges { // clang-format on constexpr bool _Is_sized1 = sized_sentinel_for<_Se, _It>; constexpr bool _Is_sized2 = sized_sentinel_for<_OSe, _Out>; - if constexpr (_Ptr_move_cat<_It, _Out>::_Really_trivial && _Sized_or_unreachable_sentinel_for<_Se, _It> // + if constexpr (_Iter_move_cat<_It, _Out>::_Trivially_constructible + && _Sized_or_unreachable_sentinel_for<_Se, _It> // && _Sized_or_unreachable_sentinel_for<_OSe, _Out>) { if constexpr (_Is_sized1 && _Is_sized2) { return _Copy_memcpy_common(_IFirst, _RANGES next(_IFirst, _STD move(_ILast)), _OFirst, @@ -1675,7 +1676,7 @@ _CONSTEXPR20 _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::_Trivially_constructible>, _Uses_default_construct<_Alloc, _Ptrval, decltype(*_UFirst)>>) { #if _HAS_CXX20 if (!_STD is_constant_evaluated()) @@ -1698,7 +1699,7 @@ _CONSTEXPR20 _Alloc_ptr_t<_Alloc> _Uninitialized_copy( template _CONSTEXPR20 _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 (_Iter_copy_cat<_InIt, _NoThrowFwdIt>::_Trivially_constructible) { #if _HAS_CXX20 if (!_STD is_constant_evaluated()) #endif // _HAS_CXX20 @@ -1734,7 +1735,7 @@ _CONSTEXPR20 _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::_Trivially_constructible>, _Uses_default_construct<_Alloc, _Ptrval, decltype(_STD move(*_UFirst))>>) { #if _HAS_CXX20 if (!_STD is_constant_evaluated()) diff --git a/stl/inc/xstring b/stl/inc/xstring index ac4faad1c29..61bc9b3e1a6 100644 --- a/stl/inc/xstring +++ b/stl/inc/xstring @@ -552,7 +552,16 @@ struct _Char_traits_lt { // library-provided char_traits::eq behaves like equal_to<_Elem> // TRANSITION: This should not be activated for user-defined specializations of char_traits template -_INLINE_VAR constexpr bool _Pred_is_consistent_with_memcmp<_Elem, _Elem, _Char_traits_eq>> = true; +_INLINE_VAR constexpr bool _Can_memcmp_elements_with_pred<_Elem, _Elem, _Char_traits_eq>> = + _Can_memcmp_elements<_Elem, _Elem>; + +// library-provided char_traits::lt behaves like less> +// TRANSITION: This should not be activated for user-defined specializations of char_traits +template +struct _Lex_compare_memcmp_classify_pred<_Elem, _Elem, _Char_traits_lt>> { + using _UElem = make_unsigned_t<_Elem>; + using _Pred = conditional_t<_Lex_compare_memcmp_classify_elements<_UElem, _UElem>, less, void>; +}; template using _Traits_ch_t = typename _Traits::char_type; diff --git a/stl/inc/xutility b/stl/inc/xutility index dc3f747c32a..2608309055d 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -3918,76 +3918,91 @@ _NODISCARD constexpr auto _To_address(const _Iter& _Val) noexcept { } #endif // ^^^ !defined(__cpp_lib_concepts) ^^^ +template +_NODISCARD constexpr auto _To_address(const move_iterator<_Iter>& _Val) noexcept { + return _To_address(_Val.base()); +} + // _Iterators_are_contiguous<_Iter1, _Iter2> reports whether both iterators are known to be contiguous. template _INLINE_VAR constexpr bool _Iterators_are_contiguous = _Iterator_is_contiguous<_Iter1>&& _Iterator_is_contiguous<_Iter2>; -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; -}; +template +_INLINE_VAR constexpr bool _Iterator_is_volatile = is_volatile_v>>; -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>; +template +_INLINE_VAR constexpr bool _Is_pointer_address_convertible = is_void_v<_Source> || is_void_v<_Dest> + // NOTE: is_same_v is required for function pointers to work + || is_same_v, remove_cv_t<_Dest>> +#ifdef __cpp_lib_is_pointer_interconvertible + || is_pointer_interconvertible_base_of_v<_Dest, _Source> +#endif // __cpp_lib_is_pointer_interconvertible + ; + +template +struct _Trivial_cat { + using _USource = _Unwrap_enum_t<_Source>; + using _UDest = _Unwrap_enum_t<_Dest>; + + static constexpr bool _Same_size_and_compatible = + sizeof(_Source) == sizeof(_Dest) + // If _UDest is bool _USource also needs to be bool + // Conversion from non-bool => non-bool | bool => bool | bool => non-bool is fine. + // Conversion from non-bool => bool is not fine. + && is_same_v >= is_same_v // + && (is_same_v<_Source, _Dest> || !(is_member_pointer_v<_Source> || is_member_pointer_v<_Dest>) ); + + static constexpr bool _Trivially_constructible = + _Same_size_and_compatible && is_trivially_constructible_v<_Dest, _SourceRef>; + + static constexpr bool _Trivially_assignable = + _Same_size_and_compatible && is_trivially_assignable_v<_DestRef, _SourceRef>; + + static constexpr bool _Trivially_constructible_and_assignable = _Trivially_constructible && _Trivially_assignable; }; -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 +struct _Trivial_cat<_Source*, _Dest*, _SourceRef, _DestRef> { + static constexpr bool _Trivially_constructible = + _Is_pointer_address_convertible<_Source, _Dest> && is_trivially_constructible_v<_Dest*, _SourceRef>; -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; -}; + static constexpr bool _Trivially_assignable = + _Is_pointer_address_convertible<_Source, _Dest> && is_trivially_assignable_v<_DestRef, _SourceRef>; -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; + static constexpr bool _Trivially_constructible_and_assignable = _Trivially_constructible && _Trivially_assignable; }; -struct _False_copy_cat { - static constexpr bool _Really_trivial = false; - static constexpr bool _Trivially_copyable = false; +struct _False_trivial_cat { + static constexpr bool _Trivially_constructible = false; + static constexpr bool _Trivially_assignable = false; + static constexpr bool _Trivially_constructible_and_assignable = false; }; -// NOTE: pointer is not a contiguous iterator if it points to volatile type -template > -struct _Ptr_move_cat : _False_copy_cat {}; +template // + && !_Iterator_is_volatile<_SourceIt> && !_Iterator_is_volatile<_DestIt>> +struct _Iter_move_cat : _Trivial_cat<_Iter_value_t<_SourceIt>, _Iter_value_t<_DestIt>, + remove_reference_t<_Iter_ref_t<_SourceIt>>&&, _Iter_ref_t<_DestIt>> {}; -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> {}; +template +struct _Iter_move_cat<_SourceIt, _DestIt, false> : _False_trivial_cat {}; -template -struct _Ptr_move_cat, _Dest, true> : _Ptr_move_cat<_Source, _Dest> {}; +template +struct _Iter_move_cat, _DestIt, false> : _Iter_move_cat<_SourceIt, _DestIt> {}; -template > -struct _Ptr_copy_cat : _False_copy_cat {}; +template // + && !_Iterator_is_volatile<_SourceIt> && !_Iterator_is_volatile<_DestIt>> +struct _Iter_copy_cat + : _Trivial_cat<_Iter_value_t<_SourceIt>, _Iter_value_t<_DestIt>, _Iter_ref_t<_SourceIt>, _Iter_ref_t<_DestIt>> {}; -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> {}; +template +struct _Iter_copy_cat<_SourceIt, _DestIt, false> : _False_trivial_cat {}; -template -struct _Ptr_copy_cat, _Dest, true> : _Ptr_move_cat<_Source, _Dest> {}; +template +struct _Iter_copy_cat, _DestIt, false> : _Iter_move_cat<_SourceIt, _DestIt> {}; template _OutCtgIt _Copy_memmove(_CtgIt _First, _CtgIt _Last, _OutCtgIt _Dest) { @@ -4006,11 +4021,6 @@ _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 _INLINE_VAR constexpr bool _Is_vb_iterator = false; @@ -4018,7 +4028,7 @@ 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 (_Iter_copy_cat<_InIt, _OutIt>::_Trivially_assignable) { #if _HAS_CXX20 if (!_STD is_constant_evaluated()) #endif // _HAS_CXX20 @@ -4084,7 +4094,7 @@ 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 (_Iter_copy_cat<_It, _Out>::_Trivially_assignable && 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)); @@ -4137,7 +4147,7 @@ _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 (_Iter_copy_cat::_Trivially_assignable) { #if _HAS_CXX20 if (!_STD is_constant_evaluated()) #endif // _HAS_CXX20 @@ -4203,7 +4213,7 @@ _BidIt2 _Copy_backward_memmove(move_iterator<_BidIt1> _First, move_iterator<_Bid 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 (_Iter_copy_cat<_BidIt1, _BidIt2>::_Trivially_assignable) { #if _HAS_CXX20 if (!_STD is_constant_evaluated()) #endif // _HAS_CXX20 @@ -4243,7 +4253,7 @@ 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 (_Iter_move_cat<_InIt, _OutIt>::_Trivially_assignable) { #if _HAS_CXX20 if (!_STD is_constant_evaluated()) #endif // _HAS_CXX20 @@ -4285,7 +4295,7 @@ 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 (_Iter_move_cat<_BidIt1, _BidIt2>::_Trivially_assignable) { #if _HAS_CXX20 if (!_STD is_constant_evaluated()) #endif // _HAS_CXX20 @@ -4387,8 +4397,12 @@ template _NODISCARD bool _Is_all_bits_zero(const _Ty& _Val) { // checks if scalar type has all bits set to zero _STL_INTERNAL_STATIC_ASSERT(is_scalar_v<_Ty> && !is_member_pointer_v<_Ty>); - constexpr _Ty _Zero{}; - return _CSTD memcmp(&_Val, &_Zero, sizeof(_Ty)) == 0; + if constexpr (is_same_v<_Ty, nullptr_t>) { + return true; + } else { + constexpr _Ty _Zero{}; + return _CSTD memcmp(&_Val, &_Zero, sizeof(_Ty)) == 0; + } } template @@ -4518,6 +4532,18 @@ namespace ranges { } // namespace ranges #endif // __cpp_lib_concepts +template +_INLINE_VAR constexpr bool _Can_compare_with_operator_equal = false; + +template +_INLINE_VAR constexpr bool + _Can_compare_with_operator_equal<_Ty1, _Ty2, void_t() == _STD declval<_Ty2&>())>> = + true; + +template +_INLINE_VAR constexpr bool _Is_pointer_address_comparable = + (_Is_pointer_address_convertible<_Ty1, + _Ty2> || _Is_pointer_address_convertible<_Ty2, _Ty1>) &&_Can_compare_with_operator_equal<_Ty1*, _Ty2*>; // _Can_memcmp_elements<_Elem1, _Elem2> reports whether `_Elem1 == _Elem2` can be optimized to memcmp. // Here, _Elem1 and _Elem2 aren't top-level const, because we remove_const_t before using _Can_memcmp_elements. @@ -4530,9 +4556,7 @@ namespace ranges { #pragma warning(push) #pragma warning(disable : 4806) // no value of type 'bool' promoted to type 'char' can equal the given constant template && !is_volatile_v<_Elem1> // - && is_integral_v<_Elem2> && !is_volatile_v<_Elem2>> + bool = sizeof(_Elem1) == sizeof(_Elem2) && is_integral_v<_Elem1>&& is_integral_v<_Elem2>> _INLINE_VAR constexpr bool _Can_memcmp_elements = is_same_v<_Elem1, bool> || is_same_v<_Elem2, bool> || static_cast<_Elem1>(-1) == static_cast<_Elem2>(-1); #pragma warning(pop) @@ -4547,49 +4571,45 @@ inline constexpr bool _Can_memcmp_elements = true; // Pointer elements are eligible for memcmp 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_memcmp_elements<_Ty1*, _Ty2*, false> = is_same_v, remove_cv_t<_Ty2>>; +_INLINE_VAR constexpr bool _Can_memcmp_elements<_Ty1*, _Ty2*, false> = _Is_pointer_address_comparable<_Ty1, _Ty2>; template _INLINE_VAR constexpr bool _Can_memcmp_elements<_Elem1, _Elem2, false> = false; -// _Pred_is_consistent_with_memcmp<_Elem1, _Elem2, _Pr> reports whether `_Pr(_Elem1, _Elem2)` returns -// `_Elem1 == _Elem2` without performing any type conversions that could affect the memcmp optimization. -// (It isn't concerned with whether the elements are actually eligible.) -// Again, _Elem1 and _Elem2 aren't top-level const here. +// _Can_memcmp_elements_with_pred<_Elem1, _Elem2, _Pr> reports whether the memcmp optimization is applicable, +// given contiguously stored elements. (This avoids having to repeat the metaprogramming that finds the element types.) +// _Elem1 and _Elem2 aren't top-level const here. template -_INLINE_VAR constexpr bool _Pred_is_consistent_with_memcmp = false; +_INLINE_VAR constexpr bool _Can_memcmp_elements_with_pred = false; -// When all of the types are the same, equal_to<_Elem> is effectively transparent. -// Performance note: This doesn't attempt to handle equal_to(int, long) or equal_to(T*, const T*). -template -_INLINE_VAR constexpr bool _Pred_is_consistent_with_memcmp<_Elem, _Elem, equal_to<_Elem>> = true; +// With equal_to<_Elem3> we need to make sure that both _Elem1 and _Elem2 are convertible to _Elem3 without changing +// object representation (we use _Iter_copy_cat for this task) and _Elem3 can be safely memcmp'ed with itself +template +_INLINE_VAR constexpr bool _Can_memcmp_elements_with_pred<_Elem1, _Elem2, equal_to<_Elem3>> = + _Iter_copy_cat<_Elem1*, _Elem3*>::_Trivially_constructible&& _Iter_copy_cat<_Elem2*, + _Elem3*>::_Trivially_constructible&& _Can_memcmp_elements, remove_cv_t<_Elem3>>; // equal_to<> is transparent. template -_INLINE_VAR constexpr bool _Pred_is_consistent_with_memcmp<_Elem1, _Elem2, equal_to<>> = true; +_INLINE_VAR constexpr bool _Can_memcmp_elements_with_pred<_Elem1, _Elem2, equal_to<>> = + _Can_memcmp_elements<_Elem1, _Elem2>; #ifdef __cpp_lib_concepts // ranges::equal_to is also transparent. template -_INLINE_VAR constexpr bool _Pred_is_consistent_with_memcmp<_Elem1, _Elem2, _RANGES equal_to> = true; +_INLINE_VAR constexpr bool _Can_memcmp_elements_with_pred<_Elem1, _Elem2, _RANGES equal_to> = + _Can_memcmp_elements<_Elem1, _Elem2>; #endif // __cpp_lib_concepts -// _Can_memcmp_elements_with_pred<_Elem1, _Elem2, _Pr> reports whether the memcmp optimization is applicable, -// given contiguously stored elements. (This avoids having to repeat the metaprogramming that finds the element types.) -// _Elem1 and _Elem2 aren't top-level const here. -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 // 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> // - && _Can_memcmp_elements_with_pred>>, - remove_const_t>>, _Pr>; +_INLINE_VAR constexpr bool _Equal_memcmp_is_safe_helper = + _Iterators_are_contiguous<_Iter1, _Iter2> // + && !_Iterator_is_volatile<_Iter1> && !_Iterator_is_volatile<_Iter2> // + && _Can_memcmp_elements_with_pred<_Iter_value_t<_Iter1>, _Iter_value_t<_Iter2>, _Pr>; template _INLINE_VAR constexpr bool _Equal_memcmp_is_safe = @@ -4842,135 +4862,109 @@ namespace ranges { } // namespace ranges #endif // __cpp_lib_concepts -template -struct _Lex_compare_check_element_types_helper - : bool_constant< - conjunction_v<_Is_character<_Elem1>, _Is_character<_Elem2>, _Is_character<_FTy>, is_unsigned<_FTy>>> { - // checks the lex_compare element types for memcmp safety for builtin functors (e.g., less) -}; - template -struct _Lex_compare_check_element_types_helper<_Elem1, _Elem2, void> - : bool_constant< - conjunction_v<_Is_character<_Elem1>, _Is_character<_Elem2>, is_unsigned<_Elem1>, is_unsigned<_Elem2>>> { - // checks the lex_compare element types for memcmp safety for transparent functors (e.g. less<>) -}; +_INLINE_VAR constexpr bool _Lex_compare_memcmp_classify_elements = conjunction_v<_Is_character_or_bool<_Elem1>, + _Is_character_or_bool<_Elem2>, is_unsigned<_Elem1>, is_unsigned<_Elem2>>; #ifdef __cpp_lib_byte template <> -struct _Lex_compare_check_element_types_helper : true_type { - // std::byte with builtin functors (e.g. less) is memcmp safe -}; - -template <> -struct _Lex_compare_check_element_types_helper : true_type { - // std::byte with transparent functors (e.g. less<>) is memcmp safe -}; +_INLINE_VAR constexpr bool _Lex_compare_memcmp_classify_elements = true; #endif // __cpp_lib_byte -template -struct _Lex_compare_optimize { - explicit _Lex_compare_optimize() = default; - - using _Pred = _Memcmp_pr; -}; // optimization tag for lexicographical_compare +template +struct _Lex_compare_memcmp_classify_pred { + using _Pred = void; +}; -template -using _Lex_compare_check_element_types = _Lex_compare_optimize, remove_const_t<_Obj2>, _FTy>::value, _Memcmp_pr, - void>>; // checks the lex_compare element types for memcmp safety +template +struct _Lex_compare_memcmp_classify_pred<_Elem1, _Elem2, less<_Elem3>> { + using _Pred = conditional_t<_Lex_compare_memcmp_classify_elements<_Elem3, _Elem3> // + && _Iter_copy_cat<_Elem1*, _Elem3*>::_Trivially_constructible + && _Iter_copy_cat<_Elem2*, _Elem3*>::_Trivially_constructible, + less, void>; +}; -template -constexpr auto _Lex_compare_memcmp_classify(const _InIt1&, const _InIt2&, const _Pr&) { - // return lex_compare optimization category for arbitrary iterators - return _Lex_compare_optimize{}; -} +template +struct _Lex_compare_memcmp_classify_pred<_Elem1, _Elem2, less<>> { + using _Pred = conditional_t<_Lex_compare_memcmp_classify_elements<_Elem1, _Elem2>, less, void>; +}; -template , int> = 0> -constexpr auto _Lex_compare_memcmp_classify(const _CtgIt1&, const _CtgIt2&, const less<_FTy>&) { - // return lex_compare optimization category for contiguous iterators and less<_FTy> - return _Lex_compare_check_element_types, remove_reference_t<_Iter_ref_t<_CtgIt1>>, - remove_reference_t<_Iter_ref_t<_CtgIt2>>, _FTy>{}; -} +template +struct _Lex_compare_memcmp_classify_pred<_Elem1, _Elem2, greater<_Elem3>> { + using _Pred = conditional_t<_Lex_compare_memcmp_classify_elements<_Elem3, _Elem3> // + && _Iter_copy_cat<_Elem1*, _Elem3*>::_Trivially_constructible + && _Iter_copy_cat<_Elem2*, _Elem3*>::_Trivially_constructible, + greater, void>; +}; -template , int> = 0> -constexpr auto _Lex_compare_memcmp_classify(const _CtgIt1&, const _CtgIt2&, const greater<_FTy>&) { - // return lex_compare optimization category for contiguous iterators and greater<_FTy> - return _Lex_compare_check_element_types, remove_reference_t<_Iter_ref_t<_CtgIt1>>, - remove_reference_t<_Iter_ref_t<_CtgIt2>>, _FTy>{}; -} +template +struct _Lex_compare_memcmp_classify_pred<_Elem1, _Elem2, greater<>> { + using _Pred = conditional_t<_Lex_compare_memcmp_classify_elements<_Elem1, _Elem2>, greater, void>; +}; #ifdef __cpp_lib_concepts -template , int> = 0> -constexpr auto _Lex_compare_memcmp_classify(const _CtgIt1&, const _CtgIt2&, const _RANGES less&) { - // return lex_compare optimization category for contiguous iterators and ranges::less - return _Lex_compare_check_element_types, remove_reference_t<_Iter_ref_t<_CtgIt1>>, - remove_reference_t<_Iter_ref_t<_CtgIt2>>, void>{}; -} +template +struct _Lex_compare_memcmp_classify_pred<_Elem1, _Elem2, _RANGES less> { + using _Pred = conditional_t<_Lex_compare_memcmp_classify_elements<_Elem1, _Elem2>, less, void>; +}; -template , int> = 0> -constexpr auto _Lex_compare_memcmp_classify(const _CtgIt1&, const _CtgIt2&, const _RANGES greater&) { - // return lex_compare optimization category for contiguous iterators and ranges::greater - return _Lex_compare_check_element_types, remove_reference_t<_Iter_ref_t<_CtgIt1>>, - remove_reference_t<_Iter_ref_t<_CtgIt2>>, void>{}; -} +template +struct _Lex_compare_memcmp_classify_pred<_Elem1, _Elem2, _RANGES greater> { + using _Pred = conditional_t<_Lex_compare_memcmp_classify_elements<_Elem1, _Elem2>, greater, void>; +}; #endif // __cpp_lib_concepts -template -_NODISCARD constexpr bool _Lex_compare_unchecked( - _InIt1 _First1, _InIt1 _Last1, _InIt2 _First2, _InIt2 _Last2, _Pr _Pred, _Lex_compare_optimize) { - // order [_First1, _Last1) vs. [_First2, _Last2), no special optimization - for (; _First1 != _Last1 && _First2 != _Last2; ++_First1, (void) ++_First2) { // something to compare, do it - if (_DEBUG_LT_PRED(_Pred, *_First1, *_First2)) { - return true; - } else if (_Pred(*_First2, *_First1)) { - return false; - } - } - - return _First1 == _Last1 && _First2 != _Last2; -} - -template -_NODISCARD _CONSTEXPR20 bool _Lex_compare_unchecked( - _CtgIt1 _First1, _CtgIt1 _Last1, _CtgIt2 _First2, _CtgIt2 _Last2, _Pr _Pred, _Lex_compare_optimize<_Memcmp_pr>) { - // order [_First1, _Last1) vs. [_First2, _Last2) memcmp optimization -#if _HAS_CXX20 - if (_STD is_constant_evaluated()) { - return _Lex_compare_unchecked(_First1, _Last1, _First2, _Last2, _Pred, _Lex_compare_optimize{}); - } -#endif // _HAS_CXX20 - (void) _Pred; - const auto _Num1 = static_cast(_Last1 - _First1); - const auto _Num2 = static_cast(_Last2 - _First2); - const int _Ans = _Memcmp_count(_First1, _First2, (_STD min) (_Num1, _Num2)); - return _Memcmp_pr{}(_Ans, 0) || (_Ans == 0 && _Num1 < _Num2); -} +template +using _Lex_compare_memcmp_classify = + conditional_t<_Iterators_are_contiguous<_It1, _It2> && !_Iterator_is_volatile<_It1> && !_Iterator_is_volatile<_It2>, + typename _Lex_compare_memcmp_classify_pred<_Iter_value_t<_It1>, _Iter_value_t<_It2>, _Pred>::_Pred, void>; template _NODISCARD _CONSTEXPR20 bool lexicographical_compare( - _InIt1 _First1, _InIt1 _Last1, _InIt2 _First2, _InIt2 _Last2, _Pr _Pred) { + const _InIt1 _First1, const _InIt1 _Last1, const _InIt2 _First2, const _InIt2 _Last2, _Pr _Pred) { // order [_First1, _Last1) vs. [_First2, _Last2) _Adl_verify_range(_First1, _Last1); _Adl_verify_range(_First2, _Last2); - const auto _UFirst1 = _Get_unwrapped(_First1); - const auto _ULast1 = _Get_unwrapped(_Last1); - const auto _UFirst2 = _Get_unwrapped(_First2); - const auto _ULast2 = _Get_unwrapped(_Last2); - return _Lex_compare_unchecked( - _UFirst1, _ULast1, _UFirst2, _ULast2, _Pass_fn(_Pred), _Lex_compare_memcmp_classify(_UFirst1, _UFirst2, _Pred)); + auto _UFirst1 = _Get_unwrapped(_First1); + const auto _ULast1 = _Get_unwrapped(_Last1); + auto _UFirst2 = _Get_unwrapped(_First2); + const auto _ULast2 = _Get_unwrapped(_Last2); + + using _Memcmp_pred = _Lex_compare_memcmp_classify; + if constexpr (!is_void_v<_Memcmp_pred>) { +#if _HAS_CXX20 + if (!_STD is_constant_evaluated()) +#endif // _HAS_CXX20 + { + const auto _Num1 = static_cast(_ULast1 - _UFirst1); + const auto _Num2 = static_cast(_ULast2 - _UFirst2); + const int _Ans = _Memcmp_count(_UFirst1, _UFirst2, (_STD min) (_Num1, _Num2)); + return _Memcmp_pred{}(_Ans, 0) || (_Ans == 0 && _Num1 < _Num2); + } + } + + for (; _UFirst1 != _ULast1 && _UFirst2 != _ULast2; ++_UFirst1, (void) ++_UFirst2) { // something to compare, do it + if (_DEBUG_LT_PRED(_Pred, *_UFirst1, *_UFirst2)) { + return true; + } else if (_Pred(*_UFirst2, *_UFirst1)) { + return false; + } + } + + return _UFirst1 == _ULast1 && _UFirst2 != _ULast2; } template -_NODISCARD _CONSTEXPR20 bool lexicographical_compare(_InIt1 _First1, _InIt1 _Last1, _InIt2 _First2, _InIt2 _Last2) { +_NODISCARD _CONSTEXPR20 bool lexicographical_compare( + const _InIt1 _First1, const _InIt1 _Last1, const _InIt2 _First2, const _InIt2 _Last2) { // order [_First1, _Last1) vs. [_First2, _Last2) return _STD lexicographical_compare(_First1, _Last1, _First2, _Last2, less<>{}); } #if _HAS_CXX17 template = 0> -_NODISCARD bool lexicographical_compare( - _ExPo&&, _FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _First2, _FwdIt2 _Last2, _Pr _Pred) noexcept /* terminates */ { +_NODISCARD bool lexicographical_compare(_ExPo&&, const _FwdIt1 _First1, const _FwdIt1 _Last1, const _FwdIt2 _First2, + const _FwdIt2 _Last2, _Pr _Pred) noexcept /* terminates */ { // order [_First1, _Last1) vs. [_First2, _Last2) // not parallelized at present, parallelism expected to be feasible in a future release _REQUIRE_PARALLEL_ITERATOR(_FwdIt1); @@ -4979,8 +4973,8 @@ _NODISCARD bool lexicographical_compare( } template = 0> -_NODISCARD bool lexicographical_compare( - _ExPo&&, _FwdIt1 _First1, _FwdIt1 _Last1, _FwdIt2 _First2, _FwdIt2 _Last2) noexcept /* terminates */ { +_NODISCARD bool lexicographical_compare(_ExPo&&, const _FwdIt1 _First1, const _FwdIt1 _Last1, const _FwdIt2 _First2, + const _FwdIt2 _Last2) noexcept /* terminates */ { // order [_First1, _Last1) vs. [_First2, _Last2) // not parallelized at present, parallelism expected to be feasible in a future release _REQUIRE_PARALLEL_ITERATOR(_FwdIt1); @@ -4990,35 +4984,80 @@ _NODISCARD bool lexicographical_compare( #endif // _HAS_CXX17 #ifdef __cpp_lib_concepts +template +struct _Lex_compare_three_way_memcmp_classify_comp { + using _Comp = void; +}; + +template +struct _Lex_compare_three_way_memcmp_classify_comp<_Elem1, _Elem2, compare_three_way> { + using _Comp = conditional_t<_Lex_compare_memcmp_classify_elements<_Elem1, + _Elem2> && three_way_comparable_with, + compare_three_way, void>; +}; + +template +concept _Can_strong_order = requires(const _Ty1& _Left, const _Ty2& _Right) { + strong_order(_Left, _Right); +}; + +template +concept _Can_weak_order = requires(const _Ty1& _Left, const _Ty2& _Right) { + weak_order(_Left, _Right); +}; + +template +concept _Can_partial_order = requires(const _Ty1& _Left, const _Ty2& _Right) { + partial_order(_Left, _Right); +}; + +template +struct _Lex_compare_three_way_memcmp_classify_comp<_Elem1, _Elem2, _Strong_order::_Cpo> { + using _Comp = + conditional_t<_Lex_compare_memcmp_classify_elements<_Elem1, _Elem2> && _Can_strong_order<_Elem1, _Elem2>, + _Strong_order::_Cpo, void>; +}; + +template +struct _Lex_compare_three_way_memcmp_classify_comp<_Elem1, _Elem2, _Weak_order::_Cpo> { + using _Comp = + conditional_t<_Lex_compare_memcmp_classify_elements<_Elem1, _Elem2> && _Can_weak_order<_Elem1, _Elem2>, + _Weak_order::_Cpo, void>; +}; + +template +struct _Lex_compare_three_way_memcmp_classify_comp<_Elem1, _Elem2, _Partial_order::_Cpo> { + using _Comp = + conditional_t<_Lex_compare_memcmp_classify_elements<_Elem1, _Elem2> && _Can_partial_order<_Elem1, _Elem2>, + _Partial_order::_Cpo, void>; +}; + +template +using _Lex_compare_three_way_memcmp_classify = + conditional_t<_Iterators_are_contiguous<_It1, _It2> && !_Iterator_is_volatile<_It1> && !_Iterator_is_volatile<_It2>, + typename _Lex_compare_three_way_memcmp_classify_comp<_Iter_value_t<_It1>, _Iter_value_t<_It2>, _Comp>::_Comp, + void>; + template -_NODISCARD constexpr auto lexicographical_compare_three_way( - _InIt1 _First1, _InIt1 _Last1, _InIt2 _First2, _InIt2 _Last2, _Cmp _Comp) -> decltype(_Comp(*_First1, *_First2)) { +_NODISCARD constexpr auto lexicographical_compare_three_way(const _InIt1 _First1, const _InIt1 _Last1, + const _InIt2 _First2, const _InIt2 _Last2, _Cmp _Comp) -> decltype(_Comp(*_First1, *_First2)) { _Adl_verify_range(_First1, _Last1); _Adl_verify_range(_First2, _Last2); auto _UFirst1 = _Get_unwrapped(_First1); const auto _ULast1 = _Get_unwrapped(_Last1); auto _UFirst2 = _Get_unwrapped(_First2); const auto _ULast2 = _Get_unwrapped(_Last2); - using _UIt1 = decltype(_UFirst1); - using _UIt2 = decltype(_UFirst2); - using _Ty1 = remove_const_t>; - using _Ty2 = remove_const_t>; - - if constexpr ( - conjunction_v, bool_constant<_Iterators_are_contiguous<_UIt1, _UIt2>>, - disjunction< -#ifdef __cpp_lib_byte - conjunction, is_same<_Ty2, byte>>, -#endif // __cpp_lib_byte - conjunction<_Is_character<_Ty1>, is_unsigned<_Ty1>, _Is_character<_Ty2>, is_unsigned<_Ty2>>>>) { + + using _Memcmp_pred = _Lex_compare_three_way_memcmp_classify; + if constexpr (!is_void_v<_Memcmp_pred>) { if (!_STD is_constant_evaluated()) { const auto _Num1 = static_cast(_ULast1 - _UFirst1); const auto _Num2 = static_cast(_ULast2 - _UFirst2); const int _Ans = _Memcmp_count(_UFirst1, _UFirst2, (_STD min) (_Num1, _Num2)); if (_Ans == 0) { - return _Num1 <=> _Num2; + return _Memcmp_pred{}(_Num1, _Num2); } else { - return _Ans <=> 0; + return _Memcmp_pred{}(_Ans, 0); } } } @@ -5043,7 +5082,7 @@ _NODISCARD constexpr auto lexicographical_compare_three_way( template _NODISCARD constexpr auto lexicographical_compare_three_way( - _InIt1 _First1, _InIt1 _Last1, _InIt2 _First2, _InIt2 _Last2) { + const _InIt1 _First1, const _InIt1 _Last1, const _InIt2 _First2, const _InIt2 _Last2) { return _STD lexicographical_compare_three_way(_First1, _Last1, _First2, _Last2, compare_three_way{}); } #endif // __cpp_lib_concepts diff --git a/tests/std/test.lst b/tests/std/test.lst index 0aa6125ab29..aa906b41572 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -155,6 +155,12 @@ tests\Dev11_1140665_unique_ptr_array_conversions tests\Dev11_1150223_shared_mutex tests\Dev11_1158803_regex_thread_safety tests\Dev11_1180290_filesystem_error_code +tests\GH_000431_copy_move_family +tests\GH_000431_equal_family +tests\GH_000431_equal_memcmp_is_safe +tests\GH_000431_iter_copy_move_cat +tests\GH_000431_lex_compare_family +tests\GH_000431_lex_compare_memcmp_classify tests\GH_000457_system_error_message tests\GH_000545_include_compare tests\GH_000625_vector_bool_optimization @@ -489,7 +495,6 @@ tests\VSO_0157762_feature_test_macros tests\VSO_0174871_string_replace tests\VSO_0180466_algorithm_overhauls tests\VSO_0180469_fill_family -tests\VSO_0180469_ptr_cat tests\VSO_0191296_allocator_construct tests\VSO_0204655_heap_algorithms_integer_overflow tests\VSO_0224478_scoped_allocator diff --git a/tests/std/tests/VSO_0180469_ptr_cat/env.lst b/tests/std/tests/GH_000431_copy_move_family/env.lst similarity index 100% rename from tests/std/tests/VSO_0180469_ptr_cat/env.lst rename to tests/std/tests/GH_000431_copy_move_family/env.lst diff --git a/tests/std/tests/GH_000431_copy_move_family/test.cpp b/tests/std/tests/GH_000431_copy_move_family/test.cpp new file mode 100644 index 00000000000..2924b9c7e65 --- /dev/null +++ b/tests/std/tests/GH_000431_copy_move_family/test.cpp @@ -0,0 +1,718 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if _HAS_CXX17 +#include +#endif // _HAS_CXX17 + +#if _HAS_CXX20 +#include +#endif // _HAS_CXX20 + +#pragma warning(disable : 4242) // conversion from 'X' to 'Y', possible loss of data +#pragma warning(disable : 4244) // conversion from 'X' to 'Y', possible loss of data (Yes, duplicated message.) +#pragma warning(disable : 4365) // conversion from 'X' to 'Y', signed/unsigned mismatch +#pragma warning(disable : 4984) // 'if constexpr' is a C++17 language extension + +using namespace std; + +#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) + +struct TrivialStruct { + int i; + + bool operator==(const TrivialStruct& right) const { + return i == right.i; + } +}; +STATIC_ASSERT(is_trivial_v); + +struct TriviallyCopyableStruct { + int i; + TriviallyCopyableStruct() {} + TriviallyCopyableStruct(int j) : i(j) {} + + bool operator==(const TriviallyCopyableStruct& right) const { + return i == right.i; + } +}; +STATIC_ASSERT(is_trivially_copyable_v); +STATIC_ASSERT(!is_trivial_v); + +struct TriviallyMovableStruct { + int i; + TriviallyMovableStruct() {} + TriviallyMovableStruct(int j) : i(j) {} + TriviallyMovableStruct(const TriviallyMovableStruct&) = delete; + TriviallyMovableStruct(TriviallyMovableStruct&&) = default; + TriviallyMovableStruct& operator=(const TriviallyMovableStruct&) = delete; + TriviallyMovableStruct& operator=(TriviallyMovableStruct&&) = default; + + bool operator==(const TriviallyMovableStruct& right) const { + return i == right.i; + } +}; +STATIC_ASSERT(is_trivially_copyable_v); +STATIC_ASSERT(!is_trivial_v); + +enum int_enum : int {}; +enum char_enum : char {}; +enum bool_enum : bool { false_e = false, true_e = true }; + +constexpr int_enum operator""_e(unsigned long long value) noexcept { + return static_cast(value); +} + +constexpr char_enum operator""_ce(unsigned long long value) noexcept { + return static_cast(value); +} + +struct EmptyBase {}; + +struct EmptyDerived : EmptyBase {}; + +struct StatefulBase { + int i; + + int get_i() { + return i; + } + + int get_i2() { + return i * 2; + } +}; + +struct StatefulDerived : StatefulBase, EmptyBase { + int get_i3() { + return i * 3; + } +}; + +struct StatefulDerived2 : EmptyBase, StatefulBase {}; + +#ifdef __cpp_lib_is_pointer_interconvertible +STATIC_ASSERT(is_pointer_interconvertible_base_of_v); +STATIC_ASSERT(is_pointer_interconvertible_base_of_v); +STATIC_ASSERT(!is_pointer_interconvertible_base_of_v); +STATIC_ASSERT(is_pointer_interconvertible_base_of_v); +STATIC_ASSERT(is_pointer_interconvertible_base_of_v); +#endif // __cpp_lib_is_pointer_interconvertible + + +template +void test_algorithms(CopyFn copy) { + + { // Test ints + int src[10] = {5, 7, 3, 4, 6, 4, 7, 1, 9, 5}; + int expected[10] = {5, 7, 3, 4, 6, 4, 7, 1, 9, 5}; + int dst[10]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test int => long + int src[10] = {5, 7, 3, 4, 6, 4, 7, 1, 9, 5}; + long expected[10] = {5, 7, 3, 4, 6, 4, 7, 1, 9, 5}; + long dst[10]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test long => unsigned int + long src[10] = {5, 7, 3, 4, 6, 4, 7, 1, 9, 5}; + unsigned int expected[10] = {5, 7, 3, 4, 6, 4, 7, 1, 9, 5}; + unsigned int dst[10]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test bool => char + bool src[7] = {true, true, true, false, true, false, false}; + char expected[7] = {1, 1, 1, 0, 1, 0, 0}; + char dst[7]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test char => bool + char src[7] = {5, 2, 1, 0, -1, 0, 0}; + bool expected[7] = {true, true, true, false, true, false, false}; + bool dst[7]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test int => bool + int src[7] = {5, 2, 1, 0, -1, 0, 0}; + bool expected[7] = {true, true, true, false, true, false, false}; + bool dst[7]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test int => short + int src[7] = {5, 2, 1, 0, -1, 0, 0}; + short expected[7] = {5, 2, 1, 0, -1, 0, 0}; + short dst[7]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test int => unsigned int + int src[7] = {5, 2, 1, 0, -1, 0, 0}; + unsigned int expected[7] = {5, 2, 1, 0, static_cast(-1), 0, 0}; + unsigned int dst[7]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test int_enum => int + int_enum src[7] = {5_e, 2_e, 1_e, 0_e, 7_e, 0_e, 0_e}; + int expected[7] = {5, 2, 1, 0, 7, 0, 0}; + int dst[7]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test int_enum => unsigned long + int_enum src[7] = {5_e, 2_e, 1_e, 0_e, 7_e, 0_e, 0_e}; + unsigned long expected[7] = {5, 2, 1, 0, 7, 0, 0}; + unsigned long dst[7]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test bool_enum => bool + bool_enum src[7] = {true_e, true_e, true_e, false_e, true_e, false_e, false_e}; + bool expected[7] = {true, true, true, false, true, false, false}; + bool dst[7]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test bool_enum => char + bool_enum src[7] = {true_e, true_e, true_e, false_e, true_e, false_e, false_e}; + char expected[7] = {1, 1, 1, 0, 1, 0, 0}; + char dst[7]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test char_enum => bool + char_enum src[7] = {5_ce, 2_ce, 1_ce, 0_ce, 255_ce, 0_ce, 0_ce}; + bool expected[7] = {true, true, true, false, true, false, false}; + bool dst[7]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test floats + float src[7] = {5.0f, 2.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f}; + float expected[7] = {5.0f, 2.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f}; + float dst[7]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test double => long double + double src[7] = {5.0, 2.0, 1.0, 0.0, -1.0, 0.0, 0.0}; + long double expected[7] = {5.0L, 2.0L, 1.0L, 0.0L, -1.0L, 0.0L, 0.0L}; + long double dst[7]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test long double => double + long double src[7] = {5.0L, 2.0L, 1.0L, 0.0L, -1.0L, 0.0L, 0.0L}; + double expected[7] = {5.0, 2.0, 1.0, 0.0, -1.0, 0.0, 0.0}; + double dst[7]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test int* + int arr[5]{}; + int* src[5] = {&arr[0], &arr[1], nullptr, &arr[3], &arr[4]}; + int* expected[5] = {&arr[0], &arr[1], nullptr, &arr[3], &arr[4]}; + int* dst[5]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test void* + int arr[5]{}; + void* src[5] = {&arr[0], &arr[1], nullptr, &arr[3], &arr[4]}; + void* expected[5] = {&arr[0], &arr[1], nullptr, &arr[3], &arr[4]}; + void* dst[5]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test int* => void* + int arr[5]{}; + int* src[5] = {&arr[0], &arr[1], nullptr, &arr[3], &arr[4]}; + void* expected[5] = {&arr[0], &arr[1], nullptr, &arr[3], &arr[4]}; + void* dst[5]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test EmptyBase* + EmptyBase arr[5]{}; + EmptyBase* src[5] = {&arr[0], &arr[1], nullptr, &arr[3], &arr[4]}; + EmptyBase* expected[5] = {&arr[0], &arr[1], nullptr, &arr[3], &arr[4]}; + EmptyBase* dst[5]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test EmptyDerived* + EmptyDerived arr[5]{}; + EmptyDerived* src[5] = {&arr[0], &arr[1], nullptr, &arr[3], &arr[4]}; + EmptyDerived* expected[5] = {&arr[0], &arr[1], nullptr, &arr[3], &arr[4]}; + EmptyDerived* dst[5]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test EmptyDerived* => EmptyBase* + EmptyDerived arr[5]{}; + EmptyDerived* src[5] = {&arr[0], &arr[1], nullptr, &arr[3], &arr[4]}; + EmptyBase* expected[5] = {&arr[0], &arr[1], nullptr, &arr[3], &arr[4]}; + EmptyBase* dst[5]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test StatefulDerived* => StatefulBase* + StatefulDerived arr[5]{}; + StatefulDerived* src[5] = {&arr[0], &arr[1], nullptr, &arr[3], &arr[4]}; + StatefulBase* expected[5] = {&arr[0], &arr[1], nullptr, &arr[3], &arr[4]}; + StatefulBase* dst[5]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test StatefulDerived* => EmptyBase* + StatefulDerived arr[5]{}; + StatefulDerived* src[5] = {&arr[0], &arr[1], nullptr, &arr[3], &arr[4]}; + EmptyBase* expected[5] = {&arr[0], &arr[1], nullptr, &arr[3], &arr[4]}; + EmptyBase* dst[5]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test StatefulDerived2* => StatefulBase* + StatefulDerived2 arr[5]{}; + StatefulDerived2* src[5] = {&arr[0], &arr[1], nullptr, &arr[3], &arr[4]}; + StatefulBase* expected[5] = {&arr[0], &arr[1], nullptr, &arr[3], &arr[4]}; + StatefulBase* dst[5]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test StatefulDerived2* => EmptyBase* + StatefulDerived2 arr[5]{}; + StatefulDerived2* src[5] = {&arr[0], &arr[1], nullptr, &arr[3], &arr[4]}; + EmptyBase* expected[5] = {&arr[0], &arr[1], nullptr, &arr[3], &arr[4]}; + EmptyBase* dst[5]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test void(*)(int) + using fn_ptr = void (*)(int); + auto lambda1 = [](int) { abort(); }; + auto lambda2 = [](int) { exit(0); }; + fn_ptr src[5] = {lambda1, lambda2, nullptr, lambda1, nullptr}; + fn_ptr expected[5] = {lambda1, lambda2, nullptr, lambda1, nullptr}; + fn_ptr dst[5]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + +#ifndef __clang__ + if constexpr (is_convertible_v) { // Test void(*)(int) => void* (non-standard extension) + using fn_ptr = void (*)(int); + fn_ptr lambda1 = [](int) { abort(); }; + fn_ptr lambda2 = [](int) { exit(0); }; + fn_ptr src[5] = {lambda1, lambda2, nullptr, lambda1, nullptr}; + void* expected[5] = {lambda1, lambda2, nullptr, lambda1, nullptr}; + void* dst[5]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } +#endif // __clang__ + + { // Test int StatefulBase::* + using m_ptr = int StatefulBase::*; + m_ptr src[5] = {&StatefulBase::i, &StatefulBase::i, nullptr, &StatefulBase::i, nullptr}; + m_ptr expected[5] = {&StatefulBase::i, &StatefulBase::i, nullptr, &StatefulBase::i, nullptr}; + m_ptr dst[5]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test int StatefulDerived::* + using m_ptr = int StatefulDerived::*; + m_ptr src[5] = {&StatefulDerived::i, &StatefulBase::i, nullptr, &StatefulDerived::i, nullptr}; + m_ptr expected[5] = {&StatefulDerived::i, &StatefulBase::i, nullptr, &StatefulDerived::i, nullptr}; + m_ptr dst[5]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test int (StatefulBase::*)() + using mfn_ptr = int (StatefulBase::*)(); + mfn_ptr src[5] = {&StatefulBase::get_i, &StatefulBase::get_i2, nullptr, &StatefulBase::get_i, nullptr}; + mfn_ptr expected[5] = {&StatefulBase::get_i, &StatefulBase::get_i2, nullptr, &StatefulBase::get_i, nullptr}; + mfn_ptr dst[5]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test int (StatefulDerived::*)() + using mfn_ptr = int (StatefulDerived::*)(); + mfn_ptr src[5] = {&StatefulDerived::get_i, &StatefulBase::get_i2, nullptr, &StatefulDerived::get_i3, nullptr}; + mfn_ptr expected[5] = { + &StatefulDerived::get_i, &StatefulBase::get_i2, nullptr, &StatefulDerived::get_i3, nullptr}; + mfn_ptr dst[5]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + +#if _HAS_CXX17 + { // Test string_view + string_view src[5] = {"meow"sv, "purr"sv, ""sv, {}, "peppermint"sv}; + string_view expected[5] = {"meow"sv, "purr"sv, ""sv, {}, "peppermint"sv}; + string_view dst[5]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } +#endif // _HAS_CXX17 + +#if _HAS_CXX20 + { // Test span + int arr1[3] = {1, 2, 3}; + int arr2[5] = {4, 5, 6, 7, 8}; + int arr3[1] = {9}; + span src[5] = {arr1, arr3, arr2, {}, arr1}; + span expected[5] = {arr1, arr3, arr2, {}, arr1}; + span dst[5]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst), [](const auto& left, const auto& right) { + return left.data() == right.data() && left.size() == right.size(); + })); + } +#endif // _HAS_CXX20 + + { // Test TrivialStruct + TrivialStruct src[5] = {{8}, {3}, {6}, {2}, {1}}; + TrivialStruct expected[5] = {{8}, {3}, {6}, {2}, {1}}; + TrivialStruct dst[5]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test TriviallyCopyableStruct + TriviallyCopyableStruct src[5] = {{8}, {3}, {6}, {2}, {1}}; + TriviallyCopyableStruct expected[5] = {{8}, {3}, {6}, {2}, {1}}; + TriviallyCopyableStruct dst[5]{}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test TriviallyMovableStruct + TriviallyMovableStruct src[5] = {{8}, {3}, {6}, {2}, {1}}; + TriviallyMovableStruct expected[5] = {{8}, {3}, {6}, {2}, {1}}; + TriviallyMovableStruct dst[5]{}; + + copy(make_move_iterator(begin(src)), make_move_iterator(end(src)), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test vector + vector src = {3, 6, 4, 7, 3}; + vector expected = {3, 6, 4, 7, 3}; + vector dst = {0, 0, 0, 0, 0}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test array + array src = {3, 6, 4, 7, 3}; + array expected = {3, 6, 4, 7, 3}; + array dst = {0, 0, 0, 0, 0}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test copying from vector to array + vector src = {3, 6, 4, 7, 3}; + array expected = {3, 6, 4, 7, 3}; + array dst = {0, 0, 0, 0, 0}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test copying from array to vector + array src = {3, 6, 4, 7, 3}; + vector expected = {3, 6, 4, 7, 3}; + vector dst = {0, 0, 0, 0, 0}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test list + list src = {3, 6, 4, 7, 3}; + list expected = {3, 6, 4, 7, 3}; + list dst = {0, 0, 0, 0, 0}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test copying from vector to list + vector src = {3, 6, 4, 7, 3}; + list expected = {3, 6, 4, 7, 3}; + list dst = {0, 0, 0, 0, 0}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test copying from list to vector + list src = {3, 6, 4, 7, 3}; + vector expected = {3, 6, 4, 7, 3}; + vector dst = {0, 0, 0, 0, 0}; + + copy(begin(src), end(src), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test move_iterator + int src[5] = {3, 6, 4, 7, 3}; + int expected[5] = {3, 6, 4, 7, 3}; + int dst[5]{}; + + copy(make_move_iterator(begin(src)), make_move_iterator(end(src)), begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test double move_iterator + int src[5] = {3, 6, 4, 7, 3}; + int expected[5] = {3, 6, 4, 7, 3}; + int dst[5]{}; + + copy(make_move_iterator(make_move_iterator(begin(src))), make_move_iterator(make_move_iterator(end(src))), + begin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } +} + +template +void test_uninitialized_algorithms(CopyFn copy) { + test_algorithms(copy); + + { // Test const destination + int src[10] = {5, 7, 3, 4, 6, 4, 7, 1, 9, 5}; + int expected[10] = {5, 7, 3, 4, 6, 4, 7, 1, 9, 5}; + int dst[10]{}; + + copy(begin(src), end(src), cbegin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test move_iterator with const destination + int src[10] = {5, 7, 3, 4, 6, 4, 7, 1, 9, 5}; + int expected[10] = {5, 7, 3, 4, 6, 4, 7, 1, 9, 5}; + int dst[10]{}; + + copy(make_move_iterator(begin(src)), make_move_iterator(end(src)), cbegin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } + + { // Test vector with const destination + vector src = {3, 6, 4, 7, 3}; + vector expected = {3, 6, 4, 7, 3}; + vector dst = {0, 0, 0, 0, 0}; + + copy(begin(src), end(src), cbegin(dst)); + + assert(equal(begin(expected), end(expected), begin(dst))); + } +} + +int main() { + test_algorithms([](auto begin, auto end, auto out) { copy(begin, end, out); }); + test_algorithms([](auto begin, auto end, auto out) { copy_n(begin, distance(begin, end), out); }); + test_algorithms([](auto begin, auto end, auto out) { copy_backward(begin, end, next(out, distance(begin, end))); }); + + test_algorithms([](auto begin, auto end, auto out) { move(begin, end, out); }); + test_algorithms([](auto begin, auto end, auto out) { move_backward(begin, end, next(out, distance(begin, end))); }); + + test_uninitialized_algorithms([](auto begin, auto end, auto out) { uninitialized_copy(begin, end, out); }); + test_uninitialized_algorithms( + [](auto begin, auto end, auto out) { uninitialized_copy_n(begin, distance(begin, end), out); }); + +#if _HAS_CXX17 + test_uninitialized_algorithms([](auto begin, auto end, auto out) { uninitialized_move(begin, end, out); }); + test_uninitialized_algorithms( + [](auto begin, auto end, auto out) { uninitialized_move_n(begin, distance(begin, end), out); }); +#endif // _HAS_CXX17 + +#ifdef __cpp_lib_concepts + test_algorithms([](auto begin, auto end, auto out) { ranges::copy(begin, end, out); }); + test_algorithms([](auto begin, auto end, auto out) { ranges::copy_n(begin, distance(begin, end), out); }); + test_algorithms([](auto begin, auto end, auto out) { + // Unfortunately move_iterator is not a bidirectional_iterator, so fallback to unconstrained version + if constexpr (bidirectional_iterator) { + ranges::copy_backward(begin, end, next(out, distance(begin, end))); + } else { + copy_backward(begin, end, next(out, distance(begin, end))); + } + }); + + test_algorithms([](auto begin, auto end, auto out) { ranges::move(begin, end, out); }); + test_algorithms([](auto begin, auto end, auto out) { + // Unfortunately move_iterator is not a bidirectional_iterator, so fallback to unconstrained version + if constexpr (bidirectional_iterator) { + ranges::move_backward(begin, end, next(out, distance(begin, end))); + } else { + move_backward(begin, end, next(out, distance(begin, end))); + } + }); + + test_uninitialized_algorithms([](auto begin, auto end, auto out) { + ranges::uninitialized_copy(begin, end, out, next(out, distance(begin, end))); + }); + test_uninitialized_algorithms( + [](auto begin, auto end, auto out) { ranges::uninitialized_copy(begin, end, out, unreachable_sentinel); }); + test_uninitialized_algorithms([](auto begin, auto end, auto out) { + ranges::uninitialized_copy(begin, unreachable_sentinel, out, next(out, distance(begin, end))); + }); + + test_uninitialized_algorithms([](auto begin, auto end, auto out) { + ranges::uninitialized_copy_n(begin, distance(begin, end), out, next(out, distance(begin, end))); + }); + test_uninitialized_algorithms([](auto begin, auto end, auto out) { + ranges::uninitialized_copy_n(begin, distance(begin, end), out, unreachable_sentinel); + }); + + test_uninitialized_algorithms([](auto begin, auto end, auto out) { + ranges::uninitialized_move(begin, end, out, next(out, distance(begin, end))); + }); + test_uninitialized_algorithms( + [](auto begin, auto end, auto out) { ranges::uninitialized_move(begin, end, out, unreachable_sentinel); }); + test_uninitialized_algorithms([](auto begin, auto end, auto out) { + ranges::uninitialized_move(begin, unreachable_sentinel, out, next(out, distance(begin, end))); + }); + + test_uninitialized_algorithms([](auto begin, auto end, auto out) { + ranges::uninitialized_move_n(begin, distance(begin, end), out, next(out, distance(begin, end))); + }); + test_uninitialized_algorithms([](auto begin, auto end, auto out) { + ranges::uninitialized_move_n(begin, distance(begin, end), out, unreachable_sentinel); + }); +#endif // __cpp_lib_concepts +} diff --git a/tests/std/tests/GH_000431_equal_family/env.lst b/tests/std/tests/GH_000431_equal_family/env.lst new file mode 100644 index 00000000000..19f025bd0e6 --- /dev/null +++ b/tests/std/tests/GH_000431_equal_family/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_matrix.lst diff --git a/tests/std/tests/GH_000431_equal_family/test.cpp b/tests/std/tests/GH_000431_equal_family/test.cpp new file mode 100644 index 00000000000..0d7463fd45f --- /dev/null +++ b/tests/std/tests/GH_000431_equal_family/test.cpp @@ -0,0 +1,755 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wsign-compare" // comparison of integers of different signs: 'X' and 'Y' +#pragma clang diagnostic ignored "-Wenum-compare" // comparison of different enumeration types ('X' and 'Y') +#pragma clang diagnostic ignored \ + "-Wdeprecated-enum-compare" // comparison of different enumeration types ('X' and 'Y') is deprecated +#endif // __clang__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if _HAS_CXX20 +#include +#endif // _HAS_CXX20 + +#pragma warning(disable : 4242) // conversion from 'X' to 'Y', possible loss of data +#pragma warning(disable : 4244) // conversion from 'X' to 'Y', possible loss of data (Yes, duplicated message.) +#pragma warning(disable : 4365) // conversion from 'X' to 'Y', signed/unsigned mismatch +#pragma warning(disable : 4389) // '==': signed/unsigned mismatch +#pragma warning(disable : 4805) // '==': unsafe mix of type 'X' and type 'Y' in operation +#pragma warning(disable : 4984) // 'if constexpr' is a C++17 language extension +#pragma warning(disable : 5054) // operator '==': deprecated between enumerations of different types +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wc++17-extensions" // constexpr if is a C++17 extension +#endif // __clang__ + +using namespace std; + +#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) + +enum int_enum : int {}; +enum short_enum : short {}; +enum char_enum : char {}; +enum bool_enum : bool { false_e = false, true_e = true }; + +constexpr int_enum operator""_e(unsigned long long value) noexcept { + return static_cast(value); +} + +constexpr short_enum operator""_se(unsigned long long value) noexcept { + return static_cast(value); +} + +constexpr char_enum operator""_ce(unsigned long long value) noexcept { + return static_cast(value); +} + +enum bad_int_enum : int {}; +enum bad_short_enum : short {}; +enum bad_char_enum : char {}; +enum bad_bool_enum : bool { false_be = false, true_be = true }; + +bool operator==(bad_int_enum, bad_int_enum) { + return false; +} +bool operator==(int, bad_int_enum) { + return false; +} +bool operator==(bad_int_enum, int) { + return false; +} + +bool operator==(bad_short_enum, bad_short_enum) { + return false; +} +bool operator==(short, bad_short_enum) { + return false; +} +bool operator==(bad_short_enum, short) { + return false; +} + +bool operator==(bad_char_enum, bad_char_enum) { + return false; +} +bool operator==(char, bad_char_enum) { + return false; +} +bool operator==(bad_char_enum, char) { + return false; +} + +bool operator==(bad_bool_enum, bad_bool_enum) { + return false; +} +bool operator==(bool, bad_bool_enum) { + return false; +} +bool operator==(bad_bool_enum, bool) { + return false; +} + +constexpr bad_int_enum operator""_be(unsigned long long value) noexcept { + return static_cast(value); +} + +constexpr bad_short_enum operator""_bse(unsigned long long value) noexcept { + return static_cast(value); +} + +constexpr bad_char_enum operator""_bce(unsigned long long value) noexcept { + return static_cast(value); +} + +struct EmptyBase {}; + +struct EmptyDerived : EmptyBase {}; + +struct StatefulBase { + int i; + + int get_i() { + return i; + } + + int get_i2() { + return i * 2; + } +}; + +struct StatefulDerived : StatefulBase, EmptyBase { + int get_i3() { + return i * 3; + } +}; + +struct StatefulDerived2 : EmptyBase, StatefulBase {}; + +#ifdef __cpp_lib_is_pointer_interconvertible +STATIC_ASSERT(is_pointer_interconvertible_base_of_v); +STATIC_ASSERT(is_pointer_interconvertible_base_of_v); +STATIC_ASSERT(!is_pointer_interconvertible_base_of_v); +STATIC_ASSERT(is_pointer_interconvertible_base_of_v); +STATIC_ASSERT(is_pointer_interconvertible_base_of_v); +#endif // __cpp_lib_is_pointer_interconvertible + +template +void test_algorithms(EqualFn equal) { + + { // Test ints + int arr1[10] = {5, 7, 3, 4, 6, 4, 7, 1, 9, 5}; + int arr2[10] = {5, 7, 3, 4, 6, 4, 7, 1, 9, 5}; + int arr3[10] = {5, 7, 2, 4, 6, 4, 7, 1, 9, 5}; + int_enum arr4[10] = {5_e, 7_e, 3_e, 4_e, 6_e, 4_e, 7_e, 1_e, 9_e, 5_e}; + int_enum arr5[10] = {5_e, 7_e, 2_e, 4_e, 6_e, 4_e, 7_e, 1_e, 9_e, 5_e}; + bad_int_enum arr6[10] = {5_be, 7_be, 3_be, 4_be, 6_be, 4_be, 7_be, 1_be, 9_be, 5_be}; + + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to<>{})); + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to{})); + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to{})); + + assert(!equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to<>{})); + assert(!equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to{})); + assert(!equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to{})); + + assert(equal(begin(arr1), end(arr1), begin(arr4), end(arr4), equal_to<>{})); + assert(equal(begin(arr1), end(arr1), begin(arr4), end(arr4), equal_to{})); + assert(equal(begin(arr1), end(arr1), begin(arr4), end(arr4), equal_to{})); + + assert(!equal(begin(arr1), end(arr1), begin(arr5), end(arr5), equal_to<>{})); + assert(!equal(begin(arr1), end(arr1), begin(arr5), end(arr5), equal_to{})); + assert(!equal(begin(arr1), end(arr1), begin(arr5), end(arr5), equal_to{})); + + assert(!equal(begin(arr4), end(arr4), begin(arr5), end(arr5), equal_to<>{})); + assert(!equal(begin(arr4), end(arr4), begin(arr5), end(arr5), equal_to{})); + assert(!equal(begin(arr4), end(arr4), begin(arr5), end(arr5), equal_to{})); + + assert(equal(begin(arr3), end(arr3), begin(arr5), end(arr5), equal_to<>{})); + assert(equal(begin(arr3), end(arr3), begin(arr5), end(arr5), equal_to{})); + assert(equal(begin(arr3), end(arr3), begin(arr5), end(arr5), equal_to{})); + + assert(!equal(begin(arr1), end(arr1), begin(arr6), end(arr6), equal_to<>{})); + assert(equal(begin(arr1), end(arr1), begin(arr6), end(arr6), equal_to{})); + assert(equal(begin(arr1), end(arr1), begin(arr6), end(arr6), equal_to{})); + } + + { // Test int == long + int arr1[10] = {5, 7, 3, 4, 6, 4, 7, 1, 9, 5}; + long arr2[10] = {5, 7, 3, 4, 6, 4, 7, 1, 9, 5}; + long arr3[10] = {5, 7, 3, 4, 6, 4, 7, 1, 8, 5}; + + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to<>{})); + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to{})); + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to{})); + + assert(!equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to<>{})); + assert(!equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to{})); + assert(!equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to{})); + } + + { // Test long == unsigned int + int arr1[10] = {5, 7, 3, 4, 6, -4, 7, 1, 9, 5}; + unsigned int arr2[10] = {5, 7, 3, 4, 6, static_cast(-4), 7, 1, 9, 5}; + unsigned int arr3[10] = {4, 7, 3, 4, 6, static_cast(-4), 7, 1, 9, 5}; + + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to<>{})); + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to{})); + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to{})); + + assert(!equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to<>{})); + assert(!equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to{})); + assert(!equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to{})); + } + + { // Test short == int + short arr1[10] = {5, 7, 3, 4, 6, -4, 7, 1, 9, 5}; + int arr2[10] = {5, 7, 3, 4, 6, -4, 7, 1, 9, 5}; + short_enum arr3[10] = {5_se, 7_se, 3_se, 4_se, 6_se, static_cast(-4_se), 7_se, 1_se, 9_se, 5_se}; + int_enum arr4[10] = {5_e, 7_e, 3_e, 4_e, 6_e, static_cast(-4_e), 7_e, 1_e, 9_e, 5_e}; + + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to<>{})); + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to{})); + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to{})); + + assert(equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to<>{})); + assert(equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to{})); + assert(equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to{})); + + assert(equal(begin(arr1), end(arr1), begin(arr4), end(arr4), equal_to<>{})); + assert(equal(begin(arr1), end(arr1), begin(arr4), end(arr4), equal_to{})); + assert(equal(begin(arr1), end(arr1), begin(arr4), end(arr4), equal_to{})); + + assert(equal(begin(arr2), end(arr2), begin(arr3), end(arr3), equal_to<>{})); + assert(equal(begin(arr2), end(arr2), begin(arr3), end(arr3), equal_to{})); + assert(equal(begin(arr2), end(arr2), begin(arr3), end(arr3), equal_to{})); + + assert(equal(begin(arr3), end(arr3), begin(arr4), end(arr4), equal_to<>{})); + assert(equal(begin(arr3), end(arr3), begin(arr4), end(arr4), equal_to{})); + assert(equal(begin(arr3), end(arr3), begin(arr4), end(arr4), equal_to{})); + } + + { // Test short == unsigned short + short arr1[10] = {5, 7, 3, 4, 6, -4, 7, 1, 9, 5}; + unsigned short arr2[10] = {5, 7, 3, 4, 6, static_cast(-4), 7, 1, 9, 5}; + short_enum arr3[10] = {5_se, 7_se, 3_se, 4_se, 6_se, static_cast(-4_se), 7_se, 1_se, 9_se, 5_se}; + bad_short_enum arr4[10] = { + 5_bse, 7_bse, 3_bse, 4_bse, 6_bse, static_cast(-4_bse), 7_bse, 1_bse, 9_bse, 5_bse}; + + assert(!equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to<>{})); + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to{})); + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to{})); + + assert(!equal(begin(arr3), end(arr3), begin(arr2), end(arr2), equal_to<>{})); + assert(equal(begin(arr3), end(arr3), begin(arr2), end(arr2), equal_to{})); + assert(equal(begin(arr3), end(arr3), begin(arr2), end(arr2), equal_to{})); + + assert(equal(begin(arr3), end(arr3), begin(arr1), end(arr1), equal_to<>{})); + assert(equal(begin(arr3), end(arr3), begin(arr1), end(arr1), equal_to{})); + assert(equal(begin(arr3), end(arr3), begin(arr1), end(arr1), equal_to{})); + + assert(!equal(begin(arr1), end(arr1), begin(arr4), end(arr4), equal_to<>{})); + assert(equal(begin(arr1), end(arr1), begin(arr4), end(arr4), equal_to{})); + assert(equal(begin(arr1), end(arr1), begin(arr4), end(arr4), equal_to{})); + + assert(!equal(begin(arr3), end(arr3), begin(arr4), end(arr4), equal_to<>{})); + assert(equal(begin(arr3), end(arr3), begin(arr4), end(arr4), equal_to{})); + assert(equal(begin(arr3), end(arr3), begin(arr4), end(arr4), equal_to{})); + } + + { // Test bool + bool arr1[7] = {true, true, true, false, true, false, false}; + bool arr2[7] = {true, true, true, false, true, false, false}; + bool arr3[7] = {true, true, true, false, true, false, true}; + bool_enum arr4[7] = {true_e, true_e, true_e, false_e, true_e, false_e, false_e}; + bad_bool_enum arr5[7] = {true_be, true_be, true_be, false_be, true_be, false_be, false_be}; + + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to<>{})); + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to{})); + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to{})); + + assert(!equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to<>{})); + assert(!equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to{})); + assert(!equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to{})); + + assert(equal(begin(arr1), end(arr1), begin(arr4), end(arr4), equal_to<>{})); + assert(equal(begin(arr1), end(arr1), begin(arr4), end(arr4), equal_to{})); + assert(equal(begin(arr1), end(arr1), begin(arr4), end(arr4), equal_to{})); + + assert(!equal(begin(arr3), end(arr3), begin(arr4), end(arr4), equal_to<>{})); + assert(!equal(begin(arr3), end(arr3), begin(arr4), end(arr4), equal_to{})); + assert(!equal(begin(arr3), end(arr3), begin(arr4), end(arr4), equal_to{})); + + assert(!equal(begin(arr1), end(arr1), begin(arr5), end(arr5), equal_to<>{})); + assert(equal(begin(arr1), end(arr1), begin(arr5), end(arr5), equal_to{})); + assert(equal(begin(arr1), end(arr1), begin(arr5), end(arr5), equal_to{})); + + assert(!equal(begin(arr4), end(arr4), begin(arr5), end(arr5), equal_to<>{})); + assert(equal(begin(arr4), end(arr4), begin(arr5), end(arr5), equal_to{})); + assert(equal(begin(arr4), end(arr4), begin(arr5), end(arr5), equal_to{})); + } + + { // Test bool == char + bool arr1[7] = {true, true, true, false, true, false, false}; + char arr2[7] = {1, 1, 1, 0, 1, 0, 0}; + char arr3[7] = {5, 2, 1, 0, -1, 0, 0}; + bool_enum arr4[7] = {true_e, true_e, true_e, false_e, true_e, false_e, false_e}; + char_enum arr5[7] = {1_ce, 1_ce, 1_ce, 0_ce, 1_ce, 0_ce, 0_ce}; + + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to<>{})); + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to{})); + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to{})); + + assert(!equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to<>{})); + assert(!equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to{})); + assert(equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to{})); + assert(equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to{})); + + assert(equal(begin(arr1), end(arr1), begin(arr5), end(arr5), equal_to<>{})); + assert(equal(begin(arr1), end(arr1), begin(arr5), end(arr5), equal_to{})); + assert(equal(begin(arr1), end(arr1), begin(arr5), end(arr5), equal_to{})); + assert(equal(begin(arr1), end(arr1), begin(arr5), end(arr5), equal_to{})); + + assert(equal(begin(arr2), end(arr2), begin(arr4), end(arr4), equal_to<>{})); + assert(equal(begin(arr2), end(arr2), begin(arr4), end(arr4), equal_to{})); + assert(equal(begin(arr2), end(arr2), begin(arr4), end(arr4), equal_to{})); + + assert(!equal(begin(arr3), end(arr3), begin(arr4), end(arr4), equal_to<>{})); + assert(!equal(begin(arr3), end(arr3), begin(arr4), end(arr4), equal_to{})); + assert(equal(begin(arr3), end(arr3), begin(arr4), end(arr4), equal_to{})); + assert(equal(begin(arr3), end(arr3), begin(arr4), end(arr4), equal_to{})); + } + + { // Test floating points + int arr1[10] = {5, 7, 3, 4, 0, 4, 7, 1, 9, 5}; + float arr2[10] = {5.0f, 7.0f, 3.0f, 4.0f, 0.0f, 4.0f, 7.0f, 1.0f, 9.0f, 5.0f}; + float arr3[10] = {5.0f, 7.0f, 3.0f, 4.0f, -0.0f, 4.0f, 7.0f, 1.0f, 9.0f, 5.0f}; + double arr4[10] = {5.0, 7.0, 3.0, 4.0, 0.0, 4.0, 7.0, 1.0, 9.0, 5.0}; + double arr5[10] = {5.0, 7.0, 3.0, 4.0, -0.0, 4.0, 7.0, 1.0, 9.0, 5.0}; + float arr6[10] = {5.0f, 3.0f, 3.0f, 4.0f, -0.0f, 4.0f, 7.0f, 1.0f, 9.0f, 5.0f}; + double arr7[10] = {5.0, 3.0, 3.0, 4.0, -0.0, 4.0, 7.0, 1.0, 9.0, 5.0}; + + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to<>{})); + assert(equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to<>{})); + assert(equal(begin(arr1), end(arr1), begin(arr4), end(arr4), equal_to<>{})); + assert(equal(begin(arr1), end(arr1), begin(arr5), end(arr5), equal_to<>{})); + assert(!equal(begin(arr1), end(arr1), begin(arr6), end(arr6), equal_to<>{})); + assert(!equal(begin(arr1), end(arr1), begin(arr7), end(arr7), equal_to<>{})); + + assert(equal(begin(arr2), end(arr2), begin(arr3), end(arr3), equal_to<>{})); + assert(equal(begin(arr2), end(arr2), begin(arr4), end(arr4), equal_to<>{})); + assert(equal(begin(arr2), end(arr2), begin(arr5), end(arr5), equal_to<>{})); + assert(!equal(begin(arr2), end(arr2), begin(arr6), end(arr6), equal_to<>{})); + assert(!equal(begin(arr2), end(arr2), begin(arr7), end(arr7), equal_to<>{})); + + assert(equal(begin(arr3), end(arr3), begin(arr4), end(arr4), equal_to<>{})); + assert(equal(begin(arr3), end(arr3), begin(arr5), end(arr5), equal_to<>{})); + assert(!equal(begin(arr3), end(arr3), begin(arr6), end(arr6), equal_to<>{})); + assert(!equal(begin(arr3), end(arr3), begin(arr7), end(arr7), equal_to<>{})); + + assert(equal(begin(arr4), end(arr4), begin(arr5), end(arr5), equal_to<>{})); + assert(!equal(begin(arr4), end(arr4), begin(arr6), end(arr6), equal_to<>{})); + assert(!equal(begin(arr4), end(arr4), begin(arr7), end(arr7), equal_to<>{})); + + assert(!equal(begin(arr5), end(arr5), begin(arr6), end(arr6), equal_to<>{})); + assert(!equal(begin(arr5), end(arr5), begin(arr7), end(arr7), equal_to<>{})); + + assert(equal(begin(arr6), end(arr6), begin(arr7), end(arr7), equal_to<>{})); + } + + { // Test pointers + int objs[5]{}; + int* arr1[5] = {&objs[0], &objs[1], nullptr, &objs[3], &objs[4]}; + int* arr2[5] = {&objs[0], &objs[1], nullptr, &objs[3], &objs[4]}; + void* arr3[5] = {&objs[0], &objs[1], nullptr, &objs[3], &objs[4]}; + void* arr4[5] = {&objs[0], &objs[1], nullptr, &objs[3], &objs[4]}; + int* arr5[5] = {&objs[0], &objs[2], nullptr, &objs[3], &objs[4]}; + void* arr6[5] = {&objs[0], &objs[2], nullptr, &objs[3], &objs[4]}; + + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to<>{})); + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to{})); + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to{})); + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to{})); + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to{})); + + assert(equal(begin(arr3), end(arr3), begin(arr4), end(arr4), equal_to<>{})); + assert(equal(begin(arr3), end(arr3), begin(arr4), end(arr4), equal_to{})); + assert(equal(begin(arr3), end(arr3), begin(arr4), end(arr4), equal_to{})); + + assert(equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to<>{})); + assert(equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to{})); + assert(equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to{})); + + assert(equal(begin(arr2), end(arr2), begin(arr4), end(arr4), equal_to<>{})); + assert(equal(begin(arr2), end(arr2), begin(arr4), end(arr4), equal_to{})); + assert(equal(begin(arr2), end(arr2), begin(arr4), end(arr4), equal_to{})); + + assert(!equal(begin(arr1), end(arr1), begin(arr5), end(arr5), equal_to<>{})); + assert(!equal(begin(arr1), end(arr1), begin(arr5), end(arr5), equal_to{})); + assert(!equal(begin(arr1), end(arr1), begin(arr5), end(arr5), equal_to{})); + assert(!equal(begin(arr1), end(arr1), begin(arr5), end(arr5), equal_to{})); + assert(!equal(begin(arr1), end(arr1), begin(arr5), end(arr5), equal_to{})); + + assert(!equal(begin(arr3), end(arr3), begin(arr6), end(arr6), equal_to<>{})); + assert(!equal(begin(arr3), end(arr3), begin(arr6), end(arr6), equal_to{})); + assert(!equal(begin(arr3), end(arr3), begin(arr6), end(arr6), equal_to{})); + + assert(equal(begin(arr5), end(arr5), begin(arr6), end(arr6), equal_to<>{})); + assert(equal(begin(arr5), end(arr5), begin(arr6), end(arr6), equal_to{})); + assert(equal(begin(arr5), end(arr5), begin(arr6), end(arr6), equal_to{})); + } + + { // Test pointers to derived classes + EmptyDerived objs[5]{}; + + EmptyBase* arr1[5] = {&objs[0], nullptr, &objs[2], &objs[3], &objs[4]}; + EmptyBase* arr2[5] = {&objs[0], nullptr, &objs[2], &objs[3], &objs[4]}; + EmptyBase* arr3[5] = {&objs[0], nullptr, &objs[1], &objs[3], &objs[4]}; + EmptyDerived* arr4[5] = {&objs[0], nullptr, &objs[2], &objs[3], &objs[4]}; + EmptyDerived* arr5[5] = {&objs[0], nullptr, &objs[1], &objs[3], &objs[4]}; + + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to<>{})); + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to{})); + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to{})); + + assert(!equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to<>{})); + assert(!equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to{})); + assert(!equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to{})); + + assert(equal(begin(arr1), end(arr1), begin(arr4), end(arr4), equal_to<>{})); + assert(equal(begin(arr1), end(arr1), begin(arr4), end(arr4), equal_to{})); + assert(equal(begin(arr1), end(arr1), begin(arr4), end(arr4), equal_to{})); + + assert(!equal(begin(arr1), end(arr1), begin(arr5), end(arr5), equal_to<>{})); + assert(!equal(begin(arr1), end(arr1), begin(arr5), end(arr5), equal_to{})); + assert(!equal(begin(arr1), end(arr1), begin(arr5), end(arr5), equal_to{})); + + assert(equal(begin(arr3), end(arr3), begin(arr5), end(arr5), equal_to<>{})); + assert(equal(begin(arr3), end(arr3), begin(arr5), end(arr5), equal_to{})); + assert(equal(begin(arr3), end(arr3), begin(arr5), end(arr5), equal_to{})); + + assert(!equal(begin(arr4), end(arr4), begin(arr5), end(arr5), equal_to<>{})); + assert(!equal(begin(arr4), end(arr4), begin(arr5), end(arr5), equal_to{})); + assert(!equal(begin(arr4), end(arr4), begin(arr5), end(arr5), equal_to{})); + } + + { // Test pointers to derived classes more + StatefulDerived objs[5]{}; + + EmptyBase* arr1[5] = {&objs[0], nullptr, &objs[2], &objs[3], &objs[4]}; + EmptyBase* arr2[5] = {&objs[0], nullptr, &objs[1], &objs[3], &objs[4]}; + StatefulBase* arr3[5] = {&objs[0], nullptr, &objs[2], &objs[3], &objs[4]}; + StatefulBase* arr4[5] = {&objs[0], nullptr, &objs[1], &objs[3], &objs[4]}; + StatefulDerived* arr5[5] = {&objs[0], nullptr, &objs[2], &objs[3], &objs[4]}; + StatefulDerived* arr6[5] = {&objs[0], nullptr, &objs[1], &objs[3], &objs[4]}; + + assert(equal(begin(arr1), end(arr1), begin(arr5), end(arr5), equal_to<>{})); + assert(equal(begin(arr1), end(arr1), begin(arr5), end(arr5), equal_to{})); + assert(!equal(begin(arr1), end(arr1), begin(arr5), end(arr5), equal_to{})); + + assert(!equal(begin(arr1), end(arr1), begin(arr6), end(arr6), equal_to<>{})); + assert(!equal(begin(arr1), end(arr1), begin(arr6), end(arr6), equal_to{})); + assert(!equal(begin(arr1), end(arr1), begin(arr6), end(arr6), equal_to{})); + + assert(!equal(begin(arr2), end(arr2), begin(arr5), end(arr5), equal_to<>{})); + assert(!equal(begin(arr2), end(arr2), begin(arr5), end(arr5), equal_to{})); + assert(!equal(begin(arr2), end(arr2), begin(arr5), end(arr5), equal_to{})); + + assert(equal(begin(arr2), end(arr2), begin(arr6), end(arr6), equal_to<>{})); + assert(equal(begin(arr2), end(arr2), begin(arr6), end(arr6), equal_to{})); + assert(!equal(begin(arr2), end(arr2), begin(arr6), end(arr6), equal_to{})); + + assert(equal(begin(arr3), end(arr3), begin(arr5), end(arr5), equal_to<>{})); + assert(equal(begin(arr3), end(arr3), begin(arr5), end(arr5), equal_to{})); + assert(equal(begin(arr3), end(arr3), begin(arr5), end(arr5), equal_to{})); + + assert(!equal(begin(arr3), end(arr3), begin(arr6), end(arr6), equal_to<>{})); + assert(!equal(begin(arr3), end(arr3), begin(arr6), end(arr6), equal_to{})); + assert(!equal(begin(arr3), end(arr3), begin(arr6), end(arr6), equal_to{})); + + assert(!equal(begin(arr4), end(arr4), begin(arr5), end(arr5), equal_to<>{})); + assert(!equal(begin(arr4), end(arr4), begin(arr5), end(arr5), equal_to{})); + assert(!equal(begin(arr4), end(arr4), begin(arr5), end(arr5), equal_to{})); + + assert(equal(begin(arr4), end(arr4), begin(arr6), end(arr6), equal_to<>{})); + assert(equal(begin(arr4), end(arr4), begin(arr6), end(arr6), equal_to{})); + assert(equal(begin(arr4), end(arr4), begin(arr6), end(arr6), equal_to{})); + } + + { // Test pointers to derived classes even more + StatefulDerived2 objs[5]{}; + + EmptyBase* arr1[5] = {&objs[0], nullptr, &objs[2], &objs[3], &objs[4]}; + EmptyBase* arr2[5] = {&objs[0], nullptr, &objs[1], &objs[3], &objs[4]}; + StatefulBase* arr3[5] = {&objs[0], nullptr, &objs[2], &objs[3], &objs[4]}; + StatefulBase* arr4[5] = {&objs[0], nullptr, &objs[1], &objs[3], &objs[4]}; + StatefulDerived2* arr5[5] = {&objs[0], nullptr, &objs[2], &objs[3], &objs[4]}; + StatefulDerived2* arr6[5] = {&objs[0], nullptr, &objs[1], &objs[3], &objs[4]}; + + assert(equal(begin(arr1), end(arr1), begin(arr5), end(arr5), equal_to<>{})); + assert(equal(begin(arr1), end(arr1), begin(arr5), end(arr5), equal_to{})); + assert(equal(begin(arr1), end(arr1), begin(arr5), end(arr5), equal_to{})); + + assert(!equal(begin(arr1), end(arr1), begin(arr6), end(arr6), equal_to<>{})); + assert(!equal(begin(arr1), end(arr1), begin(arr6), end(arr6), equal_to{})); + assert(!equal(begin(arr1), end(arr1), begin(arr6), end(arr6), equal_to{})); + + assert(!equal(begin(arr2), end(arr2), begin(arr5), end(arr5), equal_to<>{})); + assert(!equal(begin(arr2), end(arr2), begin(arr5), end(arr5), equal_to{})); + assert(!equal(begin(arr2), end(arr2), begin(arr5), end(arr5), equal_to{})); + + assert(equal(begin(arr2), end(arr2), begin(arr6), end(arr6), equal_to<>{})); + assert(equal(begin(arr2), end(arr2), begin(arr6), end(arr6), equal_to{})); + assert(equal(begin(arr2), end(arr2), begin(arr6), end(arr6), equal_to{})); + + assert(equal(begin(arr3), end(arr3), begin(arr5), end(arr5), equal_to<>{})); + assert(equal(begin(arr3), end(arr3), begin(arr5), end(arr5), equal_to{})); + assert(equal(begin(arr3), end(arr3), begin(arr5), end(arr5), equal_to{})); + + assert(!equal(begin(arr3), end(arr3), begin(arr6), end(arr6), equal_to<>{})); + assert(!equal(begin(arr3), end(arr3), begin(arr6), end(arr6), equal_to{})); + assert(!equal(begin(arr3), end(arr3), begin(arr6), end(arr6), equal_to{})); + + assert(!equal(begin(arr4), end(arr4), begin(arr5), end(arr5), equal_to<>{})); + assert(!equal(begin(arr4), end(arr4), begin(arr5), end(arr5), equal_to{})); + assert(!equal(begin(arr4), end(arr4), begin(arr5), end(arr5), equal_to{})); + + assert(equal(begin(arr4), end(arr4), begin(arr6), end(arr6), equal_to<>{})); + assert(equal(begin(arr4), end(arr4), begin(arr6), end(arr6), equal_to{})); + assert(equal(begin(arr4), end(arr4), begin(arr6), end(arr6), equal_to{})); + } + + { // Test function pointers + using fn_ptr = void (*)(int); + fn_ptr lambda1 = [](int) { abort(); }; + fn_ptr lambda2 = [](int) { exit(0); }; + fn_ptr arr1[5] = {lambda1, lambda2, nullptr, lambda1, nullptr}; + fn_ptr arr2[5] = {lambda1, lambda2, nullptr, lambda1, nullptr}; + fn_ptr arr3[5] = {lambda1, lambda1, nullptr, lambda1, nullptr}; + + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to<>{})); + assert(!equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to<>{})); + +#ifndef __clang__ + if constexpr (is_convertible_v) { // Test void(*)(int) == void* (non-standard extension) + void* arr4[5] = {lambda1, lambda2, nullptr, lambda1, nullptr}; + void* arr5[5] = {lambda1, lambda1, nullptr, lambda1, nullptr}; + + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to{})); + assert(!equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to{})); + + assert(equal(begin(arr1), end(arr1), begin(arr4), end(arr4), equal_to<>{})); + assert(equal(begin(arr1), end(arr1), begin(arr4), end(arr4), equal_to{})); + assert(!equal(begin(arr1), end(arr1), begin(arr5), end(arr5), equal_to<>{})); + assert(!equal(begin(arr1), end(arr1), begin(arr5), end(arr5), equal_to{})); + assert(equal(begin(arr3), end(arr3), begin(arr5), end(arr5), equal_to<>{})); + assert(equal(begin(arr3), end(arr3), begin(arr5), end(arr5), equal_to{})); + } +#endif // __clang__ + } + + { // Test member object pointers + using m_ptr = int StatefulBase::*; + using m_ptr2 = int StatefulDerived::*; + m_ptr arr1[5] = {&StatefulBase::i, &StatefulBase::i, nullptr, &StatefulBase::i, nullptr}; + m_ptr arr2[5] = {&StatefulBase::i, &StatefulBase::i, nullptr, &StatefulBase::i, nullptr}; + m_ptr arr3[5] = {nullptr, &StatefulBase::i, nullptr, &StatefulBase::i, nullptr}; + m_ptr2 arr4[5] = {&StatefulDerived::i, &StatefulBase::i, nullptr, &StatefulDerived::i, nullptr}; + m_ptr2 arr5[5] = {nullptr, &StatefulBase::i, nullptr, &StatefulDerived::i, nullptr}; + + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to<>{})); + assert(!equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to<>{})); + assert(equal(begin(arr1), end(arr1), begin(arr4), end(arr4), equal_to<>{})); + assert(!equal(begin(arr1), end(arr1), begin(arr5), end(arr5), equal_to<>{})); + assert(equal(begin(arr3), end(arr3), begin(arr5), end(arr5), equal_to<>{})); + } + + { // Test member function pointers + using mfn_ptr = int (StatefulBase::*)(); + using mfn_ptr2 = int (StatefulDerived::*)(); + mfn_ptr arr1[5] = {&StatefulBase::get_i, &StatefulBase::get_i2, nullptr, &StatefulBase::get_i2, nullptr}; + mfn_ptr arr2[5] = {&StatefulBase::get_i, &StatefulBase::get_i2, nullptr, &StatefulBase::get_i2, nullptr}; + mfn_ptr arr3[5] = {&StatefulBase::get_i, &StatefulBase::get_i, nullptr, &StatefulBase::get_i2, nullptr}; + mfn_ptr2 arr4[5] = {&StatefulDerived::get_i, &StatefulBase::get_i2, nullptr, &StatefulDerived::get_i2, nullptr}; + mfn_ptr2 arr5[5] = {&StatefulDerived::get_i, &StatefulBase::get_i2, nullptr, &StatefulDerived::get_i2, nullptr}; + mfn_ptr2 arr6[5] = {&StatefulDerived::get_i, &StatefulBase::get_i2, nullptr, &StatefulDerived::get_i3, nullptr}; + mfn_ptr2 arr7[5] = {&StatefulDerived::get_i, &StatefulBase::get_i, nullptr, &StatefulDerived::get_i3, nullptr}; + +#if _HAS_CXX20 && defined(_MSC_VER) // TRANSITION, bug not yet reported +#define assert2(x) +#else // ^^^ ICE ^^^ / vvv no ICE vvv +#define assert2 assert +#endif // ^^^ no ICE ^^^ + + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to<>{})); + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to{})); + assert2(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to{})); + assert(!equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to<>{})); + assert(!equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to{})); + assert2(!equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to{})); + + assert(equal(begin(arr1), end(arr1), begin(arr4), end(arr4), equal_to<>{})); + assert2(equal(begin(arr1), end(arr1), begin(arr4), end(arr4), equal_to{})); + assert(!equal(begin(arr1), end(arr1), begin(arr6), end(arr6), equal_to<>{})); + assert2(!equal(begin(arr1), end(arr1), begin(arr6), end(arr6), equal_to{})); + assert(!equal(begin(arr1), end(arr1), begin(arr7), end(arr7), equal_to<>{})); + assert2(!equal(begin(arr1), end(arr1), begin(arr7), end(arr7), equal_to{})); + + assert(!equal(begin(arr3), end(arr3), begin(arr4), end(arr4), equal_to<>{})); + assert2(!equal(begin(arr3), end(arr3), begin(arr4), end(arr4), equal_to{})); + assert(!equal(begin(arr3), end(arr3), begin(arr6), end(arr6), equal_to<>{})); + assert2(!equal(begin(arr3), end(arr3), begin(arr6), end(arr6), equal_to{})); + assert(!equal(begin(arr3), end(arr3), begin(arr7), end(arr7), equal_to<>{})); + assert2(!equal(begin(arr3), end(arr3), begin(arr7), end(arr7), equal_to{})); + + assert(equal(begin(arr4), end(arr4), begin(arr5), end(arr5), equal_to<>{})); + assert(equal(begin(arr4), end(arr4), begin(arr5), end(arr5), equal_to{})); + assert(!equal(begin(arr4), end(arr4), begin(arr6), end(arr6), equal_to<>{})); + assert(!equal(begin(arr4), end(arr4), begin(arr6), end(arr6), equal_to{})); + assert(!equal(begin(arr4), end(arr4), begin(arr7), end(arr7), equal_to<>{})); + assert(!equal(begin(arr4), end(arr4), begin(arr7), end(arr7), equal_to{})); + + assert(!equal(begin(arr6), end(arr6), begin(arr7), end(arr7), equal_to<>{})); + assert(!equal(begin(arr6), end(arr6), begin(arr7), end(arr7), equal_to{})); + } + + { // Test vector + vector arr1 = {3, 6, 4, 7, 3}; + vector arr2 = {3, 6, 4, 7, 3}; + vector arr3 = {3, 6, 3, 7, 3}; + + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to<>{})); + assert(!equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to<>{})); + } + + { // Test array + array arr1 = {3, 6, 4, 7, 3}; + array arr2 = {3, 6, 4, 7, 3}; + array arr3 = {3, 6, 3, 7, 3}; + + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to<>{})); + assert(!equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to<>{})); + } + + { // Test comparing array and vector + vector arr1 = {3, 6, 4, 7, 3}; + array arr2 = {3, 6, 4, 7, 3}; + array arr3 = {3, 6, 3, 7, 3}; + vector arr4 = {3, 6, 3, 7, 3}; + + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to<>{})); + assert(equal(begin(arr3), end(arr3), begin(arr4), end(arr4), equal_to<>{})); + assert(!equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to<>{})); + assert(!equal(begin(arr2), end(arr2), begin(arr4), end(arr4), equal_to<>{})); + } + + { // Test list + list arr1 = {3, 6, 4, 7, 3}; + list arr2 = {3, 6, 4, 7, 3}; + list arr3 = {3, 6, 3, 7, 3}; + + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to<>{})); + assert(!equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to<>{})); + } + + { // Test comparing list and vector + vector arr1 = {3, 6, 4, 7, 3}; + list arr2 = {3, 6, 4, 7, 3}; + list arr3 = {3, 6, 3, 7, 3}; + vector arr4 = {3, 6, 3, 7, 3}; + + assert(equal(begin(arr1), end(arr1), begin(arr2), end(arr2), equal_to<>{})); + assert(equal(begin(arr3), end(arr3), begin(arr4), end(arr4), equal_to<>{})); + assert(!equal(begin(arr1), end(arr1), begin(arr3), end(arr3), equal_to<>{})); + assert(!equal(begin(arr2), end(arr2), begin(arr4), end(arr4), equal_to<>{})); + } +} + +int main() { + test_algorithms([](auto begin1, auto end1, auto begin2, auto end2, auto pred) { + return equal(begin1, end1, begin2, end2, pred); + }); + +#ifdef __cpp_lib_concepts + test_algorithms([](auto begin1, auto end1, auto begin2, auto end2, auto pred) { + return ranges::equal(begin1, end1, begin2, end2, pred); + }); +#endif // test_algorithms +} + +#ifdef __cpp_lib_concepts +// Also test GH-1523, in which std::equal didn't properly convert non-pointer contiguous iterators to pointers. +struct gh1523_iter { + // a contiguous_iterator that doesn't unwrap into a pointer + using iterator_concept = contiguous_iterator_tag; + using iterator_category = random_access_iterator_tag; + using value_type = int; + + int* ptr = nullptr; + + int& operator*() const { + return *ptr; + } + gh1523_iter& operator++() { + return *this; + } + gh1523_iter operator++(int) { + return {}; + } + gh1523_iter& operator--() { + return *this; + } + gh1523_iter operator--(int) { + return {}; + } + ptrdiff_t operator-(const gh1523_iter&) const { + return 0; + } + auto operator<=>(const gh1523_iter&) const = default; + gh1523_iter& operator-=(ptrdiff_t) { + return *this; + } + gh1523_iter operator-(ptrdiff_t) const { + return {}; + } + gh1523_iter& operator+=(ptrdiff_t) { + return *this; + } + gh1523_iter operator+(ptrdiff_t) const { + return {}; + } + friend gh1523_iter operator+(ptrdiff_t, const gh1523_iter&) { + return {}; + } + int& operator[](ptrdiff_t) const { + return *ptr; + } +}; + +template <> +struct pointer_traits { + using pointer = gh1523_iter; + using element_type = int; + using difference_type = ptrdiff_t; + + static int* to_address(const pointer&) noexcept { + return nullptr; + } +}; +static_assert(contiguous_iterator); + +void test_gh1523() { + (void) equal(gh1523_iter{}, gh1523_iter{}, gh1523_iter{}, gh1523_iter{}); +} +#endif // __cpp_lib_concepts diff --git a/tests/std/tests/GH_000431_equal_memcmp_is_safe/env.lst b/tests/std/tests/GH_000431_equal_memcmp_is_safe/env.lst new file mode 100644 index 00000000000..19f025bd0e6 --- /dev/null +++ b/tests/std/tests/GH_000431_equal_memcmp_is_safe/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_matrix.lst diff --git a/tests/std/tests/GH_000431_equal_memcmp_is_safe/test.compile.pass.cpp b/tests/std/tests/GH_000431_equal_memcmp_is_safe/test.compile.pass.cpp new file mode 100644 index 00000000000..4c6f5c32988 --- /dev/null +++ b/tests/std/tests/GH_000431_equal_memcmp_is_safe/test.compile.pass.cpp @@ -0,0 +1,510 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) + +template > +void assert_equal_memcmp_is_safe() { + // 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); +} + +template +void test_equal_memcmp_is_safe_for_iterators() { + // Const iterators should not change the answer + assert_equal_memcmp_is_safe(); + assert_equal_memcmp_is_safe(); + assert_equal_memcmp_is_safe(); + assert_equal_memcmp_is_safe(); +} + +template +void test_equal_memcmp_is_safe_for_pred_helper() { + test_equal_memcmp_is_safe_for_iterators(); +} + +template +void test_equal_memcmp_is_safe_for_pred() { + test_equal_memcmp_is_safe_for_pred_helper(); + + // No volatile + test_equal_memcmp_is_safe_for_pred_helper(); + test_equal_memcmp_is_safe_for_pred_helper(); + test_equal_memcmp_is_safe_for_pred_helper(); +} + +template +void test_equal_memcmp_is_safe_for_types() { + test_equal_memcmp_is_safe_for_pred>(); + + test_equal_memcmp_is_safe_for_pred>>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + + test_equal_memcmp_is_safe_for_pred>(); + +#ifdef __cpp_lib_concepts + test_equal_memcmp_is_safe_for_pred(); + test_equal_memcmp_is_safe_for_pred(); +#endif // __cpp_lib_concepts + + auto lambda = [](auto&&, auto&&) { return false; }; + test_equal_memcmp_is_safe_for_pred(); +} + +template +void test_equal_memcmp_is_safe_for_pointers() { + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); +} + +template +void test_equal_memcmp_is_safe_for_pointers_with_volatile() { + test_equal_memcmp_is_safe_for_pointers(); + test_equal_memcmp_is_safe_for_pointers(); + test_equal_memcmp_is_safe_for_pointers(); + test_equal_memcmp_is_safe_for_pointers(); +} + +template > +void test_equal_memcmp_is_safe_for_containers() { + using It1 = typename Container1::iterator; + using ConstIt1 = typename Container1::const_iterator; + using It2 = typename Container2::iterator; + using ConstIt2 = typename Container2::const_iterator; + test_equal_memcmp_is_safe_for_iterators(); +} + +enum int_enum : int {}; +enum short_enum : short {}; +enum char_enum : char {}; +enum bool_enum : bool {}; + +enum class int_enum_class : int {}; +enum class short_enum_class : short {}; +enum class char_enum_class : char {}; +enum class bool_enum_class : bool {}; + +struct EmptyBase {}; + +struct EmptyDerived : EmptyBase {}; + +struct EmptyPrivatelyDerived : private EmptyBase {}; + +struct StatefulBase { + int i; + + bool operator==(const StatefulBase& right) const noexcept { + return i == right.i; + } +}; + +struct StatefulDerived : StatefulBase, EmptyBase {}; + +struct StatefulPrivatelyDerived : private StatefulBase, private EmptyBase {}; + +struct StatefulDerived2 : EmptyBase, StatefulBase {}; + +struct StatefulPrivatelyDerived2 : private EmptyBase, private StatefulBase {}; + +#ifdef __cpp_lib_is_pointer_interconvertible +STATIC_ASSERT(is_pointer_interconvertible_base_of_v); +STATIC_ASSERT(is_pointer_interconvertible_base_of_v); +STATIC_ASSERT(is_pointer_interconvertible_base_of_v); +STATIC_ASSERT(!is_pointer_interconvertible_base_of_v); +STATIC_ASSERT(is_pointer_interconvertible_base_of_v); +STATIC_ASSERT(!is_pointer_interconvertible_base_of_v); +STATIC_ASSERT(is_pointer_interconvertible_base_of_v); +STATIC_ASSERT(is_pointer_interconvertible_base_of_v); +STATIC_ASSERT(is_pointer_interconvertible_base_of_v); +STATIC_ASSERT(is_pointer_interconvertible_base_of_v); +#endif // __cpp_lib_is_pointer_interconvertible + +void equal_memcmp_is_safe_test_cases() { + // Test identical integrals + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); +#ifdef __cpp_lib_char8_t + test_equal_memcmp_is_safe_for_types(); +#endif // __cpp_lib_char8_t + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + + // Don't allow diffrent size integrals + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + + // Signedness must be the same if usual arithmetic conversions are not bits-preserving + test_equal_memcmp_is_safe_for_types, char, signed char>(); + test_equal_memcmp_is_safe_for_types, signed char, char>(); + test_equal_memcmp_is_safe_for_types, char, unsigned char>(); + test_equal_memcmp_is_safe_for_types, unsigned char, char>(); +#ifdef __cpp_lib_char8_t + test_equal_memcmp_is_safe_for_types, char, char8_t>(); + test_equal_memcmp_is_safe_for_types, char8_t, char>(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); +#endif // __cpp_lib_char8_t + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + + // But if UACs don't change bits the signedness can differ +#ifdef __cpp_lib_char8_t + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); +#endif // __cpp_lib_char8_t + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + + // It can also differ if we explicitly convert them + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); +#ifdef __cpp_lib_char8_t + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); +#endif // __cpp_lib_char8_t + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + + // It is safe to use memcmp between bool and other integral types with the same size because we don't care about + // representations other than 0 and 1 + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + + // Need to be careful when pred is equal_to or equal_to + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + + // Don't allow enums and enum classes + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + + // But allow enums if we explicitly convert them + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + + // Again need to be careful with equal_to and equal_to + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + + // Enum classes are not convertible + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + +#ifdef __cpp_lib_byte + // Test std::byte + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); +#endif // __cpp_lib_byte + + // Don't allow floating point + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + + // Test pointers + test_equal_memcmp_is_safe_for_pointers_with_volatile(); + test_equal_memcmp_is_safe_for_pointers_with_volatile(); + test_equal_memcmp_is_safe_for_pointers_with_volatile(); + test_equal_memcmp_is_safe_for_pointers_with_volatile(); + + test_equal_memcmp_is_safe_for_pointers(); + test_equal_memcmp_is_safe_for_pointers(); + + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + test_equal_memcmp_is_safe_for_pred>(); + + // Test pointers to derived classes + constexpr bool enable_derived_to_base = +#ifdef __cpp_lib_is_pointer_interconvertible + true +#else // ^^^ __cpp_lib_is_pointer_interconvertible ^^^ / vvv !__cpp_lib_is_pointer_interconvertible vvv + false +#endif // ^^^ !__cpp_lib_is_pointer_interconvertible ^^^ + ; + + test_equal_memcmp_is_safe_for_pointers(); + test_equal_memcmp_is_safe_for_pointers(); + test_equal_memcmp_is_safe_for_pointers(); + test_equal_memcmp_is_safe_for_pointers(); + test_equal_memcmp_is_safe_for_pointers(); + test_equal_memcmp_is_safe_for_pointers(); + test_equal_memcmp_is_safe_for_pointers(); + test_equal_memcmp_is_safe_for_pointers(); + test_equal_memcmp_is_safe_for_pointers(); + test_equal_memcmp_is_safe_for_pointers(); + test_equal_memcmp_is_safe_for_pointers(); + test_equal_memcmp_is_safe_for_pointers(); + + // Don't allow privately derived + test_equal_memcmp_is_safe_for_pointers(); + test_equal_memcmp_is_safe_for_pointers(); + test_equal_memcmp_is_safe_for_pointers(); + test_equal_memcmp_is_safe_for_pointers(); + test_equal_memcmp_is_safe_for_pointers(); + test_equal_memcmp_is_safe_for_pointers(); + test_equal_memcmp_is_safe_for_pointers(); + test_equal_memcmp_is_safe_for_pointers(); + + // Test function pointers + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + + // Converting from function pointers to void pointers is a non-standard extension + test_equal_memcmp_is_safe_for_types, void (*)(int), void*>(); + test_equal_memcmp_is_safe_for_types, void*, void (*)(int)>(); + + // Don't allow member object pointers + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + + // Don't allow member function pointers + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + test_equal_memcmp_is_safe_for_types(); + + // Don't allow user-defined types + test_equal_memcmp_is_safe_for_types(); + + // Test _Char_traits_eq + test_equal_memcmp_is_safe_for_pred>>(); + test_equal_memcmp_is_safe_for_pred>>(); +#ifdef __cpp_lib_char8_t + test_equal_memcmp_is_safe_for_pred>>(); +#endif // __cpp_lib_char8_t + test_equal_memcmp_is_safe_for_pred>>(); + test_equal_memcmp_is_safe_for_pred>>(); + + // Test different containers +#ifdef __cpp_lib_concepts + test_equal_memcmp_is_safe_for_containers, vector>(); + test_equal_memcmp_is_safe_for_containers, array>(); + test_equal_memcmp_is_safe_for_containers, array>(); + test_equal_memcmp_is_safe_for_containers, vector>(); +#endif // __cpp_lib_concepts + + test_equal_memcmp_is_safe_for_containers, list>(); + test_equal_memcmp_is_safe_for_containers, list>(); + test_equal_memcmp_is_safe_for_containers, vector>(); + +#ifdef __cpp_lib_concepts + // Test counted_iterator + assert_equal_memcmp_is_safe, int*>(); + assert_equal_memcmp_is_safe>(); + assert_equal_memcmp_is_safe, counted_iterator>(); +#endif // __cpp_lib_concepts +} + +int main() {} // COMPILE-ONLY diff --git a/tests/std/tests/GH_000431_iter_copy_move_cat/env.lst b/tests/std/tests/GH_000431_iter_copy_move_cat/env.lst new file mode 100644 index 00000000000..19f025bd0e6 --- /dev/null +++ b/tests/std/tests/GH_000431_iter_copy_move_cat/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_matrix.lst diff --git a/tests/std/tests/GH_000431_iter_copy_move_cat/test.compile.pass.cpp b/tests/std/tests/GH_000431_iter_copy_move_cat/test.compile.pass.cpp new file mode 100644 index 00000000000..209cfb17450 --- /dev/null +++ b/tests/std/tests/GH_000431_iter_copy_move_cat/test.compile.pass.cpp @@ -0,0 +1,483 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include + +#if _HAS_CXX17 +#include +#endif // _HAS_CXX17 + +#if _HAS_CXX20 +#include +#endif // _HAS_CXX20 + +#pragma warning(disable : 4984) // 'if constexpr' is a C++17 language extension +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wc++17-extensions" // constexpr if is a C++17 extension +#endif // __clang__ + +using namespace std; + +#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) + +template +void assert_iter_cat() { + STATIC_ASSERT(IterCat::_Trivially_constructible == ExpectedConstructible); + STATIC_ASSERT(IterCat::_Trivially_assignable == ExpectedAssignable); + STATIC_ASSERT(IterCat::_Trivially_constructible_and_assignable == (ExpectedConstructible && ExpectedAssignable)); +} + +template +void test_iter_cat() { + assert_iter_cat>(); + assert_iter_cat, DestIt>>(); + assert_iter_cat>(); + assert_iter_cat, DestIt>>(); +} + +template +void test_iter_cat_for_iterators() { + test_iter_cat(); + test_iter_cat(); + + // Assigning to ConstDestIt doesn't make any sense but constructing is fine + test_iter_cat(); + test_iter_cat(); +} + +template +void test_iter_cat_for_types() { + test_iter_cat_for_iterators(); +} + +template +void test_iter_cat_for_trivially_copyable_types() { + test_iter_cat_for_types(); +} + +template +void test_iter_cat_for_integrals_helper() { + enum Enum1 : Int1 {}; + enum Enum2 : Int2 {}; + enum class EnumClass1 : Int1 {}; + enum class EnumClass2 : Int2 {}; + + test_iter_cat_for_trivially_copyable_types(); + + test_iter_cat_for_trivially_copyable_types(); + + if constexpr (is_same_v) { + // Allow enum and enum class + test_iter_cat_for_trivially_copyable_types(); + test_iter_cat_for_trivially_copyable_types(); + } + + // Don't allow conversion from int to enum (since that requires a cast): + test_iter_cat_for_trivially_copyable_types(); + + // Similarly, don't allow enum class types to be implicitly converted to/from integral types + test_iter_cat_for_trivially_copyable_types(); + test_iter_cat_for_trivially_copyable_types(); + + // Don't allow conversions between enums + test_iter_cat_for_trivially_copyable_types(); + test_iter_cat_for_trivially_copyable_types(); + test_iter_cat_for_trivially_copyable_types(); + test_iter_cat_for_trivially_copyable_types(); +} + +template +void test_iter_cat_for_integrals() { + test_iter_cat_for_integrals_helper(); + + if constexpr (!is_same_v) { + using UInt1 = make_unsigned_t; + + // Signedness shouldn't change answer + test_iter_cat_for_integrals_helper(); + } + + if constexpr (!is_same_v) { + using UInt2 = make_unsigned_t; + + // Signedness shouldn't change answer + test_iter_cat_for_integrals_helper(); + } + + if constexpr (!is_same_v && !is_same_v) { + using UInt1 = make_unsigned_t; + using UInt2 = make_unsigned_t; + + // Signedness shouldn't change answer + test_iter_cat_for_integrals_helper(); + } +} + +template +void test_iter_cat_for_char_types() { + using SInt1 = make_signed_t; + using UInt1 = make_unsigned_t; + using SInt2 = make_signed_t; + using UInt2 = make_unsigned_t; + + test_iter_cat_for_integrals_helper(); + test_iter_cat_for_integrals_helper(); + test_iter_cat_for_integrals_helper(); + test_iter_cat_for_integrals_helper(); + test_iter_cat_for_integrals_helper(); +} + +template +void test_iter_cat_for_pointers() { + test_iter_cat_for_trivially_copyable_types(); + test_iter_cat_for_trivially_copyable_types(); + test_iter_cat_for_trivially_copyable_types(); + + test_iter_cat_for_trivially_copyable_types(); +} + +template +void test_iter_cat_for_pointers_with_volatile() { + test_iter_cat_for_pointers(); + test_iter_cat_for_pointers(); + test_iter_cat_for_pointers(); + + test_iter_cat_for_pointers(); +} + +template +void test_iter_cat_for_containers() { + using SourceIt = typename SourceContainer::iterator; + using ConstSourceIt = typename SourceContainer::const_iterator; + using DestIt = typename DestContainer::iterator; + using ConstDestIt = typename DestContainer::const_iterator; + test_iter_cat_for_iterators(); +} + +struct TrivialStruct { + int i; +}; +STATIC_ASSERT(is_trivial_v); + +struct TriviallyCopyableStruct { + int i; + TriviallyCopyableStruct(); +}; +STATIC_ASSERT(is_trivially_copyable_v); +STATIC_ASSERT(!is_trivial_v); + +struct TriviallyMovableStruct { + int i; + TriviallyMovableStruct(); + TriviallyMovableStruct(const TriviallyMovableStruct&) = delete; + TriviallyMovableStruct(TriviallyMovableStruct&&) = default; + TriviallyMovableStruct& operator=(const TriviallyMovableStruct&) = delete; + TriviallyMovableStruct& operator=(TriviallyMovableStruct&&) = default; +}; +STATIC_ASSERT(is_trivially_copyable_v); +STATIC_ASSERT(!is_trivial_v); + +struct TriviallyConstructibleStruct { + int i; + TriviallyConstructibleStruct(); + TriviallyConstructibleStruct(const TriviallyConstructibleStruct&) = default; + TriviallyConstructibleStruct(TriviallyConstructibleStruct&&) = default; + + TriviallyConstructibleStruct& operator=(const TriviallyConstructibleStruct&); + TriviallyConstructibleStruct& operator=(TriviallyConstructibleStruct&&); +}; +STATIC_ASSERT(is_trivially_copy_constructible_v); +STATIC_ASSERT(is_trivially_move_constructible_v); +STATIC_ASSERT(!is_trivially_copy_assignable_v); +STATIC_ASSERT(!is_trivially_move_assignable_v); +STATIC_ASSERT(!is_trivially_copyable_v); +STATIC_ASSERT(!is_trivial_v); + +struct TriviallyAssignableStruct { + int i; + TriviallyAssignableStruct(); + TriviallyAssignableStruct(const TriviallyAssignableStruct&); + TriviallyAssignableStruct(TriviallyAssignableStruct&&); + + TriviallyAssignableStruct& operator=(const TriviallyAssignableStruct&) = default; + TriviallyAssignableStruct& operator=(TriviallyAssignableStruct&&) = default; +}; +STATIC_ASSERT(!is_trivially_copy_constructible_v); +STATIC_ASSERT(!is_trivially_move_constructible_v); +STATIC_ASSERT(is_trivially_copy_assignable_v); +STATIC_ASSERT(is_trivially_move_assignable_v); +STATIC_ASSERT(!is_trivially_copyable_v); +STATIC_ASSERT(!is_trivial_v); + +struct TriviallyCopyConstructibleStruct { + int i; + TriviallyCopyConstructibleStruct(); + TriviallyCopyConstructibleStruct(const TriviallyCopyConstructibleStruct&) = default; + TriviallyCopyConstructibleStruct(TriviallyCopyConstructibleStruct&&); + + TriviallyCopyConstructibleStruct& operator=(const TriviallyCopyConstructibleStruct&); + TriviallyCopyConstructibleStruct& operator=(TriviallyCopyConstructibleStruct&&); +}; +STATIC_ASSERT(is_trivially_copy_constructible_v); +STATIC_ASSERT(!is_trivially_move_constructible_v); +STATIC_ASSERT(!is_trivially_copy_assignable_v); +STATIC_ASSERT(!is_trivially_move_assignable_v); +STATIC_ASSERT(!is_trivially_copyable_v); +STATIC_ASSERT(!is_trivial_v); + +struct TriviallyCopyAssignableStruct { + int i; + TriviallyCopyAssignableStruct(); + TriviallyCopyAssignableStruct(const TriviallyCopyAssignableStruct&); + TriviallyCopyAssignableStruct(TriviallyCopyAssignableStruct&&); + + TriviallyCopyAssignableStruct& operator=(const TriviallyCopyAssignableStruct&) = default; + + TriviallyCopyAssignableStruct& operator=(TriviallyCopyAssignableStruct&&); +}; +STATIC_ASSERT(!is_trivially_copy_constructible_v); +STATIC_ASSERT(!is_trivially_move_constructible_v); +STATIC_ASSERT(is_trivially_copy_assignable_v); +STATIC_ASSERT(!is_trivially_move_assignable_v); +STATIC_ASSERT(!is_trivially_copyable_v); +STATIC_ASSERT(!is_trivial_v); + +struct TriviallyMoveConstructibleStruct { + int i; + TriviallyMoveConstructibleStruct(); + TriviallyMoveConstructibleStruct(const TriviallyMoveConstructibleStruct&); + TriviallyMoveConstructibleStruct(TriviallyMoveConstructibleStruct&&) = default; + + TriviallyMoveConstructibleStruct& operator=(const TriviallyMoveConstructibleStruct&); + TriviallyMoveConstructibleStruct& operator=(TriviallyMoveConstructibleStruct&&); +}; +STATIC_ASSERT(!is_trivially_copy_constructible_v); +STATIC_ASSERT(is_trivially_move_constructible_v); +STATIC_ASSERT(!is_trivially_copy_assignable_v); +STATIC_ASSERT(!is_trivially_move_assignable_v); +STATIC_ASSERT(!is_trivially_copyable_v); +STATIC_ASSERT(!is_trivial_v); + +struct TriviallyMoveAssignableStruct { + int i; + TriviallyMoveAssignableStruct(); + TriviallyMoveAssignableStruct(const TriviallyMoveAssignableStruct&); + TriviallyMoveAssignableStruct(TriviallyMoveAssignableStruct&&); + + TriviallyMoveAssignableStruct& operator=(const TriviallyMoveAssignableStruct&); + TriviallyMoveAssignableStruct& operator=(TriviallyMoveAssignableStruct&&) = default; +}; +STATIC_ASSERT(!is_trivially_copy_constructible_v); +STATIC_ASSERT(!is_trivially_move_constructible_v); +STATIC_ASSERT(!is_trivially_copy_assignable_v); +STATIC_ASSERT(is_trivially_move_assignable_v); +STATIC_ASSERT(!is_trivially_copyable_v); +STATIC_ASSERT(!is_trivial_v); + +struct EmptyBase {}; + +struct EmptyDerived : EmptyBase {}; + +struct EmptyPrivatelyDerived : private EmptyBase {}; + +struct StatefulBase { + int i; +}; + +struct StatefulDerived : StatefulBase, EmptyBase {}; + +struct StatefulPrivatelyDerived : private StatefulBase, private EmptyBase {}; + +struct StatefulDerived2 : EmptyBase, StatefulBase {}; + +struct StatefulPrivatelyDerived2 : private EmptyBase, private StatefulBase {}; + +#ifdef __cpp_lib_is_pointer_interconvertible +STATIC_ASSERT(is_pointer_interconvertible_base_of_v); +STATIC_ASSERT(is_pointer_interconvertible_base_of_v); +STATIC_ASSERT(is_pointer_interconvertible_base_of_v); +STATIC_ASSERT(!is_pointer_interconvertible_base_of_v); +STATIC_ASSERT(is_pointer_interconvertible_base_of_v); +STATIC_ASSERT(!is_pointer_interconvertible_base_of_v); +STATIC_ASSERT(is_pointer_interconvertible_base_of_v); +STATIC_ASSERT(is_pointer_interconvertible_base_of_v); +STATIC_ASSERT(is_pointer_interconvertible_base_of_v); +STATIC_ASSERT(is_pointer_interconvertible_base_of_v); +#endif // __cpp_lib_is_pointer_interconvertible + + +void iter_cat_test_cases() { + + // Test compatible integral types + test_iter_cat_for_integrals(); + test_iter_cat_for_integrals(); + test_iter_cat_for_integrals(); + test_iter_cat_for_integrals(); + test_iter_cat_for_integrals(); + test_iter_cat_for_integrals(); + + test_iter_cat_for_integrals(); + test_iter_cat_for_integrals(); + + test_iter_cat_for_char_types(); + test_iter_cat_for_char_types(); +#ifdef __cpp_lib_char8_t + test_iter_cat_for_char_types(); +#endif // __cpp_lib_char8_t + test_iter_cat_for_char_types(); + test_iter_cat_for_char_types(); + + // Test bool conversions + test_iter_cat_for_integrals(); + test_iter_cat_for_integrals(); + test_iter_cat_for_integrals(); + test_iter_cat_for_integrals(); + + // Don't allow conversions if size differ + test_iter_cat_for_integrals(); + test_iter_cat_for_integrals(); + test_iter_cat_for_integrals(); + test_iter_cat_for_integrals(); + +#ifdef __cpp_lib_byte + // Test std::byte + test_iter_cat_for_trivially_copyable_types(); +#endif // __cpp_lib_byte + + // Test floating point + test_iter_cat_for_trivially_copyable_types(); + test_iter_cat_for_trivially_copyable_types(); + test_iter_cat_for_trivially_copyable_types(); + test_iter_cat_for_trivially_copyable_types(); + + test_iter_cat_for_trivially_copyable_types(); + test_iter_cat_for_trivially_copyable_types(); + + // Test pointers + test_iter_cat_for_pointers_with_volatile(); + test_iter_cat_for_pointers(); + test_iter_cat_for_pointers(); + + // Test void conversions + test_iter_cat_for_pointers_with_volatile(); + test_iter_cat_for_pointers_with_volatile(); + test_iter_cat_for_pointers(); + + // Test pointers to derived classes + constexpr bool enable_derived_to_base = +#ifdef __cpp_lib_is_pointer_interconvertible + true +#else // ^^^ __cpp_lib_is_pointer_interconvertible ^^^ / vvv !__cpp_lib_is_pointer_interconvertible vvv + false +#endif // ^^^ !__cpp_lib_is_pointer_interconvertible ^^^ + ; + + test_iter_cat_for_pointers(); + test_iter_cat_for_pointers(); + test_iter_cat_for_pointers(); + test_iter_cat_for_pointers(); + test_iter_cat_for_pointers(); + test_iter_cat_for_pointers(); + test_iter_cat_for_pointers(); + test_iter_cat_for_pointers(); + + // Don't allow privately derived + test_iter_cat_for_pointers(); + test_iter_cat_for_pointers(); + test_iter_cat_for_pointers(); + test_iter_cat_for_pointers(); + + // Test function pointers + test_iter_cat_for_trivially_copyable_types(); + test_iter_cat_for_trivially_copyable_types(); + + // Converting from function pointers to void pointers is a non-standard extension + test_iter_cat_for_trivially_copyable_types, void (*)(int), void*>(); + test_iter_cat_for_trivially_copyable_types(); + + // Test member object pointers + test_iter_cat_for_trivially_copyable_types(); + test_iter_cat_for_trivially_copyable_types(); + test_iter_cat_for_trivially_copyable_types(); + test_iter_cat_for_trivially_copyable_types(); + + // Test member function pointers + test_iter_cat_for_trivially_copyable_types(); + test_iter_cat_for_trivially_copyable_types(); + test_iter_cat_for_trivially_copyable_types(); + test_iter_cat_for_trivially_copyable_types(); + +#if _HAS_CXX17 +#ifndef _MSC_VER // TRANSITION, GH-2128 + test_iter_cat_for_trivially_copyable_types(); +#endif // !defined(_MSC_VER) + test_iter_cat_for_trivially_copyable_types(); +#endif // _HAS_CXX17 + +#if _HAS_CXX20 + test_iter_cat_for_trivially_copyable_types, span>(); + test_iter_cat_for_trivially_copyable_types, span>(); + test_iter_cat_for_trivially_copyable_types, span>(); + test_iter_cat_for_trivially_copyable_types, span>(); + test_iter_cat_for_trivially_copyable_types, span>(); + test_iter_cat_for_trivially_copyable_types, span>(); +#endif // _HAS_CXX20 + + test_iter_cat_for_types(); + test_iter_cat_for_types(); + test_iter_cat_for_types(); + test_iter_cat_for_types(); + test_iter_cat_for_types(); + test_iter_cat_for_types(); + test_iter_cat_for_types(); + test_iter_cat_for_types(); + test_iter_cat_for_types(); + + // Test different containers +#ifdef __cpp_lib_concepts + test_iter_cat_for_containers, vector>(); + test_iter_cat_for_containers, array>(); + test_iter_cat_for_containers, array>(); + test_iter_cat_for_containers, vector>(); +#endif // __cpp_lib_concepts + + test_iter_cat_for_containers, list>(); + test_iter_cat_for_containers, list>(); + test_iter_cat_for_containers, vector>(); + + // Test double move_iterator + test_iter_cat, int*>(); + +#ifdef __cpp_lib_concepts + // Test counted_iterator + test_iter_cat, int*>(); + test_iter_cat>(); + test_iter_cat, counted_iterator>(); +#endif // __cpp_lib_concepts + + // No volatile + test_iter_cat_for_trivially_copyable_types(); + test_iter_cat_for_trivially_copyable_types(); + test_iter_cat_for_trivially_copyable_types(); + + test_iter_cat_for_containers, array>(); + test_iter_cat_for_containers, array>(); + test_iter_cat_for_containers, array>(); +} + +int main() {} // COMPILE-ONLY diff --git a/tests/std/tests/GH_000431_lex_compare_family/env.lst b/tests/std/tests/GH_000431_lex_compare_family/env.lst new file mode 100644 index 00000000000..19f025bd0e6 --- /dev/null +++ b/tests/std/tests/GH_000431_lex_compare_family/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_matrix.lst diff --git a/tests/std/tests/GH_000431_lex_compare_family/test.cpp b/tests/std/tests/GH_000431_lex_compare_family/test.cpp new file mode 100644 index 00000000000..425115ffc2d --- /dev/null +++ b/tests/std/tests/GH_000431_lex_compare_family/test.cpp @@ -0,0 +1,569 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#ifdef __clang__ +#pragma clang diagnostic ignored "-Wsign-compare" // comparison of integers of different signs: 'X' and 'Y' +#endif // __clang__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cpp_lib_concepts +#include +#endif // __cpp_lib_concepts + +#pragma warning(disable : 4018) // '<': signed/unsigned mismatch +#pragma warning(disable : 4365) // conversion from 'X' to 'Y', signed/unsigned mismatch +#pragma warning(disable : 4804) // '<': unsafe use of type 'bool' in operation + +using namespace std; + +#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) + +template +void test_algorithms_for_integrals(LexCompareFn lexicographical_compare) { + Int1 arr1[10] = {5, 7, 3, 4, 6, 4, 7, 1, 9, 5}; + Int2 arr2[10] = {5, 7, 3, 4, 6, 4, 7, 1, 9, 5}; + Int2 arr3[10] = {5, 7, 3, 4, 6, 4, 7, 1, 10, 5}; + Int2 arr4[10] = {5, 7, 2, 4, 6, 4, 7, 1, 9, 5}; + Int2 arr5[11] = {5, 7, 3, 4, 6, 4, 7, 1, 9, 5, 4}; + Int2 arr6[11] = {5, 7, 3, 4, 6, 4, 7, 1, 10, 5, 4}; + Int2 arr7[11] = {5, 7, 2, 4, 6, 4, 7, 1, 9, 5, 4}; + Int2 arr8[9] = {5, 7, 3, 4, 6, 4, 7, 1, 9}; + Int2 arr9[9] = {5, 7, 3, 4, 6, 4, 7, 1, 10}; + Int2 arr10[9] = {5, 7, 2, 4, 6, 4, 7, 1, 9}; + + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr2), end(arr2), less{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr2), end(arr2), greater{})); + + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr3), end(arr3), less{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr3), end(arr3), greater{})); + + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr4), end(arr4), less{})); + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr4), end(arr4), greater{})); + + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr5), end(arr5), less{})); + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr5), end(arr5), greater{})); + + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr6), end(arr6), less{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr6), end(arr6), greater{})); + + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr7), end(arr7), less{})); + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr7), end(arr7), greater{})); + + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr8), end(arr8), less{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr8), end(arr8), greater{})); + + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr9), end(arr9), less{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr9), end(arr9), greater{})); + + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr10), end(arr10), less{})); + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr10), end(arr10), greater{})); +} + +template +void test_algorithms_for_1byte_integrals(LexCompareFn lexicographical_compare) { + test_algorithms_for_integrals(lexicographical_compare); + test_algorithms_for_integrals(lexicographical_compare); + test_algorithms_for_integrals(lexicographical_compare); + test_algorithms_for_integrals(lexicographical_compare); +#ifdef __cpp_lib_char8_t + test_algorithms_for_integrals(lexicographical_compare); +#endif // __cpp_lib_char8_t +} + +template +void test_algorithms_for_signed_integrals(LexCompareFn lexicographical_compare) { + using UInt = make_unsigned_t; + + Int arr1[10] = {5, 7, -3, 4, 6, 4, 7, 1, 9, 5}; + Int arr2[10] = {5, 7, +3, 4, 6, 4, 7, 1, 9, 5}; + UInt arr3[10] = {5, 7, static_cast(-3), 4, 6, 4, 7, 1, 9, 5}; + UInt arr4[10] = {5, 7, static_cast(+3), 4, 6, 4, 7, 1, 9, 5}; + + // Test signed + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr2), end(arr2), less<>{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr2), end(arr2), greater<>{})); + assert(!lexicographical_compare(begin(arr2), end(arr2), begin(arr1), end(arr1), less<>{})); + assert(lexicographical_compare(begin(arr2), end(arr2), begin(arr1), end(arr1), greater<>{})); + + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr2), end(arr2), less{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr2), end(arr2), greater{})); + assert(!lexicographical_compare(begin(arr2), end(arr2), begin(arr1), end(arr1), less{})); + assert(lexicographical_compare(begin(arr2), end(arr2), begin(arr1), end(arr1), greater{})); + + assert(lexicographical_compare(begin(arr3), end(arr3), begin(arr4), end(arr4), less{})); + assert(!lexicographical_compare(begin(arr3), end(arr3), begin(arr4), end(arr4), greater{})); + assert(!lexicographical_compare(begin(arr4), end(arr4), begin(arr3), end(arr3), less{})); + assert(lexicographical_compare(begin(arr4), end(arr4), begin(arr3), end(arr3), greater{})); + + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr3), end(arr3), less{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr3), end(arr3), greater{})); + assert(!lexicographical_compare(begin(arr3), end(arr3), begin(arr1), end(arr1), less{})); + assert(!lexicographical_compare(begin(arr3), end(arr3), begin(arr1), end(arr1), greater{})); + + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr4), end(arr4), less{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr4), end(arr4), greater{})); + assert(!lexicographical_compare(begin(arr4), end(arr4), begin(arr1), end(arr1), less{})); + assert(lexicographical_compare(begin(arr4), end(arr4), begin(arr1), end(arr1), greater{})); + + assert(!lexicographical_compare(begin(arr2), end(arr2), begin(arr3), end(arr3), less{})); + assert(lexicographical_compare(begin(arr2), end(arr2), begin(arr3), end(arr3), greater{})); + assert(lexicographical_compare(begin(arr3), end(arr3), begin(arr2), end(arr2), less{})); + assert(!lexicographical_compare(begin(arr3), end(arr3), begin(arr2), end(arr2), greater{})); + + assert(!lexicographical_compare(begin(arr2), end(arr2), begin(arr4), end(arr4), less{})); + assert(!lexicographical_compare(begin(arr2), end(arr2), begin(arr4), end(arr4), greater{})); + assert(!lexicographical_compare(begin(arr4), end(arr4), begin(arr2), end(arr2), less{})); + assert(!lexicographical_compare(begin(arr4), end(arr4), begin(arr2), end(arr2), greater{})); + + // Test unsigned + assert(!lexicographical_compare(begin(arr3), end(arr3), begin(arr4), end(arr4), less<>{})); + assert(lexicographical_compare(begin(arr3), end(arr3), begin(arr4), end(arr4), greater<>{})); + assert(lexicographical_compare(begin(arr4), end(arr4), begin(arr3), end(arr3), less<>{})); + assert(!lexicographical_compare(begin(arr4), end(arr4), begin(arr3), end(arr3), greater<>{})); + + assert(!lexicographical_compare(begin(arr3), end(arr3), begin(arr4), end(arr4), less{})); + assert(lexicographical_compare(begin(arr3), end(arr3), begin(arr4), end(arr4), greater{})); + assert(lexicographical_compare(begin(arr4), end(arr4), begin(arr3), end(arr3), less{})); + assert(!lexicographical_compare(begin(arr4), end(arr4), begin(arr3), end(arr3), greater{})); + + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr2), end(arr2), less{})); + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr2), end(arr2), greater{})); + assert(lexicographical_compare(begin(arr2), end(arr2), begin(arr1), end(arr1), less{})); + assert(!lexicographical_compare(begin(arr2), end(arr2), begin(arr1), end(arr1), greater{})); + + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr3), end(arr3), less{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr3), end(arr3), greater{})); + assert(!lexicographical_compare(begin(arr3), end(arr3), begin(arr1), end(arr1), less{})); + assert(!lexicographical_compare(begin(arr3), end(arr3), begin(arr1), end(arr1), greater{})); + + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr4), end(arr4), less{})); + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr4), end(arr4), greater{})); + assert(lexicographical_compare(begin(arr4), end(arr4), begin(arr1), end(arr1), less{})); + assert(!lexicographical_compare(begin(arr4), end(arr4), begin(arr1), end(arr1), greater{})); + + assert(lexicographical_compare(begin(arr2), end(arr2), begin(arr3), end(arr3), less{})); + assert(!lexicographical_compare(begin(arr2), end(arr2), begin(arr3), end(arr3), greater{})); + assert(!lexicographical_compare(begin(arr3), end(arr3), begin(arr2), end(arr2), less{})); + assert(lexicographical_compare(begin(arr3), end(arr3), begin(arr2), end(arr2), greater{})); + + assert(!lexicographical_compare(begin(arr2), end(arr2), begin(arr4), end(arr4), less{})); + assert(!lexicographical_compare(begin(arr2), end(arr2), begin(arr4), end(arr4), greater{})); + assert(!lexicographical_compare(begin(arr4), end(arr4), begin(arr2), end(arr2), less{})); + assert(!lexicographical_compare(begin(arr4), end(arr4), begin(arr2), end(arr2), greater{})); + + // Test comparing signed and unsigned + constexpr bool promoted = sizeof(Int) < sizeof(int); + + assert(promoted == lexicographical_compare(begin(arr1), end(arr1), begin(arr3), end(arr3), less<>{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr3), end(arr3), greater<>{})); + assert(!lexicographical_compare(begin(arr3), end(arr3), begin(arr1), end(arr1), less<>{})); + assert(promoted == lexicographical_compare(begin(arr3), end(arr3), begin(arr1), end(arr1), greater<>{})); + + assert(promoted == lexicographical_compare(begin(arr1), end(arr1), begin(arr4), end(arr4), less<>{})); + assert(promoted == !lexicographical_compare(begin(arr1), end(arr1), begin(arr4), end(arr4), greater<>{})); + assert(promoted == !lexicographical_compare(begin(arr4), end(arr4), begin(arr1), end(arr1), less<>{})); + assert(promoted == lexicographical_compare(begin(arr4), end(arr4), begin(arr1), end(arr1), greater<>{})); + + assert(lexicographical_compare(begin(arr2), end(arr2), begin(arr3), end(arr3), less<>{})); + assert(!lexicographical_compare(begin(arr2), end(arr2), begin(arr3), end(arr3), greater<>{})); + assert(!lexicographical_compare(begin(arr3), end(arr3), begin(arr2), end(arr2), less<>{})); + assert(lexicographical_compare(begin(arr3), end(arr3), begin(arr2), end(arr2), greater<>{})); + + assert(!lexicographical_compare(begin(arr2), end(arr2), begin(arr4), end(arr4), less<>{})); + assert(!lexicographical_compare(begin(arr2), end(arr2), begin(arr4), end(arr4), greater<>{})); + assert(!lexicographical_compare(begin(arr4), end(arr4), begin(arr2), end(arr2), less<>{})); + assert(!lexicographical_compare(begin(arr4), end(arr4), begin(arr2), end(arr2), greater<>{})); +} + +template +void test_algorithms_for_bools_helper(LexCompareFn lexicographical_compare) { + Bool1 arr1[5] = {true, true, false, false, true}; + Bool2 arr2[5] = {true, true, false, false, true}; + Bool2 arr3[5] = {true, true, true, false, true}; + Bool2 arr4[5] = {true, false, false, false, true}; + Bool2 arr5[6] = {true, true, false, false, true, false}; + Bool2 arr6[6] = {true, true, true, false, true, false}; + Bool2 arr7[6] = {true, false, false, false, true, false}; + Bool2 arr8[4] = {true, true, false, false}; + Bool2 arr9[4] = {true, true, true, false}; + Bool2 arr10[4] = {true, false, false, false}; + + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr2), end(arr2), less{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr2), end(arr2), greater{})); + + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr3), end(arr3), less{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr3), end(arr3), greater{})); + + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr4), end(arr4), less{})); + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr4), end(arr4), greater{})); + + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr5), end(arr5), less{})); + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr5), end(arr5), greater{})); + + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr6), end(arr6), less{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr6), end(arr6), greater{})); + + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr7), end(arr7), less{})); + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr7), end(arr7), greater{})); + + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr8), end(arr8), less{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr8), end(arr8), greater{})); + + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr9), end(arr9), less{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr9), end(arr9), greater{})); + + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr10), end(arr10), less{})); + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr10), end(arr10), greater{})); +} + +template +void test_algorithms_for_bools(LexCompareFn lexicographical_compare) { + test_algorithms_for_bools_helper(lexicographical_compare); + test_algorithms_for_bools_helper(lexicographical_compare); + test_algorithms_for_bools_helper(lexicographical_compare); + test_algorithms_for_bools_helper(lexicographical_compare); + test_algorithms_for_bools_helper(lexicographical_compare); + test_algorithms_for_bools_helper(lexicographical_compare); +} + +template +void test_algorithms_for_bool_and_chars(LexCompareFn lexicographical_compare) { + bool arr1[5] = {true, true, false, false, true}; + Char arr2[5] = {2, 1, 0, 0, 1}; + Char arr3[5] = {2, 1, 1, 0, 1}; + Char arr4[5] = {2, 0, 0, 0, 1}; + Char arr5[6] = {2, 1, 0, 0, 1, 0}; + Char arr6[6] = {2, 1, 1, 0, 1, 0}; + Char arr7[6] = {2, 0, 0, 0, 1, 0}; + Char arr8[4] = {2, 1, 0, 0}; + Char arr9[4] = {2, 1, 1, 0}; + Char arr10[4] = {2, 0, 0, 0}; + + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr2), end(arr2), less{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr2), end(arr2), greater{})); + + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr3), end(arr3), less{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr3), end(arr3), greater{})); + + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr4), end(arr4), less{})); + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr4), end(arr4), greater{})); + + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr5), end(arr5), less{})); + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr5), end(arr5), greater{})); + + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr6), end(arr6), less{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr6), end(arr6), greater{})); + + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr7), end(arr7), less{})); + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr7), end(arr7), greater{})); + + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr8), end(arr8), less{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr8), end(arr8), greater{})); + + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr9), end(arr9), less{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr9), end(arr9), greater{})); + + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr10), end(arr10), less{})); + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr10), end(arr10), greater{})); + + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr2), end(arr2), less{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr2), end(arr2), greater{})); + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr3), end(arr3), less{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr3), end(arr3), greater{})); + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr4), end(arr4), less{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr4), end(arr4), greater{})); + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr5), end(arr5), less{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr5), end(arr5), greater{})); + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr6), end(arr6), less{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr6), end(arr6), greater{})); + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr7), end(arr7), less{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr7), end(arr7), greater{})); + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr8), end(arr8), less{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr8), end(arr8), greater{})); + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr9), end(arr9), less{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr9), end(arr9), greater{})); + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr10), end(arr10), less{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr10), end(arr10), greater{})); + + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr2), end(arr2), less<>{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr2), end(arr2), greater<>{})); + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr3), end(arr3), less<>{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr3), end(arr3), greater<>{})); + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr4), end(arr4), less<>{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr4), end(arr4), greater<>{})); + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr5), end(arr5), less<>{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr5), end(arr5), greater<>{})); + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr6), end(arr6), less<>{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr6), end(arr6), greater<>{})); + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr7), end(arr7), less<>{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr7), end(arr7), greater<>{})); + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr8), end(arr8), less<>{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr8), end(arr8), greater<>{})); + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr9), end(arr9), less<>{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr9), end(arr9), greater<>{})); + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr10), end(arr10), less<>{})); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr10), end(arr10), greater<>{})); +} + +template +void test_algorithms(LexCompareFn lexicographical_compare) { + { // Test ints + test_algorithms_for_integrals(lexicographical_compare); + test_algorithms_for_integrals(lexicographical_compare); + test_algorithms_for_integrals(lexicographical_compare); + test_algorithms_for_integrals(lexicographical_compare); + + test_algorithms_for_integrals(lexicographical_compare); + test_algorithms_for_integrals(lexicographical_compare); + test_algorithms_for_integrals(lexicographical_compare); + test_algorithms_for_integrals(lexicographical_compare); + + test_algorithms_for_integrals(lexicographical_compare); + test_algorithms_for_integrals(lexicographical_compare); + test_algorithms_for_integrals(lexicographical_compare); + test_algorithms_for_integrals(lexicographical_compare); + } + + { // Test chars + test_algorithms_for_1byte_integrals(lexicographical_compare); + test_algorithms_for_1byte_integrals(lexicographical_compare); + test_algorithms_for_1byte_integrals(lexicographical_compare); + test_algorithms_for_1byte_integrals(lexicographical_compare); + test_algorithms_for_1byte_integrals(lexicographical_compare); + test_algorithms_for_1byte_integrals(lexicographical_compare); + test_algorithms_for_1byte_integrals(lexicographical_compare); + test_algorithms_for_1byte_integrals(lexicographical_compare); + test_algorithms_for_1byte_integrals(lexicographical_compare); +#ifdef __cpp_lib_char8_t + test_algorithms_for_1byte_integrals(lexicographical_compare); + test_algorithms_for_1byte_integrals(lexicographical_compare); + test_algorithms_for_1byte_integrals(lexicographical_compare); + test_algorithms_for_1byte_integrals(lexicographical_compare); + test_algorithms_for_1byte_integrals(lexicographical_compare); + test_algorithms_for_1byte_integrals(lexicographical_compare); + test_algorithms_for_1byte_integrals(lexicographical_compare); +#endif // __cpp_lib_char8_t + } + + { // Test signedness + test_algorithms_for_signed_integrals(lexicographical_compare); + test_algorithms_for_signed_integrals(lexicographical_compare); + } + + { // Test bool + test_algorithms_for_bools(lexicographical_compare); + test_algorithms_for_bools(lexicographical_compare); + test_algorithms_for_bools(lexicographical_compare); + test_algorithms_for_bools(lexicographical_compare); + test_algorithms_for_bools(lexicographical_compare); + test_algorithms_for_bools(lexicographical_compare); + test_algorithms_for_bools(lexicographical_compare); + test_algorithms_for_bools(lexicographical_compare); + test_algorithms_for_bools(lexicographical_compare); + test_algorithms_for_bools(lexicographical_compare); + } + + { // Test comparing bool and char + test_algorithms_for_bool_and_chars(lexicographical_compare); + test_algorithms_for_bool_and_chars(lexicographical_compare); + test_algorithms_for_bool_and_chars(lexicographical_compare); + } +} + +#ifdef __cpp_lib_concepts +template +void test_three_way_algorithms_for_integrals( + LexCompareThreeWayFn lexicographical_compare_three_way, Comp comp = compare_three_way{}) { + Int1 arr1[10] = {5, 7, 3, 4, 6, 4, 7, 1, 9, 5}; + Int2 arr2[10] = {5, 7, 3, 4, 6, 4, 7, 1, 9, 5}; + Int2 arr3[10] = {5, 7, 3, 4, 6, 4, 7, 1, 10, 5}; + Int2 arr4[10] = {5, 7, 2, 4, 6, 4, 7, 1, 9, 5}; + Int2 arr5[11] = {5, 7, 3, 4, 6, 4, 7, 1, 9, 5, 4}; + Int2 arr6[11] = {5, 7, 3, 4, 6, 4, 7, 1, 10, 5, 4}; + Int2 arr7[11] = {5, 7, 2, 4, 6, 4, 7, 1, 9, 5, 4}; + Int2 arr8[9] = {5, 7, 3, 4, 6, 4, 7, 1, 9}; + Int2 arr9[9] = {5, 7, 3, 4, 6, 4, 7, 1, 10}; + Int2 arr10[9] = {5, 7, 2, 4, 6, 4, 7, 1, 9}; + + assert(lexicographical_compare_three_way(begin(arr1), end(arr1), begin(arr2), end(arr2), comp) == 0); + assert(lexicographical_compare_three_way(begin(arr1), end(arr1), begin(arr3), end(arr3), comp) < 0); + assert(lexicographical_compare_three_way(begin(arr1), end(arr1), begin(arr4), end(arr4), comp) > 0); + + assert(lexicographical_compare_three_way(begin(arr1), end(arr1), begin(arr5), end(arr5), comp) < 0); + assert(lexicographical_compare_three_way(begin(arr1), end(arr1), begin(arr6), end(arr6), comp) < 0); + assert(lexicographical_compare_three_way(begin(arr1), end(arr1), begin(arr7), end(arr7), comp) > 0); + + assert(lexicographical_compare_three_way(begin(arr1), end(arr1), begin(arr8), end(arr8), comp) > 0); + assert(lexicographical_compare_three_way(begin(arr1), end(arr1), begin(arr9), end(arr9), comp) < 0); + assert(lexicographical_compare_three_way(begin(arr1), end(arr1), begin(arr10), end(arr10), comp) > 0); +} + +template +void test_three_way_algorithms_for_signed_integrals(LexCompareThreeWayFn lexicographical_compare_three_way) { + using UInt = make_unsigned_t; + + Int arr1[10] = {5, 7, -3, 4, 6, 4, 7, 1, 9, 5}; + Int arr2[10] = {5, 7, +3, 4, 6, 4, 7, 1, 9, 5}; + UInt arr3[10] = {5, 7, static_cast(-3), 4, 6, 4, 7, 1, 9, 5}; + UInt arr4[10] = {5, 7, static_cast(+3), 4, 6, 4, 7, 1, 9, 5}; + + // Test signed + assert(lexicographical_compare_three_way(begin(arr1), end(arr1), begin(arr2), end(arr2), compare_three_way{}) < 0); + assert(lexicographical_compare_three_way(begin(arr2), end(arr2), begin(arr1), end(arr1), compare_three_way{}) > 0); + + // Test unsigned + assert(lexicographical_compare_three_way(begin(arr3), end(arr3), begin(arr4), end(arr4), compare_three_way{}) > 0); + assert(lexicographical_compare_three_way(begin(arr4), end(arr4), begin(arr3), end(arr3), compare_three_way{}) < 0); + + // Test comparing signed and unsigned + constexpr bool promoted = sizeof(Int) < sizeof(int); + +#ifdef __clang__ // clang doesn't allow three way comparison between int and unsigned int + if constexpr (promoted) +#endif // __clang__ + { + assert(lexicographical_compare_three_way(begin(arr1), end(arr1), begin(arr3), end(arr3), compare_three_way{}) + == (promoted ? strong_ordering::less : strong_ordering::equal)); + assert(lexicographical_compare_three_way(begin(arr3), end(arr3), begin(arr1), end(arr1), compare_three_way{}) + == (promoted ? strong_ordering::greater : strong_ordering::equal)); + + assert(lexicographical_compare_three_way(begin(arr1), end(arr1), begin(arr4), end(arr4), compare_three_way{}) + == (promoted ? strong_ordering::less : strong_ordering::greater)); + assert(lexicographical_compare_three_way(begin(arr4), end(arr4), begin(arr1), end(arr1), compare_three_way{}) + == (promoted ? strong_ordering::greater : strong_ordering::less)); + + assert( + lexicographical_compare_three_way(begin(arr2), end(arr2), begin(arr3), end(arr3), compare_three_way{}) < 0); + assert( + lexicographical_compare_three_way(begin(arr3), end(arr3), begin(arr2), end(arr2), compare_three_way{}) > 0); + + assert(lexicographical_compare_three_way(begin(arr2), end(arr2), begin(arr4), end(arr4), compare_three_way{}) + == 0); + assert(lexicographical_compare_three_way(begin(arr4), end(arr4), begin(arr2), end(arr2), compare_three_way{}) + == 0); + } +} + +template +void test_three_way_algorithms_for_bools(LexCompareThreeWayFn lexicographical_compare_three_way) { + bool arr1[5] = {true, true, false, false, true}; + bool arr2[5] = {true, true, false, false, true}; + bool arr3[5] = {true, true, true, false, true}; + bool arr4[5] = {true, false, false, false, true}; + bool arr5[6] = {true, true, false, false, true, false}; + bool arr6[6] = {true, true, true, false, true, false}; + bool arr7[6] = {true, false, false, false, true, false}; + bool arr8[4] = {true, true, false, false}; + bool arr9[4] = {true, true, true, false}; + bool arr10[4] = {true, false, false, false}; + + assert(lexicographical_compare_three_way(begin(arr1), end(arr1), begin(arr2), end(arr2), compare_three_way{}) == 0); + assert(lexicographical_compare_three_way(begin(arr2), end(arr2), begin(arr1), end(arr1), compare_three_way{}) == 0); + + assert(lexicographical_compare_three_way(begin(arr1), end(arr1), begin(arr3), end(arr3), compare_three_way{}) < 0); + assert(lexicographical_compare_three_way(begin(arr3), end(arr3), begin(arr1), end(arr1), compare_three_way{}) > 0); + + assert(lexicographical_compare_three_way(begin(arr1), end(arr1), begin(arr4), end(arr4), compare_three_way{}) > 0); + assert(lexicographical_compare_three_way(begin(arr4), end(arr4), begin(arr1), end(arr1), compare_three_way{}) < 0); + + assert(lexicographical_compare_three_way(begin(arr1), end(arr1), begin(arr5), end(arr5), compare_three_way{}) < 0); + assert(lexicographical_compare_three_way(begin(arr5), end(arr5), begin(arr1), end(arr1), compare_three_way{}) > 0); + + assert(lexicographical_compare_three_way(begin(arr1), end(arr1), begin(arr6), end(arr6), compare_three_way{}) < 0); + assert(lexicographical_compare_three_way(begin(arr6), end(arr6), begin(arr1), end(arr1), compare_three_way{}) > 0); + + assert(lexicographical_compare_three_way(begin(arr1), end(arr1), begin(arr7), end(arr7), compare_three_way{}) > 0); + assert(lexicographical_compare_three_way(begin(arr7), end(arr7), begin(arr1), end(arr1), compare_three_way{}) < 0); + + assert(lexicographical_compare_three_way(begin(arr1), end(arr1), begin(arr8), end(arr8), compare_three_way{}) > 0); + assert(lexicographical_compare_three_way(begin(arr8), end(arr8), begin(arr1), end(arr1), compare_three_way{}) < 0); + + assert(lexicographical_compare_three_way(begin(arr1), end(arr1), begin(arr9), end(arr9), compare_three_way{}) < 0); + assert(lexicographical_compare_three_way(begin(arr9), end(arr9), begin(arr1), end(arr1), compare_three_way{}) > 0); + + assert( + lexicographical_compare_three_way(begin(arr1), end(arr1), begin(arr10), end(arr10), compare_three_way{}) > 0); + assert( + lexicographical_compare_three_way(begin(arr10), end(arr10), begin(arr1), end(arr1), compare_three_way{}) < 0); +} + +template +void test_three_way_algorithms(LexCompareThreeWayFn lexicographical_compare_three_way) { + { // Test ints + test_three_way_algorithms_for_integrals(lexicographical_compare_three_way, compare_three_way{}); + test_three_way_algorithms_for_integrals(lexicographical_compare_three_way, strong_order); + test_three_way_algorithms_for_integrals(lexicographical_compare_three_way, weak_order); + test_three_way_algorithms_for_integrals(lexicographical_compare_three_way, partial_order); + test_three_way_algorithms_for_integrals( + lexicographical_compare_three_way, compare_strong_order_fallback); + test_three_way_algorithms_for_integrals( + lexicographical_compare_three_way, compare_weak_order_fallback); + test_three_way_algorithms_for_integrals( + lexicographical_compare_three_way, compare_partial_order_fallback); + } + + { // Test chars + test_three_way_algorithms_for_integrals(lexicographical_compare_three_way); + test_three_way_algorithms_for_integrals(lexicographical_compare_three_way); + test_three_way_algorithms_for_integrals(lexicographical_compare_three_way); + test_three_way_algorithms_for_integrals(lexicographical_compare_three_way); + test_three_way_algorithms_for_integrals(lexicographical_compare_three_way); + test_three_way_algorithms_for_integrals(lexicographical_compare_three_way); + test_three_way_algorithms_for_integrals(lexicographical_compare_three_way); + test_three_way_algorithms_for_integrals(lexicographical_compare_three_way); + test_three_way_algorithms_for_integrals(lexicographical_compare_three_way); +#ifdef __cpp_lib_char8_t + test_three_way_algorithms_for_integrals(lexicographical_compare_three_way); + test_three_way_algorithms_for_integrals(lexicographical_compare_three_way); + test_three_way_algorithms_for_integrals(lexicographical_compare_three_way); + test_three_way_algorithms_for_integrals(lexicographical_compare_three_way); + test_three_way_algorithms_for_integrals(lexicographical_compare_three_way); + test_three_way_algorithms_for_integrals(lexicographical_compare_three_way); + test_three_way_algorithms_for_integrals(lexicographical_compare_three_way); +#endif // __cpp_lib_char8_t + } + + { // Test signedness + test_three_way_algorithms_for_signed_integrals(lexicographical_compare_three_way); + test_three_way_algorithms_for_signed_integrals(lexicographical_compare_three_way); + } + + { // Test bool + test_three_way_algorithms_for_bools(lexicographical_compare_three_way); + } +} +#endif // __cpp_lib_concepts + +int main() { + test_algorithms([](auto begin1, auto end1, auto begin2, auto end2, auto pred) { + return lexicographical_compare(begin1, end1, begin2, end2, pred); + }); + +#ifdef __cpp_lib_concepts + test_algorithms([](auto begin1, auto end1, auto begin2, auto end2, auto pred) { + return ranges::lexicographical_compare(begin1, end1, begin2, end2, pred); + }); + + test_algorithms([](auto begin1, auto end1, auto begin2, auto end2, auto pred) { + return lexicographical_compare_three_way(begin1, end1, begin2, end2, [&](const auto& left, const auto& right) { + return pred(left, right) ? strong_ordering::less + : pred(right, left) ? strong_ordering::greater + : strong_ordering::equal; + }) < 0; + }); + + test_three_way_algorithms([](auto begin1, auto end1, auto begin2, auto end2, auto comp) { + return lexicographical_compare_three_way(begin1, end1, begin2, end2, comp); + }); +#endif // __cpp_lib_concepts +} diff --git a/tests/std/tests/GH_000431_lex_compare_memcmp_classify/env.lst b/tests/std/tests/GH_000431_lex_compare_memcmp_classify/env.lst new file mode 100644 index 00000000000..19f025bd0e6 --- /dev/null +++ b/tests/std/tests/GH_000431_lex_compare_memcmp_classify/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_matrix.lst diff --git a/tests/std/tests/GH_000431_lex_compare_memcmp_classify/test.compile.pass.cpp b/tests/std/tests/GH_000431_lex_compare_memcmp_classify/test.compile.pass.cpp new file mode 100644 index 00000000000..cc4edcd3257 --- /dev/null +++ b/tests/std/tests/GH_000431_lex_compare_memcmp_classify/test.compile.pass.cpp @@ -0,0 +1,340 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cpp_lib_concepts +#include +#endif // __cpp_lib_concepts + +using namespace std; + +#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) + +template +void assert_lex_compare_memcmp_classify() { + STATIC_ASSERT(is_same_v<_Lex_compare_memcmp_classify, Expected>); +} + +#ifdef __cpp_lib_concepts +template +void assert_lex_compare_three_way_memcmp_classify() { + STATIC_ASSERT(is_same_v<_Lex_compare_three_way_memcmp_classify, Expected>); +} +#endif // __cpp_lib_concepts + +template +void test_lex_compare_memcmp_classify_for_iterators() { + assert_lex_compare_memcmp_classify(); + assert_lex_compare_memcmp_classify(); + assert_lex_compare_memcmp_classify(); + assert_lex_compare_memcmp_classify(); +} + +#ifdef __cpp_lib_concepts +template +void test_lex_compare_three_way_memcmp_classify_for_iterators() { + assert_lex_compare_three_way_memcmp_classify(); + assert_lex_compare_three_way_memcmp_classify(); + assert_lex_compare_three_way_memcmp_classify(); + assert_lex_compare_three_way_memcmp_classify(); +} +#endif // __cpp_lib_concepts + +template +void test_lex_compare_memcmp_classify_for_pred_helper() { + test_lex_compare_memcmp_classify_for_iterators(); +} + +#ifdef __cpp_lib_concepts +template +void test_lex_compare_three_way_memcmp_classify_for_comp_helper() { + test_lex_compare_three_way_memcmp_classify_for_iterators(); +} +#endif // __cpp_lib_concepts + +template +void test_lex_compare_memcmp_classify_for_pred() { + test_lex_compare_memcmp_classify_for_pred_helper(); + + // No volatile + test_lex_compare_memcmp_classify_for_pred_helper(); + test_lex_compare_memcmp_classify_for_pred_helper(); + test_lex_compare_memcmp_classify_for_pred_helper(); +} + +#ifdef __cpp_lib_concepts +template +void test_lex_compare_three_way_memcmp_classify_for_comp() { + test_lex_compare_three_way_memcmp_classify_for_comp_helper(); + + // No volatile + test_lex_compare_three_way_memcmp_classify_for_comp_helper(); + test_lex_compare_three_way_memcmp_classify_for_comp_helper(); + test_lex_compare_three_way_memcmp_classify_for_comp_helper(); +} +#endif // __cpp_lib_concepts + +template +void test_lex_compare_memcmp_classify_for_opaque_preds_helper() { + using expected_less = conditional_t, void>; + using expected_greater = conditional_t, void>; + + test_lex_compare_memcmp_classify_for_pred>(); + test_lex_compare_memcmp_classify_for_pred>(); +} + +template +void test_lex_compare_memcmp_classify_for_opaque_preds() { + enum Enum1 : Type1 {}; + enum Enum2 : Type2 {}; + enum class EnumClass1 : Type1 {}; + enum class EnumClass2 : Type2 {}; + + enum EnumPred : PredType {}; + enum class EnumClassPred : PredType {}; + + test_lex_compare_memcmp_classify_for_opaque_preds_helper(); + test_lex_compare_memcmp_classify_for_opaque_preds_helper(); + test_lex_compare_memcmp_classify_for_opaque_preds_helper(); + test_lex_compare_memcmp_classify_for_opaque_preds_helper(); + + // Enum classes are not convertible + test_lex_compare_memcmp_classify_for_opaque_preds_helper(); + test_lex_compare_memcmp_classify_for_opaque_preds_helper(); + test_lex_compare_memcmp_classify_for_opaque_preds_helper(); + test_lex_compare_memcmp_classify_for_opaque_preds_helper(); + test_lex_compare_memcmp_classify_for_opaque_preds_helper(); + + // Don't allow converting to and between enums + test_lex_compare_memcmp_classify_for_opaque_preds_helper(); + test_lex_compare_memcmp_classify_for_opaque_preds_helper(); + test_lex_compare_memcmp_classify_for_opaque_preds_helper(); + test_lex_compare_memcmp_classify_for_opaque_preds_helper(); + test_lex_compare_memcmp_classify_for_opaque_preds_helper(); + test_lex_compare_memcmp_classify_for_opaque_preds_helper(); + test_lex_compare_memcmp_classify_for_opaque_preds_helper(); + test_lex_compare_memcmp_classify_for_opaque_preds_helper(); + test_lex_compare_memcmp_classify_for_opaque_preds_helper(); + test_lex_compare_memcmp_classify_for_opaque_preds_helper(); + test_lex_compare_memcmp_classify_for_opaque_preds_helper(); + test_lex_compare_memcmp_classify_for_opaque_preds_helper(); + test_lex_compare_memcmp_classify_for_opaque_preds_helper(); + test_lex_compare_memcmp_classify_for_opaque_preds_helper(); + test_lex_compare_memcmp_classify_for_opaque_preds_helper(); + test_lex_compare_memcmp_classify_for_opaque_preds_helper(); + test_lex_compare_memcmp_classify_for_opaque_preds_helper(); + test_lex_compare_memcmp_classify_for_opaque_preds_helper(); +} + +template +void test_lex_compare_memcmp_classify_for_types() { + using expected_less = conditional_t, void>; + using expected_greater = conditional_t, void>; + + test_lex_compare_memcmp_classify_for_pred>(); + test_lex_compare_memcmp_classify_for_pred>(); + + test_lex_compare_memcmp_classify_for_pred>(); + test_lex_compare_memcmp_classify_for_pred>(); + + test_lex_compare_memcmp_classify_for_pred>(); + test_lex_compare_memcmp_classify_for_pred>(); + test_lex_compare_memcmp_classify_for_pred>(); + test_lex_compare_memcmp_classify_for_pred>(); + + test_lex_compare_memcmp_classify_for_pred>(); + test_lex_compare_memcmp_classify_for_pred>(); + test_lex_compare_memcmp_classify_for_pred>(); + + auto lambda = [](auto&&, auto&&) { return false; }; + test_lex_compare_memcmp_classify_for_pred(); + +#ifdef __cpp_lib_concepts + test_lex_compare_memcmp_classify_for_pred(); + test_lex_compare_memcmp_classify_for_pred(); + + test_lex_compare_memcmp_classify_for_pred(); + test_lex_compare_memcmp_classify_for_pred(); + test_lex_compare_memcmp_classify_for_pred(); + + // Test three way version + using strong_order_t = remove_const_t; + using weak_order_t = remove_const_t; + using partial_order_t = remove_const_t; + using expected_compare_three_way = + conditional_t == is_same_v, compare_three_way, void>; + using expected_strong_order = + conditional_t, decay_t>, strong_order_t, void>; + using expected_weak_order = + conditional_t, decay_t>, weak_order_t, void>; + using expected_partial_order = + conditional_t, decay_t>, partial_order_t, void>; + + test_lex_compare_three_way_memcmp_classify_for_comp(); + test_lex_compare_three_way_memcmp_classify_for_comp(); + test_lex_compare_three_way_memcmp_classify_for_comp(); + test_lex_compare_three_way_memcmp_classify_for_comp(); + + auto three_way_lambda = [](auto&&, auto&&) { return strong_ordering::equal; }; + test_lex_compare_memcmp_classify_for_pred(); +#endif // __cpp_lib_concepts +} + +template +void test_lex_compare_memcmp_classify_for_1byte_integrals() { + test_lex_compare_memcmp_classify_for_types(); + + test_lex_compare_memcmp_classify_for_opaque_preds, Type1, Type2, char>(); + test_lex_compare_memcmp_classify_for_opaque_preds(); + test_lex_compare_memcmp_classify_for_opaque_preds(); +#ifdef __cpp_lib_char8_t + test_lex_compare_memcmp_classify_for_opaque_preds(); +#endif // __cpp_lib_char8_t + test_lex_compare_memcmp_classify_for_opaque_preds && is_same_v, Type1, Type2, + bool>(); +} + +template +void test_lex_compare_memcmp_classify_for_containers() { + using expected_less = conditional_t, void>; + using expected_greater = conditional_t, void>; + + using It1 = typename Container1::iterator; + using ConstIt1 = typename Container1::const_iterator; + using It2 = typename Container2::iterator; + using ConstIt2 = typename Container2::const_iterator; + test_lex_compare_memcmp_classify_for_iterators>(); + test_lex_compare_memcmp_classify_for_iterators>(); +} + +enum char_enum : char {}; +enum schar_enum : signed char {}; +enum uchar_enum : unsigned char {}; +#ifdef __cpp_lib_char8_t +enum char8_t_enum : char8_t {}; +#endif // __cpp_lib_char8_t + +enum class char_enum_class : char {}; +enum class schar_enum_class : signed char {}; +enum class uchar_enum_class : unsigned char {}; +#ifdef __cpp_lib_char8_t +enum class char8_t_enum_class : char8_t {}; +#endif // __cpp_lib_char8_t + +struct user_struct {}; + +bool operator<(const user_struct&, const user_struct&) { + return false; +} + +void lex_compare_memcmp_classify_test_cases() { + // Allow unsigned 1 byte integrals + test_lex_compare_memcmp_classify_for_1byte_integrals, char, char>(); + test_lex_compare_memcmp_classify_for_1byte_integrals, unsigned char, char>(); + test_lex_compare_memcmp_classify_for_1byte_integrals, char, unsigned char>(); + test_lex_compare_memcmp_classify_for_1byte_integrals(); + test_lex_compare_memcmp_classify_for_1byte_integrals(); + test_lex_compare_memcmp_classify_for_1byte_integrals(); + test_lex_compare_memcmp_classify_for_1byte_integrals(); + test_lex_compare_memcmp_classify_for_1byte_integrals(); + test_lex_compare_memcmp_classify_for_1byte_integrals(); +#ifdef __cpp_lib_char8_t + test_lex_compare_memcmp_classify_for_1byte_integrals(); + test_lex_compare_memcmp_classify_for_1byte_integrals(); + test_lex_compare_memcmp_classify_for_1byte_integrals(); + test_lex_compare_memcmp_classify_for_1byte_integrals, char8_t, char>(); + test_lex_compare_memcmp_classify_for_1byte_integrals, char, char8_t>(); + test_lex_compare_memcmp_classify_for_1byte_integrals(); + test_lex_compare_memcmp_classify_for_1byte_integrals(); +#endif // __cpp_lib_char8_t + + // Test bool + test_lex_compare_memcmp_classify_for_1byte_integrals(); + test_lex_compare_memcmp_classify_for_1byte_integrals(); + test_lex_compare_memcmp_classify_for_1byte_integrals(); +#ifdef __cpp_lib_char8_t + test_lex_compare_memcmp_classify_for_1byte_integrals(); + test_lex_compare_memcmp_classify_for_1byte_integrals(); +#endif // __cpp_lib_char8_t + test_lex_compare_memcmp_classify_for_1byte_integrals(); + test_lex_compare_memcmp_classify_for_1byte_integrals(); + test_lex_compare_memcmp_classify_for_1byte_integrals(); + test_lex_compare_memcmp_classify_for_1byte_integrals(); + + // Don't allow enums + test_lex_compare_memcmp_classify_for_types(); + test_lex_compare_memcmp_classify_for_types(); + test_lex_compare_memcmp_classify_for_types(); +#ifdef __cpp_lib_char8_t + test_lex_compare_memcmp_classify_for_types(); +#endif // __cpp_lib_char8_t + + // Don't allow enum classes + test_lex_compare_memcmp_classify_for_types(); + test_lex_compare_memcmp_classify_for_types(); + test_lex_compare_memcmp_classify_for_types(); +#ifdef __cpp_lib_char8_t + test_lex_compare_memcmp_classify_for_types(); +#endif // __cpp_lib_char8_t + +#ifdef __cpp_lib_byte + // Test std::byte + test_lex_compare_memcmp_classify_for_types(); +#endif // __cpp_lib_byte + + // Don't allow bigger integrals + test_lex_compare_memcmp_classify_for_types(); + test_lex_compare_memcmp_classify_for_types(); + test_lex_compare_memcmp_classify_for_types(); + test_lex_compare_memcmp_classify_for_types(); + test_lex_compare_memcmp_classify_for_types(); + test_lex_compare_memcmp_classify_for_types(); + + // Don't allow pointers + test_lex_compare_memcmp_classify_for_types(); + + // Don't allow user-defined types + test_lex_compare_memcmp_classify_for_types(); + + // Test _Char_traits_lt + test_lex_compare_memcmp_classify_for_pred, char, char, _Char_traits_lt>>(); +#ifdef __cpp_lib_char8_t + test_lex_compare_memcmp_classify_for_pred, char8_t, char8_t, _Char_traits_lt>>(); +#endif // __cpp_lib_char8_t + + test_lex_compare_memcmp_classify_for_pred>>(); + test_lex_compare_memcmp_classify_for_pred>>(); + test_lex_compare_memcmp_classify_for_pred>>(); + + // Test different containers +#ifdef __cpp_lib_concepts + test_lex_compare_memcmp_classify_for_containers, vector>(); + test_lex_compare_memcmp_classify_for_containers, array>(); + test_lex_compare_memcmp_classify_for_containers, array>(); + test_lex_compare_memcmp_classify_for_containers, array>(); +#endif // __cpp_lib_concepts + + test_lex_compare_memcmp_classify_for_containers, list>(); + test_lex_compare_memcmp_classify_for_containers, list>(); + test_lex_compare_memcmp_classify_for_containers, vector>(); + +#ifdef __cpp_lib_concepts + // Test counted_iterator + assert_lex_compare_memcmp_classify, counted_iterator, unsigned char*, less<>>(); + assert_lex_compare_memcmp_classify, unsigned char*, counted_iterator, less<>>(); + assert_lex_compare_memcmp_classify, counted_iterator, counted_iterator, + less<>>(); +#endif // __cpp_lib_concepts +} + +int main() {} // COMPILE-ONLY diff --git a/tests/std/tests/P0202R3_constexpr_algorithm_and_exchange/test.cpp b/tests/std/tests/P0202R3_constexpr_algorithm_and_exchange/test.cpp index 4200898f9e5..65fa66c8f8d 100644 --- a/tests/std/tests/P0202R3_constexpr_algorithm_and_exchange/test.cpp +++ b/tests/std/tests/P0202R3_constexpr_algorithm_and_exchange/test.cpp @@ -115,6 +115,10 @@ struct bidirectional_pointer { template struct output_pointer { using iterator_category = output_iterator_tag; + using reference = T&; + using value_type = T; + using pointer = T*; + using difference_type = ptrdiff_t; constexpr explicit output_pointer(T* const ptr_) : ptr(ptr_) {} 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 deleted file mode 100644 index 0f8716cf6b7..00000000000 --- a/tests/std/tests/VSO_0180469_ptr_cat/test.compile.pass.cpp +++ /dev/null @@ -1,561 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception - -#define _SILENCE_CXX20_IS_POD_DEPRECATION_WARNING - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef __cpp_lib_concepts -#include -#include -#endif // __cpp_lib_concepts - -using namespace std; - -#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__) - -int main() {} // COMPILE-ONLY - -template -void assert_same() { - STATIC_ASSERT(is_same_v); -} - -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_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_ASSERT(Expected == MoveReallyTrivial + MoveTriviallyCopyable); - STATIC_ASSERT(!MoveReallyTrivial || MoveTriviallyCopyable); -}; - -template -void test_ptr_cat() { - (void) test_ptr_cat_helper{}; - // Also make sure that the source being const doesn't change the answer - (void) test_ptr_cat_helper{}; - // Also make sure wrapping the source in a move_iterator doesn't change the answer: - (void) test_ptr_cat_helper, Dest*>{}; - (void) test_ptr_cat_helper, Dest*>{}; - // const Dest doesn't make any sense so we always want that to blow up in the general implementation - (void) test_ptr_cat_helper<0, Source*, const Dest*>{}; - (void) test_ptr_cat_helper<0, const Source*, const Dest*>{}; - (void) test_ptr_cat_helper<0, volatile Source*, const Dest*>{}; - (void) test_ptr_cat_helper<0, Source*, const volatile Dest*>{}; - (void) test_ptr_cat_helper<0, volatile Source*, const volatile Dest*>{}; - (void) test_ptr_cat_helper<0, const volatile Source*, const Dest*>{}; - (void) test_ptr_cat_helper<0, const Source*, const volatile Dest*>{}; - (void) test_ptr_cat_helper<0, const volatile Source*, const volatile Dest*>{}; - - // Also make sure _Ptr_cat listens to the iterator type - (void) test_ptr_cat_helper<0, typename list::iterator, typename list::iterator>{}; - (void) test_ptr_cat_helper<0, typename list::const_iterator, typename list::iterator>{}; -} - -struct pod_struct { - int a; -}; - -STATIC_ASSERT(is_pod_v); - -struct trivially_copyable_struct { - int a; - trivially_copyable_struct() { - // do some complex setup :) - } - - trivially_copyable_struct(const trivially_copyable_struct&) = default; - trivially_copyable_struct& operator=(const trivially_copyable_struct&) = default; -}; - -STATIC_ASSERT(is_trivially_copyable_v); -STATIC_ASSERT(!is_trivial_v); - -struct custom_copy_struct { - int a; - custom_copy_struct() = default; - custom_copy_struct(const custom_copy_struct& o) { - a = o.a; - } - - custom_copy_struct& operator=(const custom_copy_struct& o) { - a = o.a; - return *this; - } -}; - -STATIC_ASSERT(!is_trivially_copyable_v); -STATIC_ASSERT(!is_trivial_v); - -enum int_enum : int {}; -enum short_enum : short {}; -enum bool_enum : bool {}; - -enum class int_enum_class : int {}; -enum class short_enum_class : short {}; -enum class bool_enum_class : bool {}; - -struct base_class {}; - -bool operator<(const base_class&, const base_class&) { - return false; -} - -struct derived_class : base_class {}; - - -void ptr_cat_test_cases() { - // Identity cases: - test_ptr_cat<2, int, int>(); - test_ptr_cat<2, bool, bool>(); - test_ptr_cat<2, pod_struct, pod_struct>(); - test_ptr_cat<1, trivially_copyable_struct, trivially_copyable_struct>(); - test_ptr_cat<0, custom_copy_struct, custom_copy_struct>(); - test_ptr_cat<2, int_enum, int_enum>(); - test_ptr_cat<2, short_enum, short_enum>(); - test_ptr_cat<2, bool_enum, bool_enum>(); - test_ptr_cat<2, int_enum_class, int_enum_class>(); - test_ptr_cat<2, short_enum_class, short_enum_class>(); - test_ptr_cat<2, bool_enum_class, bool_enum_class>(); - - // Allow conversions between integers of the same size: - static_assert(sizeof(int) == sizeof(long), "this test assumes that int and long are the same size"); - test_ptr_cat<2, int, long>(); - test_ptr_cat<2, long, int>(); - test_ptr_cat<2, unsigned int, long>(); - test_ptr_cat<2, long, unsigned int>(); - test_ptr_cat<2, int_enum, int>(); - test_ptr_cat<2, short_enum, short>(); - test_ptr_cat<2, bool_enum, bool>(); - test_ptr_cat<2, int, unsigned int>(); - test_ptr_cat<2, unsigned int, int>(); - test_ptr_cat<2, char, signed char>(); - test_ptr_cat<2, char, unsigned char>(); - test_ptr_cat<2, signed char, char>(); - test_ptr_cat<2, signed char, unsigned char>(); - test_ptr_cat<2, unsigned char, char>(); - test_ptr_cat<2, unsigned char, signed char>(); - // Don't allow conversion from int to enum (since that requires a cast): - test_ptr_cat<0, int, int_enum>(); - test_ptr_cat<0, short, short_enum>(); - test_ptr_cat<0, bool, bool_enum>(); - - // Don't allow such conversions if the sizes differ: - test_ptr_cat<0, int, short>(); - test_ptr_cat<0, short_enum, int>(); - - // Pointer cases: - 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*>(); - // ... adding const on the destination: - test_ptr_cat<2, int*, const int*>(); - test_ptr_cat<2, pod_struct*, const pod_struct*>(); - test_ptr_cat<2, trivially_copyable_struct*, const trivially_copyable_struct*>(); - test_ptr_cat<2, custom_copy_struct*, const custom_copy_struct*>(); - // ... other cv changes: - test_ptr_cat<2, int*, volatile int*>(); - test_ptr_cat<2, int*, const volatile int*>(); - test_ptr_cat<2, const int*, const int*>(); - test_ptr_cat<2, const int*, const volatile int*>(); - test_ptr_cat<2, volatile int*, volatile int*>(); - test_ptr_cat<2, volatile int*, const volatile int*>(); - test_ptr_cat<2, const volatile int*, const volatile int*>(); - - // 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>(); - test_ptr_cat<0, derived_class*, base_class*>(); - // Paranoia - test_ptr_cat<0, base_class, derived_class>(); - test_ptr_cat<0, base_class*, derived_class*>(); - - // Don't allow conversions between pointers to distinct types, even if - // those types have the same representation. That is: - // unsigned int * a = meow; - // int * b = purr; - // a = b; - // won't compile, so we don't want copy-like algorithms to compile - // by reinterpret_cast-ing to char *: - test_ptr_cat<0, int*, unsigned int*>(); - test_ptr_cat<0, int*, const unsigned int*>(); - - // Similarly, don't allow enum class types to be implicitly converted to/from - // integral types: - test_ptr_cat<0, int_enum_class, int>(); - test_ptr_cat<0, int, int_enum_class>(); - test_ptr_cat<0, short_enum_class, short>(); - test_ptr_cat<0, bool_enum_class, bool>(); - test_ptr_cat<0, bool, bool_enum_class>(); - test_ptr_cat<0, bool_enum, bool_enum_class>(); - test_ptr_cat<0, bool_enum_class, bool_enum>(); - - // bool always generates code when you inspect it, so special case it to the general thing: - test_ptr_cat<0, bool, char>(); - test_ptr_cat<0, char, bool>(); - test_ptr_cat<0, bool_enum, char>(); -} - -template -void test_case_Equal_memcmp_is_safe_comparator() { - // Default case - STATIC_ASSERT(_Equal_memcmp_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); - // 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); - // 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); - -#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, - 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, - 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, - Pr> == Expected); - } - - if constexpr (!is_same_v) { - STATIC_ASSERT(_Equal_memcmp_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, - Pr> == Expected); - } - // span iterators are contiguous - STATIC_ASSERT( - _Equal_memcmp_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, Pr> == Expected); - STATIC_ASSERT(_Equal_memcmp_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); - 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, 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, 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, - 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); -#endif // __cpp_lib_concepts - - // Non-contiguous iterators should explode - STATIC_ASSERT(_Equal_memcmp_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>(); -#ifdef __cpp_lib_concepts - test_case_Equal_memcmp_is_safe_comparator(); -#endif // __cpp_lib_concepts - - // equal_to< some other T > should explode - STATIC_ASSERT(_Equal_memcmp_is_safe>> == false); - // Non-equal_to comparison functions should explode - auto lambda = [](Elem1*, Elem2*) { return false; }; - STATIC_ASSERT(_Equal_memcmp_is_safe == false); - // equal_to should not explode - STATIC_ASSERT(_Equal_memcmp_is_safe> == (Expected && is_same_v) ); - // But again, not volatile - STATIC_ASSERT(_Equal_memcmp_is_safe> == false); - STATIC_ASSERT(_Equal_memcmp_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(); - // unless their sizes differ - test_case_Equal_memcmp_is_safe(); - test_case_Equal_memcmp_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(); - // 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(); - // 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(); - // 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(); - // No user-defined types - test_case_Equal_memcmp_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(); -#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(); - - // Pointers to not-the-same-type need to go to the general algorithm - test_case_Equal_memcmp_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(); -} - -template -void test_case_Lex_compare_optimize_helper() { - assert_same<_Lex_compare_optimize, - decltype(_Lex_compare_memcmp_classify(declval(), declval(), declval()))>(); -} - -template -void test_case_Lex_compare_optimize() { - // same cv-qualifiers song and dance as test_case_Equal_memcmp_is_safe - test_case_Lex_compare_optimize_helper(); - test_case_Lex_compare_optimize_helper(); - test_case_Lex_compare_optimize_helper(); - test_case_Lex_compare_optimize_helper(); - test_case_Lex_compare_optimize_helper(); - test_case_Lex_compare_optimize_helper(); - test_case_Lex_compare_optimize_helper(); - test_case_Lex_compare_optimize_helper(); - test_case_Lex_compare_optimize_helper(); - test_case_Lex_compare_optimize_helper(); - test_case_Lex_compare_optimize_helper(); - test_case_Lex_compare_optimize_helper(); - test_case_Lex_compare_optimize_helper(); - test_case_Lex_compare_optimize_helper(); - test_case_Lex_compare_optimize_helper(); - test_case_Lex_compare_optimize_helper(); - - // non-pointer iterators should get the general implementation - test_case_Lex_compare_optimize_helper::iterator, typename list::iterator, Pr>(); -} - -template