From 11f9feb39ca216f6750a394d99734f06c7a30c88 Mon Sep 17 00:00:00 2001 From: NylteJ <178683943+NylteJ@users.noreply.github.com> Date: Mon, 18 Aug 2025 01:28:46 +0800 Subject: [PATCH 01/35] Implement P3349R1 --- stl/inc/__msvc_ranges_tuple_formatter.hpp | 6 +- stl/inc/algorithm | 73 ++- stl/inc/format | 6 +- stl/inc/functional | 1 + stl/inc/memory | 14 +- stl/inc/numeric | 20 +- stl/inc/xmemory | 18 +- stl/inc/xutility | 103 +++- stl/inc/yvals_core.h | 1 + tests/std/test.lst | 1 + .../env.lst | 4 + .../test.cpp | 461 ++++++++++++++++++ 12 files changed, 633 insertions(+), 75 deletions(-) create mode 100644 tests/std/tests/P3349R1_contiguous_iterators_to_pointers/env.lst create mode 100644 tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp diff --git a/stl/inc/__msvc_ranges_tuple_formatter.hpp b/stl/inc/__msvc_ranges_tuple_formatter.hpp index 176bf1eb762..ee008886e04 100644 --- a/stl/inc/__msvc_ranges_tuple_formatter.hpp +++ b/stl/inc/__msvc_ranges_tuple_formatter.hpp @@ -1081,13 +1081,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 10203201835..6554a69f8de 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -376,7 +376,8 @@ constexpr bool _Vector_alg_in_unique_is_safe = _Vector_alg_in_search_is_safe<_It // Can we use this output iterator for remove_copy or unique_copy? template constexpr bool _Output_iterator_for_vector_alg_is_safe() { - if constexpr (_Iterator_is_contiguous<_Out>) { + if constexpr (is_pointer_v<_Out>) { // P3349R1: Non-pointer contiguous iterators prevent vectorization (unknown + // output size) return is_same_v<_Iter_value_t<_Out>, remove_const_t<_Iter_value_t<_In>>>; } else { return false; @@ -790,7 +791,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)); } @@ -888,8 +889,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); @@ -952,6 +956,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)); @@ -2317,6 +2324,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)); @@ -2626,7 +2634,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); @@ -3318,7 +3326,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>) { @@ -3472,6 +3480,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)); @@ -3595,9 +3604,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)); @@ -3899,13 +3910,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); @@ -3968,20 +3977,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"); } @@ -4340,7 +4354,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); } @@ -4955,7 +4969,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>) { @@ -5284,7 +5298,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>) { @@ -5504,7 +5518,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); @@ -5638,9 +5652,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; } @@ -5724,9 +5741,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; } } @@ -8364,7 +8383,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>) { @@ -10994,7 +11013,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}; @@ -11162,14 +11181,14 @@ 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)}; } } else 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)}; } @@ -11743,14 +11762,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 0e6af0261bb..23e69c94b13 100644 --- a/stl/inc/format +++ b/stl/inc/format @@ -3288,9 +3288,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."); } @@ -3301,7 +3302,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 6496b8d3dff..5561d7d3dce 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -2497,6 +2497,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 28907ee241b..ea2e332f56d 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 338abe4ca3c..77a28a18c55 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 e4cfa3008b5..c6184499427 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -1723,6 +1723,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)); @@ -1746,6 +1747,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); @@ -1760,13 +1762,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)}; } @@ -1816,12 +1818,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) { @@ -1909,7 +1913,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 c262b62aaaa..7401cd8c0b5 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -4724,6 +4724,35 @@ _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 @@ -4857,6 +4886,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); @@ -4874,15 +4904,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); @@ -5090,9 +5124,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)}; } } @@ -5195,12 +5230,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 { @@ -5371,6 +5407,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); @@ -5378,6 +5416,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>)); } @@ -5616,6 +5655,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)); @@ -5636,6 +5677,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; @@ -5773,6 +5816,10 @@ 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()) { + _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>( @@ -5959,8 +6006,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 _USE_STD_VECTOR_ALGORITHMS const auto _First1_ptr = _STD _To_address(_UFirst1); @@ -6084,8 +6131,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 _USE_STD_VECTOR_ALGORITHMS const auto _First1_ptr = _STD to_address(_UFirst1); @@ -6255,7 +6302,7 @@ _NODISCARD _CONSTEXPR20 _InIt _Find_unchecked(_InIt _First, const _InIt _Last, c #else // ^^^ _USE_STD_VECTOR_ALGORITHMS / !_USE_STD_VECTOR_ALGORITHMS 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; @@ -6341,21 +6388,21 @@ namespace ranges { #if _USE_STD_VECTOR_ALGORITHMS 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 // ^^^ _USE_STD_VECTOR_ALGORITHMS ^^^ { 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), - static_cast(_Val), static_cast(_Last - _First)))); + _Result = reinterpret_cast<_Ptr_t>(const_cast( + _CSTD wmemchr(reinterpret_cast(_First_ptr), static_cast(_Val), + static_cast(_STD _Contiguous_iter_distance(_First, _Last))))); } 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; } @@ -6933,7 +6980,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>) { @@ -7036,6 +7083,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; @@ -7043,7 +7092,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 = @@ -7231,7 +7280,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; @@ -7359,7 +7408,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)); } } @@ -7455,7 +7504,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; @@ -7577,7 +7626,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)); } } @@ -7671,9 +7720,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 // _USE_STD_VECTOR_ALGORITHMS diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 56f97244764..560ad178657 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -83,6 +83,7 @@ // P3223R2 Making istream::ignore() Less Surprising // P3323R1 Forbid atomic, Specify atomic_ref // (for atomic) +// P3349R1 Converting Contiguous Iterators To Pointers // _HAS_CXX17 directly controls: // P0005R4 not_fn() diff --git a/tests/std/test.lst b/tests/std/test.lst index 4a0b4c0864b..5c513b2c317 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -711,6 +711,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\VSO_0000000_allocator_propagation tests\VSO_0000000_any_calling_conventions tests\VSO_0000000_c_math_functions 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 00000000000..642f530ffad --- /dev/null +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_latest_matrix.lst 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 00000000000..25061985654 --- /dev/null +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp @@ -0,0 +1,461 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +struct safe_iter_out_of_bounds_err {}; + +enum range_type { range_large = 3, range_medium = 4, range_small = 5 }; + +bool safe_iter_nothrow_OOB_sign = false; + +// 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 false, throws immediately on bounds violation. +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 && 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 { + 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); + } + + 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; + } + friend constexpr bool operator==(pointer left, const safe_iter& right) noexcept { + return left == right.current_ptr; + } + + 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 * 2 / n); + + return {safe_iter{first_ptr, first_ptr, last_ptr}, safe_iter{last_ptr, first_ptr, last_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 range_size, bool nothrow> +void test() { + using iter = safe_iter, nothrow>; + using const_iter = safe_iter, nothrow>; + 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>); + + const auto container_read = + views::iota(static_cast>(0), static_cast>(range_size)) + | ranges::to(); + assert(ranges::size(container_read) == range_size); + + const auto container_read_unique = + views::iota(static_cast>(range_size), static_cast>(range_size) * 2) + | ranges::to(); + assert(ranges::size(container_read_unique) == range_size); + assert(ranges::find_first_of(container_read, container_read_unique) == ranges::end(container_read)); + + ContainerT container_write = container_read; + ContainerT container_write_2 = container_read; + assert(ranges::size(container_write) == range_size); + assert(ranges::size(container_write_2) == range_size); + +#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); \ + [[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) + + assert(r_first_s < r_first_m && r_first_m < r_first_l && r_last_s < r_last_m && 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 exist) + + // Ensures full range traversal (worst-case) + TESTR(adjacent_find(i, s)); + TESTR(contains(i, s, container_read_unique[0])); + TESTR(contains_subrange(i, s, r2_first_s, r2_last_s)); + TESTR(contains_subrange(r2_first_l, r2_last_l, i, s)); + TESTR(count(i, s, container_read_unique[0])); + TESTR(ends_with(i, s, i, s)); + TESTR(equal(i, s, i, s)); + TESTR(find(i, s, container_read_unique[0])); + TESTR(find_end(i, s, r2_first_s, r2_last_s)); + TESTR(find_end(r2_first_l, r2_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(is_sorted(i, s)); + TESTR(is_sorted_until(i, s)); + TESTR(lexicographical_compare(i, 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, i, s)); + TESTR(search(i, s, r2_first_s, r2_last_s)); + TESTR(search(r2_first_l, r2_last_l, i, s)); + TESTR(search_n(i, s, (s - i) / 2, container_read_unique[0])); + TESTR(starts_with(i, s, i, s)); + + TESTW(fill(i, s, container_read[0])); + TESTW(iota(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(shift_left(i, s, (s - i) / 2)); + TESTW(shift_right(i, s, (s - i) / 2)); + TESTW(sort(i, s)); + TESTW(stable_sort(i, s)); + TESTW(uninitialized_fill(i, s, container_read[0])); + TESTW(unique(i, s)); + + // [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, 63, nothrow>(); + test, 256, nothrow>(); + test, 256, nothrow>(); + test, 256, nothrow>(); + + // remove_copy & unique_copy + { + using iter = safe_iter; + using const_iter = safe_iter; + + const auto src = {1, 2, 3, 4, 4, 5, 5}; + + 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, 5)); + FAIL(remove_copy(src_first, src_last, dst_first, 1)); + PASS(ranges::remove_copy(src_first, src_last_ptr, dst_first, 5)); + FAIL(ranges::remove_copy(src_first, src_last_ptr, dst_first, 1)); + + 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)); + } + + // format {:s} & {:?s} + { + const vector chars = ranges::to>("\"Hello, world!\""); + + const auto [chars_first, chars_last] = safe_iter::get_iters(chars, range_medium); + + const ranges::subrange good_range{chars_first, to_address(chars_last)}; + const ranges::subrange bad_range{chars_first, to_address(chars_last) + 1}; + const ranges::subrange bad_range_2 = [&] { + if constexpr (nothrow) { + return ranges::subrange{chars_first - 1, to_address(chars_last)}; + } 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)); + } +} + +int main() { + test_matrix(); + test_matrix(); + + return 0; +} From cb8802bb62cd5c1c409fa2a032e92d1a9b280a4b Mon Sep 17 00:00:00 2001 From: NylteJ <178683943+NylteJ@users.noreply.github.com> Date: Mon, 18 Aug 2025 10:50:44 +0800 Subject: [PATCH 02/35] Moved comment in `yvals_core.h` --- stl/inc/yvals_core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 560ad178657..41877081408 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -83,7 +83,6 @@ // P3223R2 Making istream::ignore() Less Surprising // P3323R1 Forbid atomic, Specify atomic_ref // (for atomic) -// P3349R1 Converting Contiguous Iterators To Pointers // _HAS_CXX17 directly controls: // P0005R4 not_fn() @@ -322,6 +321,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 From c3e4dc06118703a36d61f2f5ea74cc88dc97fc98 Mon Sep 17 00:00:00 2001 From: NylteJ <178683943+NylteJ@users.noreply.github.com> Date: Mon, 18 Aug 2025 10:50:56 +0800 Subject: [PATCH 03/35] Change test standard to C++20 --- .../env.lst | 2 +- .../test.cpp | 64 +++++++++---------- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/env.lst b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/env.lst index 642f530ffad..351a8293d9d 100644 --- a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/env.lst +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/env.lst @@ -1,4 +1,4 @@ # Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -RUNALL_INCLUDE ..\usual_latest_matrix.lst +RUNALL_INCLUDE ..\usual_20_matrix.lst diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp index 25061985654..4fdca69e693 100644 --- a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp @@ -172,18 +172,18 @@ void single_test_fail(Func&& func) noexcept { } } -#define PASS(expr) single_test_pass([&] { (void) (expr); }) -#define FAIL(expr) single_test_fail([&] { (void) (expr); }) +#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 range_size, bool nothrow> +template void test() { - using iter = safe_iter, nothrow>; - using const_iter = safe_iter, nothrow>; + using iter = safe_iter; + using const_iter = safe_iter; static_assert(contiguous_iterator); static_assert(contiguous_iterator); @@ -192,23 +192,17 @@ void test() { static_assert(sized_sentinel_for); // Allows trivial container reuse for uninitialized memory algorithms - static_assert(is_trivially_destructible_v>); + static_assert(is_trivially_destructible_v); - const auto container_read = - views::iota(static_cast>(0), static_cast>(range_size)) - | ranges::to(); - assert(ranges::size(container_read) == range_size); + vector container_read(range_size); + iota(container_read.begin(), container_read.end(), static_cast(0)); - const auto container_read_unique = - views::iota(static_cast>(range_size), static_cast>(range_size) * 2) - | ranges::to(); - assert(ranges::size(container_read_unique) == range_size); + vector container_read_unique(range_size); + iota(container_read_unique.begin(), container_read_unique.end(), static_cast(range_size)); assert(ranges::find_first_of(container_read, container_read_unique) == ranges::end(container_read)); - ContainerT container_write = container_read; - ContainerT container_write_2 = container_read; - assert(ranges::size(container_write) == range_size); - assert(ranges::size(container_write_2) == range_size); + 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] = \ @@ -261,11 +255,7 @@ void test() { // Ensures full range traversal (worst-case) TESTR(adjacent_find(i, s)); - TESTR(contains(i, s, container_read_unique[0])); - TESTR(contains_subrange(i, s, r2_first_s, r2_last_s)); - TESTR(contains_subrange(r2_first_l, r2_last_l, i, s)); TESTR(count(i, s, container_read_unique[0])); - TESTR(ends_with(i, s, i, s)); TESTR(equal(i, s, i, s)); TESTR(find(i, s, container_read_unique[0])); TESTR(find_end(i, s, r2_first_s, r2_last_s)); @@ -285,21 +275,29 @@ void test() { TESTR(search(i, s, r2_first_s, r2_last_s)); TESTR(search(r2_first_l, r2_last_l, i, s)); TESTR(search_n(i, s, (s - i) / 2, container_read_unique[0])); - TESTR(starts_with(i, s, i, s)); TESTW(fill(i, s, container_read[0])); - TESTW(iota(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(shift_left(i, s, (s - i) / 2)); - TESTW(shift_right(i, s, (s - i) / 2)); 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, r2_first_s, r2_last_s)); + TESTR(contains_subrange(r2_first_l, r2_last_l, i, s)); + TESTR(ends_with(i, s, i, s)); + TESTR(starts_with(i, 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, @@ -352,9 +350,9 @@ void test() { #undef TEST2WB #undef TEST2W - tests_base(EXPR_WRAP_IS(adjacent_difference(i, s, w_first_m, minus>{})), r_first_m, + 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>{})); + FAIL(adjacent_difference(r_first_m, r_last_m, w_first_s, minus{})); } #undef TESTW @@ -392,10 +390,10 @@ void test() { template void test_matrix() { // Ensures vectorization (and iterator-to-pointer conversion) - test, 63, nothrow>(); - test, 256, nothrow>(); - test, 256, nothrow>(); - test, 256, nothrow>(); + test(); + test(); + test(); + test(); // remove_copy & unique_copy { @@ -423,6 +421,7 @@ void test_matrix() { FAIL(ranges::unique_copy(src_first, src_last_ptr, dst_first + 1)); } +#if _HAS_CXX23 // format {:s} & {:?s} { const vector chars = ranges::to>("\"Hello, world!\""); @@ -451,6 +450,7 @@ void test_matrix() { FAIL(format("{:?s}", bad_range)); FAIL(format("{:?s}", bad_range_2)); } +#endif // _HAS_CXX23 } int main() { From 45881ace8338af60bba2ed4ebeb8e81d1f043611 Mon Sep 17 00:00:00 2001 From: NylteJ <178683943+NylteJ@users.noreply.github.com> Date: Fri, 24 Oct 2025 11:49:04 +0800 Subject: [PATCH 04/35] Resolve stealth conflict with #5590 --- stl/inc/algorithm | 4 ++-- .../tests/P3349R1_contiguous_iterators_to_pointers/test.cpp | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index baa8448d7aa..1cafb15c9fc 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -10434,8 +10434,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); } } diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp index 4fdca69e693..747d08c6baa 100644 --- a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp @@ -262,6 +262,8 @@ void test() { TESTR(find_end(r2_first_l, r2_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(r2_first_l, r2_last_l, i, s)); TESTR(is_sorted(i, s)); TESTR(is_sorted_until(i, s)); TESTR(lexicographical_compare(i, s, i, s)); From 96d14985932ccf869a84c8c4d5f018c264bfb952 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 3 Nov 2025 07:42:19 -0800 Subject: [PATCH 05/35] Add newlines. --- stl/inc/xutility | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stl/inc/xutility b/stl/inc/xutility index 8578ad71b15..1fce794ec1e 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -4811,11 +4811,13 @@ _NODISCARD constexpr _Iter_diff_t<_Iter> _Contiguous_iter_distance(const _Iter& 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)) { @@ -4824,6 +4826,7 @@ constexpr void _Contiguous_iter_verify(const _Iter& _Val, const _Iter_diff_t<_It (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))) { From 3555bbf6ba66e248cf4e87619f86f71c5e50e13b Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 3 Nov 2025 08:19:34 -0800 Subject: [PATCH 06/35] Simplify a wmemchr() call into step-by-step casts. --- stl/inc/xutility | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index 1fce794ec1e..7a6dd5a3ff4 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -6494,9 +6494,11 @@ namespace ranges { { 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(_STD _Contiguous_iter_distance(_First, _Last))))); + 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; From d93eb1d2aad7ce93055ddce1f4909fa2bc714007 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 3 Nov 2025 09:36:53 -0800 Subject: [PATCH 07/35] nothrow => Nothrow --- .../test.cpp | 68 +++++++++---------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp index 747d08c6baa..11c75d475ea 100644 --- a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp @@ -21,9 +21,9 @@ enum range_type { range_large = 3, range_medium = 4, range_small = 5 }; bool safe_iter_nothrow_OOB_sign = false; -// When `nothrow` is true, disables bounds checking during iterator increment/decrement (to verify `to_address` calls), +// 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 false, throws immediately on bounds violation. -template +template class safe_iter { public: using iterator_concept = contiguous_iterator_tag; @@ -39,9 +39,9 @@ class safe_iter { } constexpr safe_iter() noexcept = default; - constexpr reference operator*() const noexcept(nothrow) { + constexpr reference operator*() const noexcept(Nothrow) { if (current_ptr < range_first_ptr || current_ptr >= range_last_ptr) { - if constexpr (nothrow) { + if constexpr (Nothrow) { safe_iter_nothrow_OOB_sign = true; } else { throw safe_iter_out_of_bounds_err{}; @@ -50,56 +50,56 @@ class safe_iter { return *current_ptr; } constexpr pointer operator->() const noexcept { - if constexpr (nothrow) { + 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) { + constexpr reference operator[](difference_type n) const noexcept(Nothrow) { return *(*this + n); } - constexpr safe_iter& operator+=(difference_type offset) noexcept(nothrow) { + constexpr safe_iter& operator+=(difference_type offset) noexcept(Nothrow) { current_ptr += offset; - if constexpr (!nothrow) { + 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) { + 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) { + 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) { + constexpr safe_iter& operator-=(difference_type offset) noexcept(Nothrow) { return *this += -offset; } - constexpr safe_iter operator-(difference_type offset) const noexcept(nothrow) { + 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) { + constexpr safe_iter& operator++() noexcept(Nothrow) { return *this += 1; } - constexpr safe_iter& operator--() noexcept(nothrow) { + constexpr safe_iter& operator--() noexcept(Nothrow) { return *this -= 1; } - constexpr safe_iter operator++(int) noexcept(nothrow) { + constexpr safe_iter operator++(int) noexcept(Nothrow) { auto temp = *this; ++*this; return temp; } - constexpr safe_iter operator--(int) noexcept(nothrow) { + constexpr safe_iter operator--(int) noexcept(Nothrow) { auto temp = *this; --*this; return temp; @@ -151,16 +151,16 @@ class safe_iter { pointer range_last_ptr = nullptr; }; -template +template void single_test_pass(Func&& func) noexcept { func(); - if constexpr (nothrow) { + if constexpr (Nothrow) { assert(!safe_iter_nothrow_OOB_sign); } } -template +template void single_test_fail(Func&& func) noexcept { - if constexpr (nothrow) { + if constexpr (Nothrow) { func(); assert(exchange(safe_iter_nothrow_OOB_sign, false)); } else { @@ -172,18 +172,18 @@ void single_test_fail(Func&& func) noexcept { } } -#define PASS(expr) single_test_pass([&]() { (void) (expr); }) -#define FAIL(expr) single_test_fail([&]() { (void) (expr); }) +#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 +template void test() { - using iter = safe_iter; - using const_iter = safe_iter; + using iter = safe_iter; + using const_iter = safe_iter; static_assert(contiguous_iterator); static_assert(contiguous_iterator); @@ -389,18 +389,18 @@ void test() { } } -template +template void test_matrix() { // Ensures vectorization (and iterator-to-pointer conversion) - test(); - test(); - test(); - test(); + test(); + test(); + test(); + test(); // remove_copy & unique_copy { - using iter = safe_iter; - using const_iter = safe_iter; + using iter = safe_iter; + using const_iter = safe_iter; const auto src = {1, 2, 3, 4, 4, 5, 5}; @@ -428,12 +428,12 @@ void test_matrix() { { const vector chars = ranges::to>("\"Hello, world!\""); - const auto [chars_first, chars_last] = safe_iter::get_iters(chars, range_medium); + const auto [chars_first, chars_last] = safe_iter::get_iters(chars, range_medium); const ranges::subrange good_range{chars_first, to_address(chars_last)}; const ranges::subrange bad_range{chars_first, to_address(chars_last) + 1}; const ranges::subrange bad_range_2 = [&] { - if constexpr (nothrow) { + if constexpr (Nothrow) { return ranges::subrange{chars_first - 1, to_address(chars_last)}; } else { return bad_range; From 1c1126f9aad4f3773695e36d50ee532ad3648230 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 3 Nov 2025 09:44:19 -0800 Subject: [PATCH 08/35] `enum range_type` => `enum class range_type` --- .../P3349R1_contiguous_iterators_to_pointers/test.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp index 11c75d475ea..c9b95aba803 100644 --- a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp @@ -17,7 +17,7 @@ using namespace std; struct safe_iter_out_of_bounds_err {}; -enum range_type { range_large = 3, range_medium = 4, range_small = 5 }; +enum class range_type { range_large = 3, range_medium = 4, range_small = 5 }; bool safe_iter_nothrow_OOB_sign = false; @@ -206,7 +206,7 @@ void test() { #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); \ + 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) \ @@ -428,7 +428,8 @@ void test_matrix() { { const vector chars = ranges::to>("\"Hello, world!\""); - const auto [chars_first, chars_last] = safe_iter::get_iters(chars, range_medium); + const auto [chars_first, chars_last] = + safe_iter::get_iters(chars, range_type::range_medium); const ranges::subrange good_range{chars_first, to_address(chars_last)}; const ranges::subrange bad_range{chars_first, to_address(chars_last) + 1}; From cad143ba0a1b615ac2ef134b50086bd443483ec5 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 3 Nov 2025 09:52:55 -0800 Subject: [PATCH 09/35] Include more headers. --- .../tests/P3349R1_contiguous_iterators_to_pointers/test.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp index c9b95aba803..e9da8612a46 100644 --- a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include #include #include @@ -11,6 +13,8 @@ #include #include #include +#include +#include #include using namespace std; From b38e1202e120bf6ca253ea21820ddce2994c345b Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 3 Nov 2025 09:55:20 -0800 Subject: [PATCH 10/35] Assert one thing at a time. --- .../P3349R1_contiguous_iterators_to_pointers/test.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp index e9da8612a46..ecc442cfb20 100644 --- a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp @@ -39,7 +39,8 @@ class safe_iter { 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 && current_ptr <= range_last_ptr); + assert(current_ptr >= range_first_ptr); + assert(current_ptr <= range_last_ptr); } constexpr safe_iter() noexcept = default; @@ -224,7 +225,10 @@ void test() { GEN_RANGE_ITERS(w, container_write, iter) GEN_RANGE_ITERS(w2, container_write_2, iter) - assert(r_first_s < r_first_m && r_first_m < r_first_l && r_last_s < r_last_m && r_last_m < r_last_l); + assert(r_first_s < r_first_m); + assert(r_first_m < r_first_l); + assert(r_last_s < r_last_m); + assert(r_last_m < r_last_l); #undef GEN_RANGE_ITERS #undef GEN_RANGE_ITER From cfa1d9c96c2b042187cc625ded431595e916df69 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 3 Nov 2025 09:59:20 -0800 Subject: [PATCH 11/35] Comment cleanups. --- .../tests/P3349R1_contiguous_iterators_to_pointers/test.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp index ecc442cfb20..d8b42b46e17 100644 --- a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp @@ -259,7 +259,7 @@ void test() { #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 exist) + // Tests ranges:: and std:: versions (when they exist) // Ensures full range traversal (worst-case) TESTR(adjacent_find(i, s)); @@ -405,7 +405,7 @@ void test_matrix() { test(); test(); - // remove_copy & unique_copy + // remove_copy and unique_copy { using iter = safe_iter; using const_iter = safe_iter; @@ -432,7 +432,7 @@ void test_matrix() { } #if _HAS_CXX23 - // format {:s} & {:?s} + // format {:s} and {:?s} { const vector chars = ranges::to>("\"Hello, world!\""); From b5c86256f08e9202d14b94dfe35e9650ecdaaa77 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 3 Nov 2025 10:00:09 -0800 Subject: [PATCH 12/35] Raw string literals are cool string literals. --- .../std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp index d8b42b46e17..3a83d9adc9e 100644 --- a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp @@ -434,7 +434,7 @@ void test_matrix() { #if _HAS_CXX23 // format {:s} and {:?s} { - const vector chars = ranges::to>("\"Hello, world!\""); + const vector chars = ranges::to>(R"("Hello, world!")"); const auto [chars_first, chars_last] = safe_iter::get_iters(chars, range_type::range_medium); From 1e06f30eaa51af798f070f17668f6b2be3844699 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 3 Nov 2025 10:11:12 -0800 Subject: [PATCH 13/35] Drop default `false` for `safe_iter`. --- .../std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp index 3a83d9adc9e..96ea2084017 100644 --- a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp @@ -27,7 +27,7 @@ bool safe_iter_nothrow_OOB_sign = false; // 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 false, throws immediately on bounds violation. -template +template class safe_iter { public: using iterator_concept = contiguous_iterator_tag; From d69296ab1b72cc22cfca8e840699551f12636922 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 3 Nov 2025 11:46:50 -0800 Subject: [PATCH 14/35] Drop unnecessary `operator==` overload. --- .../tests/P3349R1_contiguous_iterators_to_pointers/test.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp index 96ea2084017..e70fdf3399a 100644 --- a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp @@ -126,9 +126,6 @@ class safe_iter { constexpr bool operator==(pointer right) const noexcept { return current_ptr == right; } - friend constexpr bool operator==(pointer left, const safe_iter& right) noexcept { - return left == right.current_ptr; - } 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))}; From 481607435096b1b04d28ef616b2e2cdf85f6b83c Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 3 Nov 2025 13:49:09 -0800 Subject: [PATCH 15/35] Comment `operator->` being unconditionally `noexcept`. --- .../std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp index e70fdf3399a..138ce968624 100644 --- a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp @@ -54,7 +54,7 @@ class safe_iter { } return *current_ptr; } - constexpr pointer operator->() const noexcept { + 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; From 67f1aee78d81058969adced1b9d77ebd9a30e0dd Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 3 Nov 2025 13:49:36 -0800 Subject: [PATCH 16/35] Teach `operator+=` how to set the `Nothrow` bool. --- .../tests/P3349R1_contiguous_iterators_to_pointers/test.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp index 138ce968624..af5874772be 100644 --- a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp @@ -68,8 +68,10 @@ class safe_iter { 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) { + 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{}; } } From 2cf5a3982cd16489e4c376e0d8fb4d0fb7b1d756 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 3 Nov 2025 13:50:27 -0800 Subject: [PATCH 17/35] Drop `bad_range_2`, extract `last_ptr` to simplify. --- .../test.cpp | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp index af5874772be..adae02ddc97 100644 --- a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp @@ -437,28 +437,19 @@ void test_matrix() { 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, to_address(chars_last)}; - const ranges::subrange bad_range{chars_first, to_address(chars_last) + 1}; - const ranges::subrange bad_range_2 = [&] { - if constexpr (Nothrow) { - return ranges::subrange{chars_first - 1, to_address(chars_last)}; - } else { - return bad_range; - } - }(); + const ranges::subrange good_range{chars_first, last_ptr}; + const ranges::subrange bad_range{chars_first, last_ptr + 1}; 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 } From d11fa885caeca00cf3fffd77e9d2d903a0c613c7 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 3 Nov 2025 14:02:07 -0800 Subject: [PATCH 18/35] `range_size` => `RangeSize` --- .../P3349R1_contiguous_iterators_to_pointers/test.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp index adae02ddc97..383daad97f7 100644 --- a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp @@ -184,7 +184,7 @@ void single_test_fail(Func&& func) noexcept { #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 +template void test() { using iter = safe_iter; using const_iter = safe_iter; @@ -198,11 +198,11 @@ void test() { // Allows trivial container reuse for uninitialized memory algorithms static_assert(is_trivially_destructible_v); - vector container_read(range_size); + vector container_read(RangeSize); iota(container_read.begin(), container_read.end(), static_cast(0)); - vector container_read_unique(range_size); - iota(container_read_unique.begin(), container_read_unique.end(), static_cast(range_size)); + 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; From 7f59667eaf848658111381dd6600bd46eb54d23b Mon Sep 17 00:00:00 2001 From: NylteJ <178683943+NylteJ@users.noreply.github.com> Date: Tue, 4 Nov 2025 19:14:09 +0800 Subject: [PATCH 19/35] Fix tests --- .../test.cpp | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp index 383daad97f7..3bf6f8fb582 100644 --- a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp @@ -21,10 +21,15 @@ using namespace std; struct safe_iter_out_of_bounds_err {}; -enum class range_type { range_large = 3, range_medium = 4, range_small = 5 }; +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) { + return ptr; +} + // 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 false, throws immediately on bounds violation. template @@ -144,11 +149,15 @@ class safe_iter { 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 * 2 / 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 T* unwrap(const safe_iter& iter) { + return iter.current_ptr; + } + private: pointer current_ptr = nullptr; pointer range_first_ptr = nullptr; @@ -224,8 +233,10 @@ void test() { GEN_RANGE_ITERS(w, container_write, iter) GEN_RANGE_ITERS(w2, container_write_2, iter) - assert(r_first_s < r_first_m); - assert(r_first_m < r_first_l); + // l m s s m l + // [ ( < > ) ] + assert(r_first_l < r_first_m); + assert(r_first_m < r_first_s); assert(r_last_s < r_last_m); assert(r_last_m < r_last_l); @@ -265,12 +276,12 @@ void test() { TESTR(count(i, s, container_read_unique[0])); TESTR(equal(i, s, i, s)); TESTR(find(i, s, container_read_unique[0])); - TESTR(find_end(i, s, r2_first_s, r2_last_s)); - TESTR(find_end(r2_first_l, r2_last_l, i, s)); + 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(r2_first_l, r2_last_l, i, 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, i, s)); @@ -281,9 +292,9 @@ void test() { TESTR(minmax(ranges::subrange{i, s})); TESTR(minmax_element(i, s)); TESTR(mismatch(i, s, i, s)); - TESTR(search(i, s, r2_first_s, r2_last_s)); - TESTR(search(r2_first_l, r2_last_l, i, s)); - TESTR(search_n(i, s, (s - i) / 2, container_read_unique[0])); + TESTR(search(i, s, unwrap(i), unwrap(s))); + TESTR(search(r_first_l, r_last_l, i, s)); + TESTR(search_n(i, s, 1, *(unwrap(s) - 1))); TESTW(fill(i, s, container_read[0])); TESTW(nth_element(i, i + (s - i) / 2, s)); @@ -297,8 +308,8 @@ void test() { #if _HAS_CXX23 TESTR(contains(i, s, container_read_unique[0])); - TESTR(contains_subrange(i, s, r2_first_s, r2_last_s)); - TESTR(contains_subrange(r2_first_l, r2_last_l, i, s)); + 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, i, s)); TESTR(starts_with(i, s, i, s)); From 20a8377fd9df6b49a5ae773b949cfecf5710446a Mon Sep 17 00:00:00 2001 From: NylteJ <178683943+NylteJ@users.noreply.github.com> Date: Tue, 4 Nov 2025 19:08:56 +0800 Subject: [PATCH 20/35] Improve test coverage --- .../test.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp index 3bf6f8fb582..d3a79fd3b86 100644 --- a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp @@ -274,7 +274,8 @@ void test() { // Ensures full range traversal (worst-case) TESTR(adjacent_find(i, s)); TESTR(count(i, s, container_read_unique[0])); - TESTR(equal(i, s, i, s)); + 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)); @@ -284,14 +285,16 @@ void test() { 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, 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, 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)); TESTR(search_n(i, s, 1, *(unwrap(s) - 1))); @@ -310,8 +313,10 @@ void test() { 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, i, s)); - TESTR(starts_with(i, s, 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)); From 06981ca06523ff7a23d4ed05f1d8803eeaf23929 Mon Sep 17 00:00:00 2001 From: NylteJ <178683943+NylteJ@users.noreply.github.com> Date: Tue, 4 Nov 2025 18:26:32 +0800 Subject: [PATCH 21/35] Add cross-tests with non-vectorization --- .../tests/P3349R1_contiguous_iterators_to_pointers/env.lst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/env.lst b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/env.lst index 351a8293d9d..cf749a657f1 100644 --- a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/env.lst +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/env.lst @@ -2,3 +2,9 @@ # 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" \ No newline at end of file From 0308404164dd8140c443965a01c7c73a173b9e4d Mon Sep 17 00:00:00 2001 From: NylteJ <178683943+NylteJ@users.noreply.github.com> Date: Tue, 4 Nov 2025 20:34:26 +0800 Subject: [PATCH 22/35] Avoid `search_n` forwarding to `find` `search_n` is directly forwarded to `find` when n = 1, so the previous test was meaningless. --- .../test.cpp | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp index d3a79fd3b86..a737932221d 100644 --- a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp @@ -297,7 +297,6 @@ void test() { 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)); - TESTR(search_n(i, s, 1, *(unwrap(s) - 1))); TESTW(fill(i, s, container_read[0])); TESTW(nth_element(i, i + (s - i) / 2, s)); @@ -446,6 +445,27 @@ void test_matrix() { FAIL(ranges::unique_copy(src_first, src_last_ptr, dst_first + 1)); } + // search_n + { + using const_iter = safe_iter; + + const auto rng = {1, 1, 2, 3, 3, 3}; + const auto valid_first_ptr = rng.begin() + 1; + const auto valid_last_ptr = 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}; + const auto rng_last_ptr = to_address(rng_last); + + PASS(search_n(rng_first, rng_last, 2, 3)); + FAIL(search_n(rng_first, rng_last + 1, 3, 3)); + FAIL(search_n(rng_first - 1, rng_last, 2, 1)); + + PASS(ranges::search_n(rng_first, rng_last_ptr, 2, 3)); + FAIL(ranges::search_n(rng_first, rng_last_ptr + 1, 3, 3)); + FAIL(ranges::search_n(rng_first - 1, rng_last_ptr, 2, 1)); + } + #if _HAS_CXX23 // format {:s} and {:?s} { From b639c7f40aa55f4fc64e8592e6b7f8685023637f Mon Sep 17 00:00:00 2001 From: NylteJ <178683943+NylteJ@users.noreply.github.com> Date: Tue, 4 Nov 2025 18:28:49 +0800 Subject: [PATCH 23/35] Revert "Teach `operator+=` how to set the `Nothrow` bool." This reverts commit 67f1aee78d81058969adced1b9d77ebd9a30e0dd. --- .../tests/P3349R1_contiguous_iterators_to_pointers/test.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp index a737932221d..f56cc8693c8 100644 --- a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp @@ -73,10 +73,8 @@ class safe_iter { constexpr safe_iter& operator+=(difference_type offset) noexcept(Nothrow) { current_ptr += offset; - if (current_ptr < range_first_ptr || current_ptr > range_last_ptr) { - if constexpr (Nothrow) { - safe_iter_nothrow_OOB_sign = true; - } else { + if constexpr (!Nothrow) { + if (current_ptr < range_first_ptr || current_ptr > range_last_ptr) { throw safe_iter_out_of_bounds_err{}; } } From 914014b3960fb932ee720059a48a3d3cad9bf13c Mon Sep 17 00:00:00 2001 From: NylteJ <178683943+NylteJ@users.noreply.github.com> Date: Tue, 4 Nov 2025 18:29:21 +0800 Subject: [PATCH 24/35] Revert "Drop `bad_range_2`, extract `last_ptr` to simplify." This reverts commit 2cf5a3982cd16489e4c376e0d8fb4d0fb7b1d756. --- .../test.cpp | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp index f56cc8693c8..8513c4a1fe1 100644 --- a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp @@ -471,19 +471,28 @@ void test_matrix() { 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 good_range{chars_first, to_address(chars_last)}; + const ranges::subrange bad_range{chars_first, to_address(chars_last) + 1}; + const ranges::subrange bad_range_2 = [&] { + if constexpr (Nothrow) { + return ranges::subrange{chars_first - 1, to_address(chars_last)}; + } 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 } From 9c436be24d99dcbd285f232ebfe46fc75fb41138 Mon Sep 17 00:00:00 2001 From: NylteJ <178683943+NylteJ@users.noreply.github.com> Date: Tue, 4 Nov 2025 19:36:14 +0800 Subject: [PATCH 25/35] Extract `last_ptr` to simplify --- .../P3349R1_contiguous_iterators_to_pointers/test.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp index 8513c4a1fe1..45f463a9ba8 100644 --- a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp @@ -471,12 +471,13 @@ void test_matrix() { 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, to_address(chars_last)}; - const ranges::subrange bad_range{chars_first, to_address(chars_last) + 1}; + 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, to_address(chars_last)}; + return ranges::subrange{chars_first - 1, last_ptr}; } else { return bad_range; } From e96330101426d7597f1d081b0613441c8abf0ef9 Mon Sep 17 00:00:00 2001 From: NylteJ <178683943+NylteJ@users.noreply.github.com> Date: Tue, 4 Nov 2025 20:55:31 +0800 Subject: [PATCH 26/35] Format --- .../P3349R1_contiguous_iterators_to_pointers/env.lst | 11 ++++------- .../P3349R1_contiguous_iterators_to_pointers/test.cpp | 2 +- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/env.lst b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/env.lst index cf749a657f1..78fbe6b49a4 100644 --- a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/env.lst +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/env.lst @@ -1,10 +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" \ No newline at end of file +RUNALL_INCLUDE ..\usual_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 index 45f463a9ba8..9fd4299bea2 100644 --- a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp @@ -447,7 +447,7 @@ void test_matrix() { { using const_iter = safe_iter; - const auto rng = {1, 1, 2, 3, 3, 3}; + const auto rng = {1, 1, 2, 3, 3, 3}; const auto valid_first_ptr = rng.begin() + 1; const auto valid_last_ptr = rng.end() - 1; From b897b7f981b258bca74d57fc89d9a4cd9ddd2259 Mon Sep 17 00:00:00 2001 From: NylteJ <178683943+NylteJ@users.noreply.github.com> Date: Tue, 4 Nov 2025 20:58:38 +0800 Subject: [PATCH 27/35] No, not usual_matrix Sorry for that... --- .../std/tests/P3349R1_contiguous_iterators_to_pointers/env.lst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/env.lst b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/env.lst index 78fbe6b49a4..e05d343051d 100644 --- a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/env.lst +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/env.lst @@ -1,7 +1,7 @@ # Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -RUNALL_INCLUDE ..\usual_matrix.lst +RUNALL_INCLUDE ..\usual_20_matrix.lst RUNALL_CROSSLIST * PM_CL="" * PM_CL="/D_USE_STD_VECTOR_ALGORITHMS=0" From a851166de45f1b8c8005252fa6694dda6108f4e0 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 4 Nov 2025 06:15:52 -0800 Subject: [PATCH 28/35] Explain the remove_copy/unique_copy limitation in more detail, citing the Standard. --- stl/inc/algorithm | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 02f95e3ed92..e95528c127d 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -465,8 +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 (is_pointer_v<_Out>) { // P3349R1: Non-pointer contiguous iterators prevent vectorization (unknown - // output size) + // 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; From 47fab07d57653dedb1f4f40262b90eede10d2de0 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 4 Nov 2025 06:19:17 -0800 Subject: [PATCH 29/35] Move the increment/decrement comment to `operator+=` where it applies. --- .../tests/P3349R1_contiguous_iterators_to_pointers/test.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp index 9fd4299bea2..e3eb1b1c431 100644 --- a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp @@ -30,8 +30,6 @@ constexpr T* unwrap(T* ptr) { return ptr; } -// 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 false, throws immediately on bounds violation. template class safe_iter { public: @@ -71,6 +69,9 @@ class safe_iter { 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) { From 051ebcf93dbb6336b2f1e93aa293c2c43fd7aa10 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 4 Nov 2025 06:26:57 -0800 Subject: [PATCH 30/35] unwrap() can be noexcept. --- .../tests/P3349R1_contiguous_iterators_to_pointers/test.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp index e3eb1b1c431..8dca64cbd3f 100644 --- a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp @@ -26,7 +26,7 @@ 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) { +constexpr T* unwrap(T* ptr) noexcept { return ptr; } @@ -153,7 +153,7 @@ class safe_iter { return {safe_iter{first_ptr, first_ptr, last_ptr}, safe_iter{last_ptr, first_ptr, last_ptr}}; } - friend constexpr T* unwrap(const safe_iter& iter) { + friend constexpr T* unwrap(const safe_iter& iter) noexcept { return iter.current_ptr; } From 5c955366dcbbdc8e55c397caf024b40a31d4666a Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 4 Nov 2025 06:27:28 -0800 Subject: [PATCH 31/35] `safe_iter` consistently uses `pointer`. --- .../std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp index 8dca64cbd3f..83e207f92cc 100644 --- a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp @@ -153,7 +153,7 @@ class safe_iter { return {safe_iter{first_ptr, first_ptr, last_ptr}, safe_iter{last_ptr, first_ptr, last_ptr}}; } - friend constexpr T* unwrap(const safe_iter& iter) noexcept { + friend constexpr pointer unwrap(const safe_iter& iter) noexcept { return iter.current_ptr; } From cbcebf2358a96e27b79ed04114c1896782b1f4f4 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 4 Nov 2025 06:33:39 -0800 Subject: [PATCH 32/35] Add one more assertion for completeness. --- .../std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp index 83e207f92cc..311bf15e4a9 100644 --- a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp @@ -236,6 +236,7 @@ void test() { // [ ( < > ) ] 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); From 2f2bb28528e5180fac15c3a3ba8267a22a1e4779 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 4 Nov 2025 07:05:52 -0800 Subject: [PATCH 33/35] Unique numbers for unique purposes. --- .../test.cpp | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp index 311bf15e4a9..a82d1cb8ac3 100644 --- a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp @@ -424,7 +424,7 @@ void test_matrix() { using iter = safe_iter; using const_iter = safe_iter; - const auto src = {1, 2, 3, 4, 4, 5, 5}; + const auto src = {10, 20, 30, 40, 40, 50, 50}; const auto src_first = const_iter::begin(src); const auto src_last = const_iter::end(src); @@ -434,10 +434,10 @@ void test_matrix() { 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, 5)); - FAIL(remove_copy(src_first, src_last, dst_first, 1)); - PASS(ranges::remove_copy(src_first, src_last_ptr, dst_first, 5)); - FAIL(ranges::remove_copy(src_first, src_last_ptr, dst_first, 1)); + 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)); @@ -449,7 +449,7 @@ void test_matrix() { { using const_iter = safe_iter; - const auto rng = {1, 1, 2, 3, 3, 3}; + const auto rng = {10, 10, 20, 30, 30, 30}; const auto valid_first_ptr = rng.begin() + 1; const auto valid_last_ptr = rng.end() - 1; @@ -457,13 +457,13 @@ void test_matrix() { const const_iter rng_last{valid_last_ptr, valid_first_ptr, valid_last_ptr}; const auto rng_last_ptr = to_address(rng_last); - PASS(search_n(rng_first, rng_last, 2, 3)); - FAIL(search_n(rng_first, rng_last + 1, 3, 3)); - FAIL(search_n(rng_first - 1, rng_last, 2, 1)); + 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, rng_last_ptr, 2, 3)); - FAIL(ranges::search_n(rng_first, rng_last_ptr + 1, 3, 3)); - FAIL(ranges::search_n(rng_first - 1, rng_last_ptr, 2, 1)); + PASS(ranges::search_n(rng_first, rng_last_ptr, 2, 30)); + FAIL(ranges::search_n(rng_first, rng_last_ptr + 1, 3, 30)); + FAIL(ranges::search_n(rng_first - 1, rng_last_ptr, 2, 10)); } #if _HAS_CXX23 From 5e22a563241f5412df73eaa5aa357feebcff6db7 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 4 Nov 2025 07:09:30 -0800 Subject: [PATCH 34/35] Use `array` instead of `initializer_list`. --- .../P3349R1_contiguous_iterators_to_pointers/test.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp index a82d1cb8ac3..d07e6ee5b29 100644 --- a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp @@ -424,7 +424,7 @@ void test_matrix() { using iter = safe_iter; using const_iter = safe_iter; - const auto src = {10, 20, 30, 40, 40, 50, 50}; + 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); @@ -449,9 +449,9 @@ void test_matrix() { { using const_iter = safe_iter; - const auto rng = {10, 10, 20, 30, 30, 30}; - const auto valid_first_ptr = rng.begin() + 1; - const auto valid_last_ptr = rng.end() - 1; + 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}; From 2f163a73fc4321f473cb4eb12b17ecd0a5d1bd1a Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Tue, 4 Nov 2025 07:11:17 -0800 Subject: [PATCH 35/35] Replace rng_last_ptr with valid_last_ptr. --- .../P3349R1_contiguous_iterators_to_pointers/test.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp index d07e6ee5b29..a5136023707 100644 --- a/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp +++ b/tests/std/tests/P3349R1_contiguous_iterators_to_pointers/test.cpp @@ -455,15 +455,14 @@ void test_matrix() { 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}; - const auto rng_last_ptr = to_address(rng_last); 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, rng_last_ptr, 2, 30)); - FAIL(ranges::search_n(rng_first, rng_last_ptr + 1, 3, 30)); - FAIL(ranges::search_n(rng_first - 1, rng_last_ptr, 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