diff --git a/stl/inc/__msvc_ranges_tuple_formatter.hpp b/stl/inc/__msvc_ranges_tuple_formatter.hpp index e47a92f870..50098b0b36 100644 --- a/stl/inc/__msvc_ranges_tuple_formatter.hpp +++ b/stl/inc/__msvc_ranges_tuple_formatter.hpp @@ -1086,13 +1086,15 @@ struct _Range_default_formatter<_Kind, _Rng, _CharT> { template _FormatContext::iterator format(_Range_type& _Rx, _FormatContext& _Ctx) const { if constexpr (_RANGES contiguous_range<_Range_type>) { - const auto _Size = _STD _To_unsigned_like(_RANGES distance(_Rx)); + const auto _Dist = _RANGES distance(_Rx); + const auto _Size = _STD _To_unsigned_like(_Dist); if (!_STD in_range(_Size)) { _Throw_format_error("Formatted range is too long."); } - const basic_string_view<_CharT> _Str(_STD to_address(_RANGES begin(_Rx)), static_cast(_Size)); + const auto _First = _RANGES begin(_Rx); + const basic_string_view<_CharT> _Str(_STD to_address(_First), _STD to_address(_First + _Dist)); return _Underlying.format(_Str, _Ctx); } else { return _Underlying.format(basic_string<_CharT>{from_range, _Rx}, _Ctx); diff --git a/stl/inc/algorithm b/stl/inc/algorithm index d09fa5084c..e95528c127 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -465,7 +465,10 @@ constexpr bool _Vector_alg_in_unique_is_safe = _Vector_alg_in_search_is_safe<_It // Can we use this output iterator for remove_copy and unique_copy? template constexpr bool _Output_iterator_for_vector_alg_is_safe() { - if constexpr (_Iterator_is_contiguous<_Out>) { + // Before running remove_copy and unique_copy, the output size is unknown. This limits vectorization + // to pointers (after unwrapping). N5014 [iterator.requirements.general]/13 allows us to convert + // a range of contiguous iterators to pointers if and only if we know the size of the range ahead of time. + if constexpr (is_pointer_v<_Out>) { return is_same_v<_Iter_value_t<_Out>, remove_const_t<_Iter_value_t<_In>>>; } else { return false; @@ -886,7 +889,7 @@ namespace ranges { } const auto _First_ptr = _STD _To_address(_First); - const auto _Last_ptr = _First_ptr + static_cast(_Last - _First); + const auto _Last_ptr = _STD _To_address(_First + (_Last - _First)); return static_cast>(_STD _Count_vectorized(_First_ptr, _Last_ptr, _Val)); } @@ -984,8 +987,11 @@ _NODISCARD _CONSTEXPR20 pair<_InIt1, _InIt2> mismatch(_InIt1 _First1, const _InI if (!_STD _Is_constant_evaluated()) { constexpr size_t _Elem_size = sizeof(_Iter_value_t<_InIt1>); + const auto _Dist = _STD _Contiguous_iter_distance(_UFirst1, _ULast1); + _STD _Contiguous_iter_verify(_UFirst2, static_cast<_Iter_diff_t<_InIt2>>(_Dist)); + const size_t _Pos = _STD _Mismatch_vectorized<_Elem_size>( - _STD _To_address(_UFirst1), _STD _To_address(_UFirst2), static_cast(_ULast1 - _UFirst1)); + _STD _To_address(_UFirst1), _STD _To_address(_UFirst2), static_cast(_Dist)); _UFirst1 += static_cast<_Iter_diff_t<_InIt1>>(_Pos); _UFirst2 += static_cast<_Iter_diff_t<_InIt2>>(_Pos); @@ -1048,6 +1054,9 @@ _NODISCARD _CONSTEXPR20 pair<_InIt1, _InIt2> mismatch( if (!_STD _Is_constant_evaluated()) { constexpr size_t _Elem_size = sizeof(_Iter_value_t<_InIt1>); + (void) _STD _To_address(_ULast1); + (void) _STD _To_address(_ULast2); + const size_t _Pos = _STD _Mismatch_vectorized<_Elem_size>( _STD _To_address(_UFirst1), _STD _To_address(_UFirst2), static_cast(_Count)); @@ -2413,6 +2422,7 @@ _NODISCARD _CONSTEXPR20 _FwdItHaystack search(_FwdItHaystack _First1, _FwdItHays if constexpr (_Vector_alg_in_search_is_safe) { if (!_STD _Is_constant_evaluated()) { const auto _Ptr1 = _STD _To_address(_UFirst1); + (void) _STD _To_address(_ULast2); const auto _Ptr_res1 = _STD _Search_vectorized( _Ptr1, _STD _To_address(_ULast1), _STD _To_address(_UFirst2), static_cast(_Count2)); @@ -2722,7 +2732,7 @@ namespace ranges { } const auto _First_ptr = _STD _To_address(_First); - const auto _Last_ptr = _First_ptr + static_cast(_Dist); + const auto _Last_ptr = _STD _To_address(_First + _Dist); const auto _Result = _STD _Search_n_vectorized(_First_ptr, _Last_ptr, static_cast(_Count), _Val); @@ -3414,7 +3424,7 @@ namespace ranges { } const auto _First_ptr = _STD _To_address(_First); - const auto _Last_ptr = _First_ptr + static_cast(_Count); + const auto _Last_ptr = _STD _To_address(_First + _Count); const auto _Result = _STD _Find_last_vectorized(_First_ptr, _Last_ptr, _Value); if constexpr (is_pointer_v<_It>) { @@ -3568,6 +3578,7 @@ _NODISCARD _CONSTEXPR20 _FwdIt1 find_end( if constexpr (_Vector_alg_in_search_is_safe) { if (!_STD _Is_constant_evaluated()) { const auto _Ptr1 = _STD _To_address(_UFirst1); + (void) _STD _To_address(_ULast2); const auto _Ptr_res1 = _STD _Find_end_vectorized( _Ptr1, _STD _To_address(_ULast1), _STD _To_address(_UFirst2), static_cast(_Count2)); @@ -3691,9 +3702,11 @@ namespace ranges { if constexpr (_Vector_alg_in_search_is_safe<_It1, _It2, _Pr> && is_same_v<_Pj1, identity> && is_same_v<_Pj2, identity>) { if (!_STD is_constant_evaluated()) { + _STD _Contiguous_iter_verify(_First2, _Count2); + const auto _Ptr1 = _STD to_address(_First1); const auto _Ptr2 = _STD to_address(_First2); - const auto _Ptr_last1 = _Ptr1 + static_cast(_Count1); + const auto _Ptr_last1 = _STD to_address(_First1 + _Count1); const auto _Ptr_res1 = _STD _Find_end_vectorized(_Ptr1, _Ptr_last1, _Ptr2, static_cast(_Count2)); @@ -3995,13 +4008,11 @@ namespace ranges { if constexpr (_Vector_alg_in_search_is_safe<_It1, _It2, _Pr> && sized_sentinel_for<_Se1, _It1> && sized_sentinel_for<_Se2, _It2> && is_same_v<_Pj1, identity> && is_same_v<_Pj2, identity>) { if (!_STD is_constant_evaluated() && _Last1 - _First1 >= _Threshold_find_first_of) { - const auto _Count1 = static_cast(_Last1 - _First1); const auto _First1_ptr = _STD _To_address(_First1); - const auto _Last1_ptr = _First1_ptr + _Count1; + const auto _Last1_ptr = _STD _To_address(_First1 + (_Last1 - _First1)); - const auto _Count2 = static_cast(_Last2 - _First2); const auto _First2_ptr = _STD _To_address(_First2); - const auto _Last2_ptr = _First2_ptr + _Count2; + const auto _Last2_ptr = _STD _To_address(_First2 + (_Last2 - _First2)); const auto _Result = _STD _Find_first_of_vectorized(_First1_ptr, _Last1_ptr, _First2_ptr, _Last2_ptr); @@ -4065,20 +4076,25 @@ namespace ranges { if constexpr (_Is_sized1 && _Is_sized2) { const size_t _Count = (_STD min) (static_cast(_Last1 - _First1), static_cast(_Last2 - _First2)); - const auto _Last1_addr = _First1_addr + _Count; + const auto _Last1_addr = _STD to_address(_First1 + static_cast>(_Count)); + _STD _Contiguous_iter_verify(_First2, static_cast>(_Count)); ::__std_swap_ranges_trivially_swappable_noalias(_First1_addr, _Last1_addr, _First2_addr); return {_First1 + static_cast>(_Count), _First2 + static_cast>(_Count)}; } else if constexpr (_Is_sized1) { - const auto _Final1 = _RANGES next(_First1, _Last1); + const auto _Dist1 = _Last1 - _First1; + const auto _Final1 = _First1 + _Dist1; const auto _Last1_addr = _STD to_address(_Final1); + _STD _Contiguous_iter_verify(_First2, static_cast>(_Dist1)); ::__std_swap_ranges_trivially_swappable_noalias(_First1_addr, _Last1_addr, _First2_addr); - return {_Final1, _First2 + static_cast>(_Last1 - _First1)}; + return {_Final1, _First2 + static_cast>(_Dist1)}; } else if constexpr (_Is_sized2) { - const auto _Final2 = _RANGES next(_First2, _Last2); + const auto _Dist2 = _Last2 - _First2; + const auto _Final2 = _First2 + _Dist2; const auto _Last2_addr = _STD to_address(_Final2); + _STD _Contiguous_iter_verify(_First1, static_cast>(_Dist2)); ::__std_swap_ranges_trivially_swappable_noalias(_First2_addr, _Last2_addr, _First1_addr); - return {_First1 + static_cast>(_Last2 - _First2), _Final2}; + return {_First1 + static_cast>(_Dist2), _Final2}; } else { _STL_ASSERT(false, "Tried to swap_ranges with two unreachable sentinels"); } @@ -4463,7 +4479,7 @@ namespace ranges { if (_STD _Could_compare_equal_to_value_type<_It>(_Oldval)) { const auto _First_ptr = _STD to_address(_First); - const auto _Last_ptr = _First_ptr + static_cast(_Count); + const auto _Last_ptr = _STD to_address(_First + _Count); _STD _Replace_vectorized(_First_ptr, _Last_ptr, _Oldval, _Newval); } @@ -5078,7 +5094,7 @@ namespace ranges { if (!_STD is_constant_evaluated()) { const auto _Size = _Last - _First; const auto _First_ptr = _STD to_address(_First); - const auto _Last_ptr = _First_ptr + static_cast(_Size); + const auto _Last_ptr = _STD to_address(_First + _Size); const auto _Result = _STD _Remove_vectorized(_First_ptr, _Last_ptr, _Val); if constexpr (is_pointer_v<_It>) { @@ -5407,7 +5423,7 @@ namespace ranges { if (!_STD is_constant_evaluated()) { const auto _Size = _Last - _First; const auto _First_ptr = _STD to_address(_First); - const auto _Last_ptr = _First_ptr + static_cast(_Size); + const auto _Last_ptr = _STD to_address(_First + _Size); const auto _Result = _STD _Unique_vectorized(_First_ptr, _Last_ptr); if constexpr (is_pointer_v<_It>) { @@ -5627,7 +5643,7 @@ namespace ranges { if (!_STD is_constant_evaluated()) { const auto _Size = _Last - _First; const auto _First_ptr = _STD to_address(_First); - const auto _Last_ptr = _First_ptr + static_cast(_Size); + const auto _Last_ptr = _STD to_address(_First + _Size); const auto _Output_ptr = _STD to_address(_Output); const auto _Result = _STD _Unique_copy_vectorized(_First_ptr, _Last_ptr, _Output_ptr); @@ -5761,9 +5777,12 @@ _CONSTEXPR20 _OutIt reverse_copy(_BidIt _First, _BidIt _Last, _OutIt _Dest) { if (!_STD is_constant_evaluated()) #endif // _HAS_CXX20 { + const auto _Dest_dist = static_cast<_Iter_diff_t>(_ULast - _UFirst); + _STD _Contiguous_iter_verify(_UDest, _Dest_dist); + _STD _Reverse_copy_vectorized<_Nx>( _STD _To_address(_UFirst), _STD _To_address(_ULast), _STD _To_address(_UDest)); - _UDest += _ULast - _UFirst; + _UDest += _Dest_dist; _STD _Seek_wrapped(_Dest, _UDest); return _Dest; } @@ -5847,9 +5866,11 @@ namespace ranges { if constexpr (_Allow_vectorization && _Nx <= 8 && (_Nx & (_Nx - 1)) == 0) { if (!_STD is_constant_evaluated()) { + const auto _Dest_dist = static_cast>(_Last - _First); + _STD _Contiguous_iter_verify(_Output, _Dest_dist); _STD _Reverse_copy_vectorized<_Nx>( _STD to_address(_First), _STD to_address(_Last), _STD to_address(_Output)); - _Output += _Last - _First; + _Output += _Dest_dist; return _Output; } } @@ -8542,7 +8563,7 @@ namespace ranges { if constexpr (_Is_greater || _Is_predicate_less<_It, _Pr>) { if (!_STD is_constant_evaluated()) { const auto _First_ptr = _STD _To_address(_First); - const auto _Last_ptr = _First_ptr + static_cast(_Last - _First); + const auto _Last_ptr = _STD _To_address(_First + (_Last - _First)); const auto _Result = _STD _Is_sorted_until_vectorized(_First_ptr, _Last_ptr, _Is_greater); if constexpr (is_pointer_v<_It>) { @@ -10471,8 +10492,8 @@ namespace ranges { if (!_STD is_constant_evaluated()) { const auto _First1_ptr = _STD to_address(_First1); const auto _First2_ptr = _STD to_address(_First2); - const auto _Last1_ptr = _First1_ptr + static_cast(_Last1 - _First1); - const auto _Last2_ptr = _First2_ptr + static_cast(_Last2 - _First2); + const auto _Last1_ptr = _STD to_address(_First1 + (_Last1 - _First1)); + const auto _Last2_ptr = _STD to_address(_First2 + (_Last2 - _First2)); return _STD _Includes_vectorized(_First1_ptr, _Last1_ptr, _First2_ptr, _Last2_ptr); } } @@ -11195,7 +11216,7 @@ namespace ranges { && sized_sentinel_for<_Se, _It>) { if (!_STD is_constant_evaluated()) { const auto _First_ptr = _STD to_address(_First); - const auto _Last_ptr = _First_ptr + static_cast(_Last - _First); + const auto _Last_ptr = _STD to_address(_First + (_Last - _First)); const auto _Result = _STD _Minmax_element_vectorized(_First_ptr, _Last_ptr); if constexpr (is_pointer_v<_It>) { return {_Result.first, _Result.second}; @@ -11374,7 +11395,7 @@ namespace ranges { if constexpr (_Is_min_max_value_optimization_safe) { if (!_STD is_constant_evaluated()) { const auto _First_ptr = _STD to_address(_UFirst); - const auto _Last_ptr = _First_ptr + static_cast(_ULast - _UFirst); + const auto _Last_ptr = _STD to_address(_UFirst + (_ULast - _UFirst)); const auto _Result = _STD _Minmax_vectorized(_First_ptr, _Last_ptr); return {static_cast<_Vty>(_Result._Min), static_cast<_Vty>(_Result._Max)}; } @@ -11385,7 +11406,7 @@ namespace ranges { if constexpr (_Is_min_max_optimization_safe) { if (!_STD is_constant_evaluated()) { const auto _First_ptr = _STD to_address(_UFirst); - const auto _Last_ptr = _First_ptr + static_cast(_ULast - _UFirst); + const auto _Last_ptr = _STD to_address(_UFirst + (_ULast - _UFirst)); const auto _Result = _STD _Minmax_element_vectorized(_First_ptr, _Last_ptr); return { *static_cast(_Result.first), *static_cast(_Result.second)}; @@ -11963,14 +11984,14 @@ namespace ranges { if (!_STD is_constant_evaluated()) { size_t _Num1; if constexpr (_Is_sized1) { - _Num1 = static_cast(_Last1 - _First1); + _Num1 = static_cast(_STD _Contiguous_iter_distance(_First1, _Last1)); } else { _Num1 = SIZE_MAX; } size_t _Num2; if constexpr (_Is_sized2) { - _Num2 = static_cast(_Last2 - _First2); + _Num2 = static_cast(_STD _Contiguous_iter_distance(_First2, _Last2)); } else { _Num2 = SIZE_MAX; } diff --git a/stl/inc/format b/stl/inc/format index 9a111a85d2..472d3017e9 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -3367,9 +3367,10 @@ void _Range_formatter_format_as_sequence(const formatter<_Ty, _CharT>& _Underlyi template void _Range_formatter_format_as_string(_Range&& _Rng, _FormatContext& _Ctx, const bool _Debug) { if constexpr (_RANGES contiguous_range<_Range>) { - const auto _Size = _STD _To_unsigned_like(_RANGES distance(_Rng)); + const auto _Dist = _RANGES distance(_Rng); #ifdef _DEBUG + const auto _Size = _STD _To_unsigned_like(_Dist); if constexpr (sizeof(_Size) > sizeof(size_t)) { _STL_VERIFY(_Size <= size_t{PTRDIFF_MAX}, "contiguous range has impossible size."); } @@ -3380,7 +3381,8 @@ void _Range_formatter_format_as_string(_Range&& _Rng, _FormatContext& _Ctx, cons _String_view_formatter.set_debug_format(); } - const basic_string_view<_CharT> _Str(_STD to_address(_RANGES begin(_Rng)), static_cast(_Size)); + const auto _First = _RANGES begin(_Rng); + const basic_string_view<_CharT> _Str(_STD to_address(_First), _STD to_address(_First + _Dist)); _String_view_formatter.format(_Str, _Ctx); } else { using _String = basic_string<_CharT>; diff --git a/stl/inc/functional b/stl/inc/functional index 9c61372fcd..01253a856d 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -2527,6 +2527,7 @@ _CONSTEXPR20 pair<_FwdItHaystack, _FwdItHaystack> _Search_pair_unchecked( if constexpr (_Vector_alg_in_search_is_safe<_FwdItHaystack, _FwdItPat, _Pred_eq>) { if (!_STD _Is_constant_evaluated()) { const auto _Ptr1 = _STD _To_address(_First1); + (void) _STD _To_address(_Last2); const auto _Ptr_res1 = _STD _Search_vectorized( _Ptr1, _STD _To_address(_Last1), _STD _To_address(_First2), static_cast(_Count2)); diff --git a/stl/inc/memory b/stl/inc/memory index d527ead02b..7abee62f02 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -102,12 +102,14 @@ namespace ranges { return _RANGES _Copy_memcpy_common(_IFirst, _RANGES next(_IFirst, _STD move(_ILast)), _OFirst, _RANGES next(_OFirst, _STD move(_OLast))); } else if constexpr (_Is_sized1) { - return _RANGES _Copy_memcpy_distance( - _IFirst, _OFirst, _IFirst, _RANGES next(_IFirst, _STD move(_ILast))); + const auto _Dist = _ILast - _IFirst; + _STD _Contiguous_iter_verify(_OFirst, static_cast>(_Dist)); + return _RANGES _Copy_memcpy_distance(_IFirst, _OFirst, _IFirst, _IFirst + _Dist); } else { _STL_INTERNAL_STATIC_ASSERT(_Is_sized2); - return _RANGES _Copy_memcpy_distance( - _IFirst, _OFirst, _OFirst, _RANGES next(_OFirst, _STD move(_OLast))); + const auto _Dist = _OLast - _OFirst; + _STD _Contiguous_iter_verify(_IFirst, static_cast>(_Dist)); + return _RANGES _Copy_memcpy_distance(_IFirst, _OFirst, _OFirst, _OFirst + _Dist); } } else { if constexpr (_Can_memcpy) { @@ -200,6 +202,8 @@ namespace ranges { _IFirst = _STD move(_UResult.in); _OFirst = _STD move(_UResult.out); } else { + _STD _Contiguous_iter_verify(_IFirst, _Count); + _STD _Contiguous_iter_verify(_OFirst, static_cast>(_Count)); auto _UResult = _RANGES _Copy_memcpy_count(_IFirst, _OFirst, static_cast(_Count)); _IFirst = _STD move(_UResult.in); _OFirst = _STD move(_UResult.out); @@ -363,6 +367,8 @@ namespace ranges { _IFirst = _STD move(_UResult.in); _OFirst = _STD move(_UResult.out); } else { + _STD _Contiguous_iter_verify(_IFirst, _Count); + _STD _Contiguous_iter_verify(_OFirst, static_cast>(_Count)); auto _UResult = _RANGES _Copy_memcpy_count(_IFirst, _OFirst, static_cast(_Count)); _IFirst = _STD move(_UResult.in); _OFirst = _STD move(_UResult.out); diff --git a/stl/inc/numeric b/stl/inc/numeric index d9a04eaa4b..c8a6a4a4ff 100644 --- a/stl/inc/numeric +++ b/stl/inc/numeric @@ -489,21 +489,25 @@ _CONSTEXPR20 _OutIt adjacent_difference(const _InIt _First, const _InIt _Last, _ if (!_STD is_constant_evaluated()) #endif { + const auto _Dest_dist = static_cast<_Iter_diff_t>(_ULast - _UFirst); + _STD _Contiguous_iter_verify(_UDest, _Dest_dist); + // Go with pointers and without loop-carried dependency to enable vectorization const auto _First_ptr = _STD _To_address(_UFirst); const auto _Last_ptr = _STD _To_address(_ULast); const auto _Dest_ptr = _STD _To_address(_UDest); - const auto _Count = _Last_ptr - _First_ptr; // Need to perform aliasing analysis. // The vectorizer is generally able to do that on its own, and would guard the vectorized code with // that, but when we eliminate the loop-carried dependency we change the semantics of the unvectorized // code too. So we need to perform this check manually, and after that we can tell the compiler that // there's no aliasing, to avoid it checking for that again. - if (reinterpret_cast(_Dest_ptr + _Count) <= reinterpret_cast(_First_ptr) + if (reinterpret_cast(_Dest_ptr + static_cast(_Dest_dist)) + <= reinterpret_cast(_First_ptr) || reinterpret_cast(_Last_ptr) <= reinterpret_cast(_Dest_ptr)) { - _STD _Adjacent_difference_no_overlap(_Dest_ptr, _First_ptr, _Count, _STD _Pass_fn(_Func)); - _STD _Seek_wrapped(_Dest, _UDest + _Count); + _STD _Adjacent_difference_no_overlap( + _Dest_ptr, _First_ptr, static_cast(_Dest_dist), _STD _Pass_fn(_Func)); + _STD _Seek_wrapped(_Dest, _UDest + _Dest_dist); return _Dest; } } @@ -556,7 +560,8 @@ _CONSTEXPR20 void iota(_FwdIt _First, _FwdIt _Last, _Ty _Val) { if constexpr (_Iterator_is_contiguous && is_integral_v<_Ty> && sizeof(_Ty) >= 4) { // TRANSITION, DevCom-10593477: help the compiler vectorize - const auto _Ptr = _To_address(_UFirst); + const auto _Ptr = _To_address(_UFirst); + (void) _STD _To_address(_ULast); const auto _Size = static_cast(_ULast - _UFirst); if (_STD _In_range<_Ty>(_Size)) { @@ -611,7 +616,8 @@ namespace ranges { && sizeof(_Ty) >= 4) { // TRANSITION, DevCom-10593477: help the compiler vectorize const auto _Ptr = _To_address(_First); - const auto _Size = static_cast(_Last - _First); + const auto _Dist = _STD _Contiguous_iter_distance(_First, _Last); + const auto _Size = static_cast(_Dist); if (_STD _In_range<_Ty>(_Size)) { const auto _Size_typed = static_cast<_Ty>(_Size); @@ -621,7 +627,7 @@ namespace ranges { } _Val += _Size_typed; - return _First + _Size; + return _First + _Dist; } } diff --git a/stl/inc/xmemory b/stl/inc/xmemory index c73653f10b..138e2a62d1 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -1720,6 +1720,7 @@ namespace ranges { template in_out_result<_InIt, _OutIt> _Copy_memcpy_count(_InIt _IFirst, _OutIt _OFirst, const size_t _Count) noexcept { + // (pre-verified contiguous iterator) const auto _IFirstPtr = _STD _To_address(_IFirst); const auto _OFirstPtr = _STD _To_address(_OFirst); const auto _IFirst_ch = const_cast(reinterpret_cast(_IFirstPtr)); @@ -1743,6 +1744,7 @@ namespace ranges { template in_out_result<_InIt, _OutIt> _Copy_memcpy_distance( _InIt _IFirst, _OutIt _OFirst, const _DistIt _DFirst, const _DistIt _DLast) noexcept { + // (pre-verified contiguous iterator) // equivalent to _Copy_memcpy_count(_IFirst, _OFirst, _DLast - _DFirst) but computes distance more efficiently const auto _IFirstPtr = _STD _To_address(_IFirst); const auto _OFirstPtr = _STD _To_address(_OFirst); @@ -1757,13 +1759,13 @@ namespace ranges { if constexpr (is_pointer_v<_InIt>) { _IFirst = reinterpret_cast<_InIt>(_IFirst_ch + _Count_bytes); } else { - _IFirst += _Count_bytes / sizeof(iter_value_t<_InIt>); + _IFirst += static_cast>(_Count_bytes / sizeof(iter_value_t<_InIt>)); } if constexpr (is_pointer_v<_OutIt>) { _OFirst = reinterpret_cast<_OutIt>(_OFirst_ch + _Count_bytes); } else { - _OFirst += _Count_bytes / sizeof(iter_value_t<_OutIt>); + _OFirst += static_cast>(_Count_bytes / sizeof(iter_value_t<_OutIt>)); } return {_STD move(_IFirst), _STD move(_OFirst)}; } @@ -1813,12 +1815,14 @@ namespace ranges { return _RANGES _Copy_memcpy_common(_IFirst, _RANGES next(_IFirst, _STD move(_ILast)), _OFirst, _RANGES next(_OFirst, _STD move(_OLast))); } else if constexpr (_Is_sized1) { - return _RANGES _Copy_memcpy_distance( - _IFirst, _OFirst, _IFirst, _RANGES next(_IFirst, _STD move(_ILast))); + const auto _Dist = _ILast - _IFirst; + _STD _Contiguous_iter_verify(_OFirst, static_cast>(_Dist)); + return _RANGES _Copy_memcpy_distance(_IFirst, _OFirst, _IFirst, _IFirst + _Dist); } else { _STL_INTERNAL_STATIC_ASSERT(_Is_sized2); - return _RANGES _Copy_memcpy_distance( - _IFirst, _OFirst, _OFirst, _RANGES next(_OFirst, _STD move(_OLast))); + const auto _Dist = _OLast - _OFirst; + _STD _Contiguous_iter_verify(_IFirst, static_cast>(_Dist)); + return _RANGES _Copy_memcpy_distance(_IFirst, _OFirst, _OFirst, _OFirst + _Dist); } } else { if constexpr (_Can_memcpy) { @@ -1906,7 +1910,7 @@ _CONSTEXPR20 _Alloc_ptr_t<_Alloc> _Uninitialized_copy( _STD _Copy_memmove(_STD _To_address(_UFirst), _STD _To_address(_ULast), _STD _Unfancy(_Dest)); _Dest += _ULast - _UFirst; } else { - const auto _Count = static_cast(_ULast - _UFirst); + const auto _Count = static_cast(_STD _Contiguous_iter_distance(_UFirst, _ULast)); _STD _Copy_memmove_n(_STD _To_address(_UFirst), _Count, _STD _Unfancy(_Dest)); _Dest += _Count; } diff --git a/stl/inc/xutility b/stl/inc/xutility index da3e663bde..7a6dd5a3ff 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -4801,6 +4801,38 @@ _NODISCARD constexpr auto _To_address(const move_iterator<_Iter>& _Val) noexcept return _To_address(_Val.base()); } +template +_NODISCARD constexpr _Iter_diff_t<_Iter> _Contiguous_iter_distance(const _Iter& _First, const _Sent& _Last) + noexcept(is_same_v<_Iter, _Sent> || noexcept(_First + (_Last - _First))) { + _STL_INTERNAL_STATIC_ASSERT(_Iterator_is_contiguous<_Iter>); + if constexpr (is_same_v<_Iter, _Sent>) { + return static_cast<_Iter_diff_t<_Iter>>(_STD _To_address(_Last) - _STD _To_address(_First)); + } else { + return static_cast<_Iter_diff_t<_Iter>>(_STD _To_address(_First + (_Last - _First)) - _STD _To_address(_First)); + } +} + +template +_NODISCARD constexpr auto _Contiguous_iter_distance(const move_iterator<_Iter>& _First, const _Sent& _Last) + noexcept(noexcept(_STD _Contiguous_iter_distance(_First.base(), _Last))) { + return _STD _Contiguous_iter_distance(_First.base(), _Last); +} + +template +constexpr void _Contiguous_iter_verify(const _Iter& _Val, const _Iter_diff_t<_Iter>& _Off) + noexcept(noexcept(_Val + _Off)) { + _STL_INTERNAL_STATIC_ASSERT(_Iterator_is_contiguous<_Iter>); + if constexpr (!is_pointer_v<_Iter>) { + (void) _STD _To_address(_Val + _Off); + } +} + +template +constexpr void _Contiguous_iter_verify(const move_iterator<_Iter>& _Val, const _Iter_diff_t<_Iter>& _Off) + noexcept(noexcept(_STD _Contiguous_iter_verify(_Val.base(), _Off))) { + _STD _Contiguous_iter_verify(_Val.base(), _Off); +} + // _Iterators_are_contiguous<_Iter1, _Iter2> reports whether both iterators are known to be contiguous. template @@ -4934,6 +4966,7 @@ template _OutCtgIt _Copy_memmove_tail( const char* const _First_ch, const _OutCtgIt _Dest, const size_t _Byte_count, const size_t _Object_count) { _STL_INTERNAL_CHECK(_Byte_count == _Object_count * sizeof(*_Dest)); + // (pre-verified contiguous iterator) const auto _Dest_ptr = _STD _To_address(_Dest); const auto _Dest_ch = const_cast(reinterpret_cast(_Dest_ptr)); _CSTD memmove(_Dest_ch, _First_ch, _Byte_count); @@ -4951,15 +4984,19 @@ _OutCtgIt _Copy_memmove(_CtgIt _First, _CtgIt _Last, _OutCtgIt _Dest) { _STL_INTERNAL_CHECK(_First <= _Last); const auto _First_ptr = _STD _To_address(_First); const auto _Last_ptr = _STD _To_address(_Last); - const auto _Object_count = static_cast(_Last_ptr - _First_ptr); + const auto _Ptr_diff = _Last_ptr - _First_ptr; + const auto _Object_count = static_cast(_Ptr_diff); const auto _First_ch = const_cast(reinterpret_cast(_First_ptr)); const auto _Last_ch = const_cast(reinterpret_cast(_Last_ptr)); const auto _Byte_count = static_cast(_Last_ch - _First_ch); + _STD _Contiguous_iter_verify(_Dest, static_cast<_Iter_diff_t<_OutCtgIt>>(_Ptr_diff)); return _STD _Copy_memmove_tail(_First_ch, _STD move(_Dest), _Byte_count, _Object_count); } template _OutCtgIt _Copy_memmove_n(_CtgIt _First, const size_t _Object_count, _OutCtgIt _Dest) { + _STD _Contiguous_iter_verify(_First, static_cast<_Iter_diff_t<_CtgIt>>(_Object_count)); + _STD _Contiguous_iter_verify(_Dest, static_cast<_Iter_diff_t<_OutCtgIt>>(_Object_count)); const auto _First_ptr = _STD _To_address(_First); const auto _First_ch = const_cast(reinterpret_cast(_First_ptr)); const auto _Byte_count = _Object_count * sizeof(*_First_ptr); @@ -5175,9 +5212,10 @@ namespace ranges { _Output = _STD _Copy_memmove(_STD move(_First), _Last, _STD move(_Output)); return {_STD move(_Last), _STD move(_Output)}; } else { - const auto _Count = static_cast(_Last - _First); + const auto _Dist = _Last - _First; + const auto _Count = static_cast(_Dist); _Output = _STD _Copy_memmove_n(_First, _Count, _STD move(_Output)); - _First += _Count; + _First += _Dist; return {_STD move(_First), _STD move(_Output)}; } } @@ -5280,12 +5318,13 @@ _CtgIt2 _Copy_backward_memmove(_CtgIt1 _First, _CtgIt1 _Last, _CtgIt2 _Dest) { // implement copy_backward-like function as memmove const auto _First_ptr = _STD _To_address(_First); const auto _Last_ptr = _STD _To_address(_Last); - const auto _Dest_ptr = _STD _To_address(_Dest); - const auto _First_ch = const_cast(reinterpret_cast(_First_ptr)); - const auto _Last_ch = const_cast(reinterpret_cast(_Last_ptr)); - const auto _Dest_ch = const_cast(reinterpret_cast(_Dest_ptr)); - const auto _Count = static_cast(_Last_ch - _First_ch); - const auto _Result = _CSTD memmove(_Dest_ch - _Count, _First_ch, _Count); + _STD _Contiguous_iter_verify(_Dest, static_cast<_Iter_diff_t<_CtgIt2>>(_First_ptr - _Last_ptr)); + const auto _Dest_ptr = _STD _To_address(_Dest); + const auto _First_ch = const_cast(reinterpret_cast(_First_ptr)); + const auto _Last_ch = const_cast(reinterpret_cast(_Last_ptr)); + const auto _Dest_ch = const_cast(reinterpret_cast(_Dest_ptr)); + const auto _Count = static_cast(_Last_ch - _First_ch); + const auto _Result = _CSTD memmove(_Dest_ch - _Count, _First_ch, _Count); if constexpr (is_pointer_v<_CtgIt2>) { return static_cast<_CtgIt2>(_Result); } else { @@ -5456,6 +5495,8 @@ constexpr bool _Fill_zero_memset_is_safe<_FwdIt, _Ty, false> = false; template void _Fill_memset(_CtgIt _Dest, const _Ty _Val, const size_t _Count) { + _STL_INTERNAL_STATIC_ASSERT(sizeof(_Iter_value_t<_CtgIt>) == sizeof(unsigned char)); + _STD _Contiguous_iter_verify(_Dest, static_cast<_Iter_diff_t<_CtgIt>>(_Count)); // implicitly convert (a cast would suppress warnings); also handles _Iter_value_t<_CtgIt> being bool _Iter_value_t<_CtgIt> _Dest_val = _Val; _CSTD memset(_STD _To_address(_Dest), static_cast(_Dest_val), _Count); @@ -5463,6 +5504,7 @@ void _Fill_memset(_CtgIt _Dest, const _Ty _Val, const size_t _Count) { template void _Fill_zero_memset(_CtgIt _Dest, const size_t _Count) { + _STD _Contiguous_iter_verify(_Dest, static_cast<_Iter_diff_t<_CtgIt>>(_Count)); _CSTD memset(_STD _To_address(_Dest), 0, _Count * sizeof(_Iter_value_t<_CtgIt>)); } @@ -5707,6 +5749,8 @@ constexpr bool _Vector_alg_in_search_is_safe = template _NODISCARD int _Memcmp_count(_CtgIt1 _First1, _CtgIt2 _First2, const size_t _Count) { + _STD _Contiguous_iter_verify(_First1, static_cast<_Iter_diff_t<_CtgIt1>>(_Count)); + _STD _Contiguous_iter_verify(_First2, static_cast<_Iter_diff_t<_CtgIt2>>(_Count)); _STL_INTERNAL_STATIC_ASSERT(sizeof(_Iter_value_t<_CtgIt1>) == sizeof(_Iter_value_t<_CtgIt2>)); const auto _First1_ch = reinterpret_cast(_STD _To_address(_First1)); const auto _First2_ch = reinterpret_cast(_STD _To_address(_First2)); @@ -5727,6 +5771,8 @@ _NODISCARD _CONSTEXPR20 bool equal(const _InIt1 _First1, const _InIt1 _Last1, co { _STL_INTERNAL_STATIC_ASSERT( sizeof(_Iter_value_t) == sizeof(_Iter_value_t)); + _STD _Contiguous_iter_verify(_UFirst2, static_cast<_Iter_diff_t<_InIt2>>(_ULast1 - _UFirst1)); + const auto _First1_ch = reinterpret_cast(_STD _To_address(_UFirst1)); const auto _Size = reinterpret_cast(_STD _To_address(_ULast1)) - _First1_ch; return _CSTD memcmp(_First1_ch, _STD _To_address(_UFirst2), static_cast(_Size)) == 0; @@ -5864,6 +5910,10 @@ namespace ranges { if constexpr (_Equal_memcmp_is_safe<_It1, _It2, _Pr> && is_same_v<_Pj1, identity> && is_same_v<_Pj2, identity>) { if (!_STD is_constant_evaluated()) { + _STL_INTERNAL_STATIC_ASSERT(sizeof(iter_value_t<_It1>) == sizeof(iter_value_t<_It2>)); + _STD _Contiguous_iter_verify(_First1, _Count); + _STD _Contiguous_iter_verify(_First2, static_cast>(_Count)); + constexpr size_t _Elem_size = sizeof(iter_value_t<_It1>); const size_t _Pos = _STD _Mismatch_vectorized<_Elem_size>( @@ -6050,8 +6100,8 @@ _NODISCARD _CONSTEXPR20 bool lexicographical_compare( if (!_STD is_constant_evaluated()) #endif // _HAS_CXX20 { - const auto _Num1 = static_cast(_ULast1 - _UFirst1); - const auto _Num2 = static_cast(_ULast2 - _UFirst2); + const auto _Num1 = static_cast(_STD _Contiguous_iter_distance(_UFirst1, _ULast1)); + const auto _Num2 = static_cast(_STD _Contiguous_iter_distance(_UFirst2, _ULast2)); const size_t _Num = (_STD min) (_Num1, _Num2); #if _VECTORIZED_MISMATCH const auto _First1_ptr = _STD _To_address(_UFirst1); @@ -6175,8 +6225,8 @@ _NODISCARD constexpr auto lexicographical_compare_three_way(const _InIt1 _First1 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 auto _Num1 = static_cast(_STD _Contiguous_iter_distance(_UFirst1, _ULast1)); + const auto _Num2 = static_cast(_STD _Contiguous_iter_distance(_UFirst2, _ULast2)); const size_t _Num = (_STD min) (_Num1, _Num2); #if _VECTORIZED_MISMATCH const auto _First1_ptr = _STD to_address(_UFirst1); @@ -6350,7 +6400,7 @@ _NODISCARD _CONSTEXPR20 _InIt _Find_unchecked(_InIt _First, const _InIt _Last, c #else // ^^^ _VECTORIZED_FIND / !_VECTORIZED_FIND vvv if constexpr (sizeof(_Iter_value_t<_InIt>) <= 2) { const auto _First_ptr = _STD _To_address(_First); - const auto _Count = static_cast(_Last - _First); + const auto _Count = static_cast(_STD _Contiguous_iter_distance(_First, _Last)); using _Ptr_t = remove_reference_t<_Iter_ref_t<_InIt>>*; _Ptr_t _Result; @@ -6437,21 +6487,23 @@ namespace ranges { #if _VECTORIZED_FIND if constexpr (_Is_sized) { // Prefer this over memchr()/wmemchr() below for performance - const auto _Last_ptr = _First_ptr + static_cast(_Last - _First); + const auto _Last_ptr = _STD _To_address(_First + (_Last - _First)); _Result = _STD _Find_vectorized(_First_ptr, _Last_ptr, _Val); } else #endif // ^^^ _VECTORIZED_FIND ^^^ { if constexpr (_Elements_are_2_bytes) { _STL_INTERNAL_STATIC_ASSERT(_Is_sized); - _Result = reinterpret_cast<_Ptr_t>( - const_cast(_CSTD wmemchr(reinterpret_cast(_First_ptr), - _STD _Find_arg_cast(_Val), static_cast(_Last - _First)))); + const auto _Wchar_ptr = reinterpret_cast(_First_ptr); + const auto _Wchar_val = _STD _Find_arg_cast(_Val); + const auto _Dist = static_cast(_STD _Contiguous_iter_distance(_First, _Last)); + const auto _Const_result = _CSTD wmemchr(_Wchar_ptr, _Wchar_val, _Dist); + _Result = reinterpret_cast<_Ptr_t>(const_cast(_Const_result)); } else { _STL_INTERNAL_STATIC_ASSERT(_Elements_are_1_byte); size_t _Count; if constexpr (_Is_sized) { - _Count = static_cast(_Last - _First); + _Count = static_cast(_STD _Contiguous_iter_distance(_First, _Last)); } else { _Count = SIZE_MAX; } @@ -7029,7 +7081,7 @@ namespace ranges { && is_same_v<_Pj, identity>) { if (!_STD is_constant_evaluated()) { const auto _First_ptr = _STD _To_address(_First); - const auto _Last_ptr = _First_ptr + static_cast(_Last - _First); + const auto _Last_ptr = _STD _To_address(_First + (_Last - _First)); const auto _Result = _STD _Adjacent_find_vectorized(_First_ptr, _Last_ptr); if constexpr (is_pointer_v<_It>) { @@ -7132,6 +7184,8 @@ namespace ranges { if constexpr (_Vector_alg_in_search_is_safe<_It1, _It2, _Pr> && is_same_v<_Pj1, identity> && is_same_v<_Pj2, identity>) { if (!_STD is_constant_evaluated()) { + _STD _Contiguous_iter_verify(_First2, _Count2); + const auto _Ptr1 = _STD to_address(_First1); const auto _Ptr2 = _STD to_address(_First2); remove_const_t _Ptr_last1; @@ -7139,7 +7193,7 @@ namespace ranges { if constexpr (is_same_v<_It1, _Se1>) { _Ptr_last1 = _STD to_address(_Last1); } else { - _Ptr_last1 = _Ptr1 + _Count1; + _Ptr_last1 = _STD to_address(_First1 + _Count1); } const auto _Ptr_res1 = @@ -7327,7 +7381,7 @@ namespace ranges { && sized_sentinel_for<_Se, _It>) { if (!_STD is_constant_evaluated()) { const auto _First_ptr = _STD to_address(_First); - const auto _Last_ptr = _First_ptr + static_cast(_Last - _First); + const auto _Last_ptr = _STD to_address(_First + (_Last - _First)); const auto _Result = _STD _Max_element_vectorized(_First_ptr, _Last_ptr); if constexpr (is_pointer_v<_It>) { return _Result; @@ -7455,7 +7509,7 @@ namespace ranges { && sized_sentinel_for) { if (!_STD is_constant_evaluated()) { const auto _First_ptr = _STD to_address(_UFirst); - const auto _Last_ptr = _First_ptr + static_cast(_ULast - _UFirst); + const auto _Last_ptr = _STD to_address(_UFirst + (_ULast - _UFirst)); return static_cast>(_STD _Max_vectorized(_First_ptr, _Last_ptr)); } } @@ -7551,7 +7605,7 @@ namespace ranges { && sized_sentinel_for<_Se, _It>) { if (!_STD is_constant_evaluated()) { const auto _First_ptr = _STD to_address(_First); - const auto _Last_ptr = _First_ptr + static_cast(_Last - _First); + const auto _Last_ptr = _STD to_address(_First + (_Last - _First)); const auto _Result = _STD _Min_element_vectorized(_First_ptr, _Last_ptr); if constexpr (is_pointer_v<_It>) { return _Result; @@ -7673,7 +7727,7 @@ namespace ranges { && sized_sentinel_for) { if (!_STD is_constant_evaluated()) { const auto _First_ptr = _STD to_address(_UFirst); - const auto _Last_ptr = _First_ptr + static_cast(_ULast - _UFirst); + const auto _Last_ptr = _STD to_address(_UFirst + (_ULast - _UFirst)); return static_cast>(_STD _Min_vectorized(_First_ptr, _Last_ptr)); } } @@ -7767,9 +7821,11 @@ _CONSTEXPR20 _FwdIt2 _Swap_ranges_unchecked(_FwdIt1 _First1, const _FwdIt1 _Last if (!_STD is_constant_evaluated()) #endif // _HAS_CXX20 { + const auto _Dist2 = static_cast<_Iter_diff_t<_FwdIt2>>(_Last1 - _First1); + _STD _Contiguous_iter_verify(_First2, _Dist2); ::__std_swap_ranges_trivially_swappable_noalias( _STD _To_address(_First1), _STD _To_address(_Last1), _STD _To_address(_First2)); - return _First2 + (_Last1 - _First1); + return _First2 + _Dist2; } } #endif // ^^^ _VECTORIZED_SWAP_RANGES ^^^ diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 1ebedc94a3..90d0fbe0d4 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -319,6 +319,7 @@ // P3136R1 Retiring Niebloids // P3323R1 Forbid atomic, Specify atomic_ref // (for atomic_ref) +// P3349R1 Converting Contiguous Iterators To Pointers // _HAS_CXX20 indirectly controls: // P0619R4 Removing C++17-Deprecated Features diff --git a/tests/std/test.lst b/tests/std/test.lst index 3d13fe2431..c304b1419f 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -722,6 +722,7 @@ tests\P2693R1_text_formatting_header_thread tests\P2693R1_text_formatting_stacktrace tests\P2693R1_text_formatting_thread_id tests\P3107R5_enabled_specializations +tests\P3349R1_contiguous_iterators_to_pointers tests\P3503R3_packaged_task_promise_with_allocator tests\VSO_0000000_allocator_propagation tests\VSO_0000000_any_calling_conventions diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/env.lst b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/env.lst new file mode 100644 index 0000000000..e05d343051 --- /dev/null +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/env.lst @@ -0,0 +1,7 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_20_matrix.lst +RUNALL_CROSSLIST +* PM_CL="" +* PM_CL="/D_USE_STD_VECTOR_ALGORITHMS=0" diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp new file mode 100644 index 0000000000..a513602370 --- /dev/null +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp @@ -0,0 +1,507 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +struct safe_iter_out_of_bounds_err {}; + +enum class range_type { range_large = 5, range_medium = 4, range_small = 3 }; + +bool safe_iter_nothrow_OOB_sign = false; + +template +constexpr T* unwrap(T* ptr) noexcept { + return ptr; +} + +template +class safe_iter { +public: + using iterator_concept = contiguous_iterator_tag; + using iterator_category = random_access_iterator_tag; + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; + + constexpr safe_iter(pointer ptr, pointer range_first, pointer range_last) noexcept + : current_ptr{ptr}, range_first_ptr{range_first}, range_last_ptr{range_last} { + assert(current_ptr >= range_first_ptr); + assert(current_ptr <= range_last_ptr); + } + constexpr safe_iter() noexcept = default; + + constexpr reference operator*() const noexcept(Nothrow) { + if (current_ptr < range_first_ptr || current_ptr >= range_last_ptr) { + if constexpr (Nothrow) { + safe_iter_nothrow_OOB_sign = true; + } else { + throw safe_iter_out_of_bounds_err{}; + } + } + return *current_ptr; + } + constexpr pointer operator->() const noexcept { // must always be noexcept, see N5014 [pointer.conversion]/3 + if constexpr (Nothrow) { + if (current_ptr < range_first_ptr || current_ptr > range_last_ptr) { + safe_iter_nothrow_OOB_sign = true; + } + } + return current_ptr; + } + constexpr reference operator[](difference_type n) const noexcept(Nothrow) { + return *(*this + n); + } + + // When `Nothrow` is true, disables bounds checking during iterator increment/decrement + // (to verify `to_address` calls), logging out-of-bounds accesses instead of throwing. + // When `Nothrow` is false, throws immediately on bounds violation. + constexpr safe_iter& operator+=(difference_type offset) noexcept(Nothrow) { + current_ptr += offset; + if constexpr (!Nothrow) { + if (current_ptr < range_first_ptr || current_ptr > range_last_ptr) { + throw safe_iter_out_of_bounds_err{}; + } + } + return *this; + } + constexpr safe_iter operator+(difference_type offset) const noexcept(Nothrow) { + auto tmp = *this; + tmp += offset; + return tmp; + } + friend constexpr safe_iter operator+(difference_type offset, const safe_iter& right) noexcept(Nothrow) { + return right + offset; + } + constexpr safe_iter& operator-=(difference_type offset) noexcept(Nothrow) { + return *this += -offset; + } + constexpr safe_iter operator-(difference_type offset) const noexcept(Nothrow) { + return *this + (-offset); + } + constexpr difference_type operator-(const safe_iter& right) const noexcept { + return current_ptr - right.current_ptr; + } + + constexpr safe_iter& operator++() noexcept(Nothrow) { + return *this += 1; + } + constexpr safe_iter& operator--() noexcept(Nothrow) { + return *this -= 1; + } + constexpr safe_iter operator++(int) noexcept(Nothrow) { + auto temp = *this; + ++*this; + return temp; + } + constexpr safe_iter operator--(int) noexcept(Nothrow) { + auto temp = *this; + --*this; + return temp; + } + + constexpr bool operator==(const safe_iter& right) const noexcept { + return current_ptr == right.current_ptr; + } + constexpr auto operator<=>(const safe_iter& right) const noexcept { + return current_ptr <=> right.current_ptr; + } + + constexpr difference_type operator-(pointer right) const noexcept { + return current_ptr - right; + } + friend constexpr difference_type operator-(pointer left, const safe_iter& right) noexcept { + return left - right.current_ptr; + } + constexpr bool operator==(pointer right) const noexcept { + return current_ptr == right; + } + + static constexpr safe_iter begin(ranges::contiguous_range auto&& rng) noexcept { + return {to_address(ranges::begin(rng)), to_address(ranges::begin(rng)), to_address(ranges::end(rng))}; + } + static constexpr safe_iter end(ranges::contiguous_range auto&& rng) noexcept { + return {to_address(ranges::end(rng)), to_address(ranges::begin(rng)), to_address(ranges::end(rng))}; + } + + static constexpr pair get_iters(ranges::contiguous_range auto&& rng, range_type type) { + const auto n = static_cast>(type); + assert(n > 2); + + // Avoid real out-of-bounds access (UB) + + const auto range_dist = ranges::distance(rng); + const auto first_ptr = to_address(ranges::begin(rng) + range_dist / n); + const auto last_ptr = to_address(ranges::begin(rng) + range_dist * (n - 1) / n); + + return {safe_iter{first_ptr, first_ptr, last_ptr}, safe_iter{last_ptr, first_ptr, last_ptr}}; + } + + friend constexpr pointer unwrap(const safe_iter& iter) noexcept { + return iter.current_ptr; + } + +private: + pointer current_ptr = nullptr; + pointer range_first_ptr = nullptr; + pointer range_last_ptr = nullptr; +}; + +template +void single_test_pass(Func&& func) noexcept { + func(); + if constexpr (Nothrow) { + assert(!safe_iter_nothrow_OOB_sign); + } +} +template +void single_test_fail(Func&& func) noexcept { + if constexpr (Nothrow) { + func(); + assert(exchange(safe_iter_nothrow_OOB_sign, false)); + } else { + try { + func(); + assert(false && "should throw, but returned"); + } catch (const safe_iter_out_of_bounds_err&) { + } + } +} + +#define PASS(expr) single_test_pass([&]() { (void) (expr); }) +#define FAIL(expr) single_test_fail([&]() { (void) (expr); }) + +#define EXPR_WRAP_IS(expr) ([&](auto&& i, auto&& s) { (void) (expr); }) +#define EXPR_WRAP_IN(expr) ([&](auto&& i, auto&& n) { (void) (expr); }) +#define EXPR_WRAP_ISD(expr) ([&](auto&& i, auto&& s, auto&& dst) { (void) (expr); }) +#define EXPR_WRAP_IS_DI_DS(expr) ([&](auto&& i, auto&& s, auto&& dst_i, auto&& dst_s) { (void) (expr); }) + +template +void test() { + using iter = safe_iter; + using const_iter = safe_iter; + static_assert(contiguous_iterator); + static_assert(contiguous_iterator); + + // Allow unsafe sentinels + static_assert(sized_sentinel_for); + static_assert(sized_sentinel_for); + + // Allows trivial container reuse for uninitialized memory algorithms + static_assert(is_trivially_destructible_v); + + vector container_read(RangeSize); + iota(container_read.begin(), container_read.end(), static_cast(0)); + + vector container_read_unique(RangeSize); + iota(container_read_unique.begin(), container_read_unique.end(), static_cast(RangeSize)); + assert(ranges::find_first_of(container_read, container_read_unique) == ranges::end(container_read)); + + auto container_write = container_read; + auto container_write_2 = container_read; + +#define GEN_RANGE_ITER(prefix, suffix, type, range, iter_type) \ + [[maybe_unused]] const auto [prefix##_first_##suffix, prefix##_last_##suffix] = \ + iter_type::get_iters(range, range_type::range_##type); \ + [[maybe_unused]] const auto prefix##_dist_##suffix = \ + ranges::distance(prefix##_first_##suffix, prefix##_last_##suffix); +#define GEN_RANGE_ITERS(prefix, range, iter_type) \ + GEN_RANGE_ITER(prefix, s, small, range, iter_type) \ + GEN_RANGE_ITER(prefix, m, medium, range, iter_type) \ + GEN_RANGE_ITER(prefix, l, large, range, iter_type) + + // Iterators with the same suffix refer to ranges of the same size + GEN_RANGE_ITERS(r, container_read, const_iter) + GEN_RANGE_ITERS(r2, container_read_unique, const_iter) + GEN_RANGE_ITERS(w, container_write, iter) + GEN_RANGE_ITERS(w2, container_write_2, iter) + + // l m s s m l + // [ ( < > ) ] + assert(r_first_l < r_first_m); + assert(r_first_m < r_first_s); + assert(r_first_s < r_last_s); + assert(r_last_s < r_last_m); + assert(r_last_m < r_last_l); + +#undef GEN_RANGE_ITERS +#undef GEN_RANGE_ITER + + static constexpr auto tests_base = [](auto&& func, auto&& first, auto&& last_or_dist) { + PASS(func(first, last_or_dist)); + FAIL(func(first - 1, last_or_dist)); + FAIL(func(first, last_or_dist + 1)); + }; + + // [i, s) + { + static constexpr auto tests_i_s = [](auto&& func, auto&& range_func, auto&& first, auto&& last) { + tests_base(func, first, last); + tests_base(range_func, first, last); + tests_base(range_func, first, to_address(last)); + }; + +#define EXPR_WRAP_IS_SMART(expr) \ + [&](auto&& i, auto&& s) { \ + if constexpr (requires { (void) (expr); }) { \ + (void) (expr); \ + } else { \ + (void) to_address(i); \ + (void) to_address(i + (s - i)); \ + } \ + } +#define TESTR(expr) tests_i_s(EXPR_WRAP_IS_SMART(expr), EXPR_WRAP_IS(ranges::expr), r_first_m, r_last_m) +#define TESTW(expr) tests_i_s(EXPR_WRAP_IS_SMART(expr), EXPR_WRAP_IS(ranges::expr), w_first_m, w_last_m) + + // Tests ranges:: and std:: versions (when they exist) + + // Ensures full range traversal (worst-case) + TESTR(adjacent_find(i, s)); + TESTR(count(i, s, container_read_unique[0])); + TESTR(equal(i, s, unwrap(i), unwrap(s))); + TESTR(equal(unwrap(i), unwrap(s), i, s)); + TESTR(find(i, s, container_read_unique[0])); + TESTR(find_end(i, s, unwrap(i), unwrap(s))); + TESTR(find_end(r_first_l, r_last_l, i, s)); + TESTR(find_first_of(i, s, r2_first_s, r2_last_s)); + TESTR(find_first_of(r2_first_l, r2_last_l, i, s)); + TESTR(includes(i, s, r2_first_s, r2_last_s)); + TESTR(includes(r_first_l, r_last_l, i, s)); + TESTR(is_sorted(i, s)); + TESTR(is_sorted_until(i, s)); + TESTR(lexicographical_compare(i, s, unwrap(i), unwrap(s))); + TESTR(lexicographical_compare(unwrap(i), unwrap(s), i, s)); + TESTR(max(ranges::subrange{i, s})); + TESTR(max_element(i, s)); + TESTR(min(ranges::subrange{i, s})); + TESTR(min_element(i, s)); + TESTR(minmax(ranges::subrange{i, s})); + TESTR(minmax_element(i, s)); + TESTR(mismatch(i, s, unwrap(i), unwrap(s))); + TESTR(mismatch(unwrap(i), unwrap(s), i, s)); + TESTR(search(i, s, unwrap(i), unwrap(s))); + TESTR(search(r_first_l, r_last_l, i, s)); + + TESTW(fill(i, s, container_read[0])); + TESTW(nth_element(i, i + (s - i) / 2, s)); + TESTW(remove(i, s, container_read[0])); + TESTW(replace(i, s, container_read[0], container_read[1])); + TESTW(reverse(i, s)); + TESTW(sort(i, s)); + TESTW(stable_sort(i, s)); + TESTW(uninitialized_fill(i, s, container_read[0])); + TESTW(unique(i, s)); + +#if _HAS_CXX23 + TESTR(contains(i, s, container_read_unique[0])); + TESTR(contains_subrange(i, s, unwrap(i), unwrap(s))); + TESTR(contains_subrange(r_first_l, r_last_l, i, s)); + TESTR(ends_with(i, s, unwrap(i), unwrap(s))); + TESTR(ends_with(unwrap(i), unwrap(s), i, s)); + TESTR(starts_with(i, s, unwrap(i), unwrap(s))); + TESTR(starts_with(unwrap(i), unwrap(s), i, s)); + + TESTW(iota(i, s, container_read[0])); + TESTW(shift_left(i, s, (s - i) / 2)); + TESTW(shift_right(i, s, (s - i) / 2)); +#endif // _HAS_CXX23 + + // [i, s) -> [dst, ...) / [..., dst) + { + static constexpr auto tests_i_s_dst = [](auto&& func, auto&& range_func, auto&& dst_small, auto&& dst_big, + auto&& src_first, auto&& src_last) { + tests_i_s( + EXPR_WRAP_IS(func(i, s, dst_big)), EXPR_WRAP_IS(range_func(i, s, dst_big)), src_first, src_last); + FAIL(func(src_first, src_last, dst_small)); + FAIL(range_func(src_first, src_last, dst_small)); + FAIL(range_func(src_first, to_address(src_last), dst_small)); + }; + static constexpr auto tests_i_s_di_ds = [](auto&& func, auto&& range_func, auto&& dst_small_first, + auto&& dst_small_last, auto&& dst_big_first, auto&& src_first, + auto&& src_last) { + tests_i_s_dst(func, EXPR_WRAP_ISD(range_func(i, s, dst, unreachable_sentinel)), dst_small_first, + dst_big_first, src_first, src_last); + + const auto range_func_with_input_range = EXPR_WRAP_IS(range_func(src_first, src_last, i, s)); + const auto range_func_with_input_range_i_s = + EXPR_WRAP_IS(range_func(src_first, to_address(src_last), i, s)); + + tests_base(range_func_with_input_range, dst_small_first, dst_small_last); + tests_base(range_func_with_input_range, dst_small_first, to_address(dst_small_last)); + tests_base(range_func_with_input_range_i_s, dst_small_first, dst_small_last); + tests_base(range_func_with_input_range_i_s, dst_small_first, to_address(dst_small_last)); + }; + +#define TEST2W(expr) \ + tests_i_s_dst(EXPR_WRAP_ISD(expr), EXPR_WRAP_ISD(ranges::expr), w_first_s, w_first_l, w2_first_m, w2_last_m) +#define TEST2WB(expr) \ + tests_i_s_dst(EXPR_WRAP_ISD(expr), EXPR_WRAP_ISD(ranges::expr), w_last_s, w_last_l, w2_first_m, w2_last_m) +#define TEST3W(expr, range_expr) \ + tests_i_s_di_ds( \ + EXPR_WRAP_ISD(expr), EXPR_WRAP_IS_DI_DS(range_expr), w_first_s, w_last_s, w_first_l, w2_first_m, w2_last_m) + + // These algorithms always traverse the entire input range, so we always use the write container. + + TEST2W(copy(i, s, dst)); + TEST2WB(copy_backward(i, s, dst)); + TEST2W(move(i, s, dst)); + TEST2WB(move_backward(i, s, dst)); + TEST2W(replace_copy(i, s, dst, container_read[0], container_read[1])); + TEST2W(reverse_copy(i, s, dst)); + TEST2W(rotate_copy(i, i + (s - i) / 2, s, dst)); + + TEST3W(swap_ranges(i, s, dst), ranges::swap_ranges(i, s, dst_i, dst_s)); + TEST3W(uninitialized_copy(i, s, dst), ranges::uninitialized_copy(i, s, dst_i, dst_s)); + TEST3W(uninitialized_move(i, s, dst), ranges::uninitialized_move(i, s, dst_i, dst_s)); + +#undef TEST3W +#undef TEST2WB +#undef TEST2W + + tests_base(EXPR_WRAP_IS(adjacent_difference(i, s, w_first_m, minus{})), r_first_m, + r_last_m); // no ranges:: version + FAIL(adjacent_difference(r_first_m, r_last_m, w_first_s, minus{})); + } + +#undef TESTW +#undef TESTR +#undef EXPR_WRAP_IS_SMART + } + + // i + [0, n) + { +#define TESTR(expr) tests_base(EXPR_WRAP_IN(expr), r_first_m, r_dist_m) +#define TESTW(expr) tests_base(EXPR_WRAP_IN(expr), w_first_s, w_dist_s) + + TESTR(copy_n(i, n, w_first_l)); + TESTR(ranges::copy_n(i, n, w_first_l)); + + TESTW(fill_n(i, n, container_read[0])); + TESTW(ranges::fill_n(i, n, container_read[0])); + + TESTR(uninitialized_copy_n(i, n, w_first_l)); + TESTR(ranges::uninitialized_copy_n(i, n, w_first_l, unreachable_sentinel)); + tests_base(EXPR_WRAP_IS(ranges::uninitialized_copy_n(r_first_m, r_dist_m, i, s)), w_first_s, w_last_s); + + TESTW(uninitialized_move_n(i, n, w2_first_l)); + TESTW(ranges::uninitialized_move_n(i, n, w2_first_l, unreachable_sentinel)); + tests_base(EXPR_WRAP_IS(ranges::uninitialized_move_n(w_first_s, w_dist_s, i, s)), w2_first_s, w2_last_s); + + TESTW(uninitialized_fill_n(i, n, container_read[0])); + TESTW(ranges::uninitialized_fill_n(i, n, container_read[0])); + +#undef TESTW +#undef TESTR + } +} + +template +void test_matrix() { + // Ensures vectorization (and iterator-to-pointer conversion) + test(); + test(); + test(); + test(); + + // remove_copy and unique_copy + { + using iter = safe_iter; + using const_iter = safe_iter; + + const array src = {10, 20, 30, 40, 40, 50, 50}; + + const auto src_first = const_iter::begin(src); + const auto src_last = const_iter::end(src); + const auto src_last_ptr = to_address(src_last); + + array dst; + const iter dst_first{ + to_address(ranges::begin(dst)), to_address(ranges::begin(dst)), to_address(ranges::begin(dst) + 5)}; + + PASS(remove_copy(src_first, src_last, dst_first, 50)); + FAIL(remove_copy(src_first, src_last, dst_first, 10)); + PASS(ranges::remove_copy(src_first, src_last_ptr, dst_first, 50)); + FAIL(ranges::remove_copy(src_first, src_last_ptr, dst_first, 10)); + + PASS(unique_copy(src_first, src_last, dst_first)); + FAIL(unique_copy(src_first, src_last, dst_first + 1)); + PASS(ranges::unique_copy(src_first, src_last_ptr, dst_first)); + FAIL(ranges::unique_copy(src_first, src_last_ptr, dst_first + 1)); + } + + // search_n + { + using const_iter = safe_iter; + + const array rng = {10, 10, 20, 30, 30, 30}; + const auto valid_first_ptr = to_address(rng.begin() + 1); + const auto valid_last_ptr = to_address(rng.end() - 1); + + const const_iter rng_first{valid_first_ptr, valid_first_ptr, valid_last_ptr}; + const const_iter rng_last{valid_last_ptr, valid_first_ptr, valid_last_ptr}; + + PASS(search_n(rng_first, rng_last, 2, 30)); + FAIL(search_n(rng_first, rng_last + 1, 3, 30)); + FAIL(search_n(rng_first - 1, rng_last, 2, 10)); + + PASS(ranges::search_n(rng_first, valid_last_ptr, 2, 30)); + FAIL(ranges::search_n(rng_first, valid_last_ptr + 1, 3, 30)); + FAIL(ranges::search_n(rng_first - 1, valid_last_ptr, 2, 10)); + } + +#if _HAS_CXX23 + // format {:s} and {:?s} + { + const vector chars = ranges::to>(R"("Hello, world!")"); + + const auto [chars_first, chars_last] = + safe_iter::get_iters(chars, range_type::range_medium); + const auto last_ptr = to_address(chars_last); + + const ranges::subrange good_range{chars_first, last_ptr}; + const ranges::subrange bad_range{chars_first, last_ptr + 1}; + const ranges::subrange bad_range_2 = [&] { + if constexpr (Nothrow) { + return ranges::subrange{chars_first - 1, last_ptr}; + } else { + return bad_range; + } + }(); + + static_assert(ranges::contiguous_range); + static_assert(ranges::contiguous_range); + static_assert(ranges::contiguous_range); + + PASS(format("{:s}", good_range)); + FAIL(format("{:s}", bad_range)); + FAIL(format("{:s}", bad_range_2)); + + PASS(format("{:?s}", good_range)); + FAIL(format("{:?s}", bad_range)); + FAIL(format("{:?s}", bad_range_2)); + } +#endif // _HAS_CXX23 +} + +int main() { + test_matrix(); + test_matrix(); + + return 0; +}