From 71852bb6b0c8c4f81aa434f119b91c2be3a912e5 Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Tue, 9 Aug 2022 15:34:43 -0700 Subject: [PATCH 01/12] Add verification of ranges non-overlap This is a squash of some existing work from Arzaghi, plus a bunch of work from nimazzuc Co-authored-by: Hamid Reza Arzaghi --- stl/inc/algorithm | 1 + stl/inc/vector | 8 +++ stl/inc/xutility | 66 ++++++++++++++++++- .../VSO_0180466_algorithm_overhauls/test.cpp | 23 +++++-- 4 files changed, 91 insertions(+), 7 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index a54ef6f99e6..f56ccc5d467 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -3046,6 +3046,7 @@ template _CONSTEXPR20 _FwdIt2 swap_ranges(const _FwdIt1 _First1, const _FwdIt1 _Last1, _FwdIt2 _First2) { // swap [_First1, _Last1) with [_First2, ...) _Adl_verify_range(_First1, _Last1); + const auto _UFirst1 = _Get_unwrapped(_First1); const auto _ULast1 = _Get_unwrapped(_Last1); const auto _UFirst2 = _Get_unwrapped_n(_First2, _Idl_distance<_FwdIt1>(_UFirst1, _ULast1)); diff --git a/stl/inc/vector b/stl/inc/vector index 9689a5862c0..7b66c32ea54 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -1363,6 +1363,14 @@ public: pointer& _Myfirst = _My_data._Myfirst; pointer& _Mylast = _My_data._Mylast; +#if _ITERATOR_DEBUG_LEVEL == 2 + { + const auto _Valptr = _STD addressof(_Val); + _STL_VERIFY(!(_Unfancy(_Myfirst) <= _Valptr && _Valptr < _Unfancy(_Mylast)), + "assignment value cannot be a reference into the container"); + } +#endif // _ITERATOR_DEBUG_LEVEL == 2 + constexpr bool _Nothrow_construct = conjunction_v, _Uses_default_construct<_Alloc, _Ty*, const _Ty&>>; diff --git a/stl/inc/xutility b/stl/inc/xutility index 65d7fa0ea10..0f41cac2dbc 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -3669,6 +3669,60 @@ struct _Iter_copy_cat<_SourceIt, _DestIt, false> : _False_trivial_cat {}; template struct _Iter_copy_cat, _DestIt, false> : _Iter_move_cat<_SourceIt, _DestIt> {}; +template +constexpr void _Verify_ranges_do_not_overlap( + const _Iter1& _First1, const _Sent1& _Last1, const _Iter2& _First2, const _Sent2& _Last2) { + (void) _First1; + (void) _Last1; + (void) _First2; + (void) _Last2; +#if _ITERATOR_DEBUG_LEVEL == 2 + if (!_STD is_constant_evaluated()) { +#ifdef __cpp_lib_concepts + if constexpr (totally_ordered_with<_Iter1, _Sent2> && totally_ordered_with<_Sent1, _Iter2>) { +#else // ^^^ __cpp_lib_concepts / __cpp_lib_concepts vvv + if constexpr (is_same_v<_Iter1, _Sent1> && is_same_v<_Sent1, _Iter2> && is_same_v<_Iter2, _Sent2> // + && _Is_ranges_random_iter_v<_Iter1>) { +#endif // __cpp_lib_concepts + _STL_VERIFY(_Last1 <= _First2 || _Last2 <= _First1, "ranges should not overlap each other"); + } else if constexpr (_Iterators_are_contiguous<_Iter1, _Iter2> +#if __cpp_lib_concepts + && sized_sentinel_for<_Sent1, _Iter1> && sized_sentinel_for<_Sent2, _Iter2> +#endif // __cpp_lib_concepts + ) { + const auto _Ptr1Offset = (_Last1 - _First1) * sizeof(*_To_address(_First1)); + const auto _Ptr2Offset = (_Last2 - _First2) * sizeof(*_To_address(_First2)); + // This cast to `cv char*` allows us to compare pointers to distinct types, + // in case one range provides storage for the other. + const auto _PtrFirst1 = reinterpret_cast(_To_address(_First1)); + const auto _PtrLast1 = _PtrFirst1 + _Ptr1Offset; + const auto _PtrFirst2 = reinterpret_cast(_To_address(_First2)); + const auto _PtrLast2 = _PtrFirst2 + _Ptr2Offset; + _STL_VERIFY(_PtrLast1 <= _PtrFirst2 || _PtrLast2 <= _PtrFirst1, "ranges should not overlap each other"); + } + } +#endif // _ITERATOR_DEBUG_LEVEL == 2 +} + +template +constexpr void _Verify_ranges_do_not_overlap(const _Iter1& _First1, const _Sent1& _Last1, const _Iter2& _First2) { + (void) _First1; + (void) _Last1; + (void) _First2; +#if _ITERATOR_DEBUG_LEVEL == 2 + if (!_STD is_constant_evaluated()) { +#ifdef __cpp_lib_concepts + if constexpr (sized_sentinel_for<_Sent1, _Iter1> && random_access_iterator<_Iter2>) { +#else // ^^^ __cpp_lib_concepts / __cpp_lib_concepts vvv + if constexpr (is_same_v<_Iter1, _Sent1> // + && _Is_cpp17_random_iter_v<_Iter1> && _Is_cpp17_random_iter_v<_Iter2>) { +#endif // __cpp_lib_concepts + _Verify_ranges_do_not_overlap(_First1, _Last1, _First2, _First2 + (_Last1 - _First1)); + } + } +#endif // _ITERATOR_DEBUG_LEVEL == 2 +} + template _OutCtgIt _Copy_memmove(_CtgIt _First, _CtgIt _Last, _OutCtgIt _Dest) { auto _FirstPtr = _To_address(_First); @@ -3736,6 +3790,9 @@ template _CONSTEXPR20 _OutIt _Copy_unchecked(_InIt _First, _Sent _Last, _OutIt _Dest) { // copy [_First, _Last) to [_Dest, ...) // note: _Copy_unchecked has callers other than the copy family + + _Verify_ranges_do_not_overlap(_First, _Last, _Dest); + if constexpr (_Sent_copy_cat<_InIt, _Sent, _OutIt>::_Bitcopy_assignable) { #if _HAS_CXX20 if (!_STD is_constant_evaluated()) @@ -4355,8 +4412,8 @@ template _INLINE_VAR constexpr bool _Can_memcmp_elements<_Elem1, _Elem2, false> = false; // _Can_memcmp_elements_with_pred<_Elem1, _Elem2, _Pr> reports whether the memcmp optimization is applicable, -// given contiguously stored elements. (This avoids having to repeat the metaprogramming that finds the element types.) -// _Elem1 and _Elem2 aren't top-level const here. +// given contiguously stored elements. (This avoids having to repeat the metaprogramming that finds the element +// types.) _Elem1 and _Elem2 aren't top-level const here. template _INLINE_VAR constexpr bool _Can_memcmp_elements_with_pred = false; @@ -4918,7 +4975,8 @@ _INLINE_VAR constexpr bool _Vector_alg_in_find_is_safe = // Can we activate the #endif // __cpp_lib_byte conjunction, is_integral<_Elem>>, // We're finding an integer in a range of integers. // The integer types can be different, which requires careful handling. - conjunction, is_same<_Ty, _Elem>>>; // We're finding a U* in a range of U* (identical types). + conjunction, is_same<_Ty, _Elem>>>; // We're finding a U* in a range of U* (identical + // types). template _NODISCARD _CONSTEXPR20 _InIt _Find_unchecked(_InIt _First, const _InIt _Last, const _Ty& _Val) { @@ -5820,6 +5878,8 @@ template _CONSTEXPR20 _FwdIt2 _Swap_ranges_unchecked(_FwdIt1 _First1, const _FwdIt1 _Last1, _FwdIt2 _First2) { // swap [_First1, _Last1) with [_First2, ...) + _Verify_ranges_do_not_overlap(_First1, _Last1, _First2); + #if _USE_STD_VECTOR_ALGORITHMS using _Elem1 = remove_reference_t<_Iter_ref_t<_FwdIt1>>; using _Elem2 = remove_reference_t<_Iter_ref_t<_FwdIt2>>; diff --git a/tests/std/tests/VSO_0180466_algorithm_overhauls/test.cpp b/tests/std/tests/VSO_0180466_algorithm_overhauls/test.cpp index 9ebd654925a..63ecb94d0c3 100644 --- a/tests/std/tests/VSO_0180466_algorithm_overhauls/test.cpp +++ b/tests/std/tests/VSO_0180466_algorithm_overhauls/test.cpp @@ -343,10 +343,25 @@ namespace test_lexicographical_compare { namespace test_std_copy { void test() { - array target{{42, 43, 44, 45}}; - array input{{1729, 1730}}; - copy(input.begin(), input.end(), target.begin() + 1); - assert((target == array{{42, 1729, 1730, 45}})); + { + array target{{42, 43, 44, 45}}; + array input{{1729, 1730}}; + copy(input.begin(), input.end(), target.begin() + 1); + assert((target == array{{42, 1729, 1730, 45}})); + } + { + // GH-177: copy different-element-types ranges. + const array input{10, 20}; + array target; + copy(input.begin(), input.end(), target.begin()); + assert((target == array{{10, 20}})); + } + { + // GH-177: copy partial-overlapping ranges. + array input{10, 20, 30, 40}; + copy(input.begin() + 1, input.end(), input.begin()); + assert((input == array{{20, 30, 40, 40}})); + } } } // namespace test_std_copy From 5c4561fe01411ec37340d0c0662949397a6d9209 Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Tue, 9 Aug 2022 15:47:34 -0700 Subject: [PATCH 02/12] fix remaining weirdnesses --- stl/inc/algorithm | 1 - stl/inc/xutility | 41 ++++++++++++++++++++++++----------------- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index f56ccc5d467..a54ef6f99e6 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -3046,7 +3046,6 @@ template _CONSTEXPR20 _FwdIt2 swap_ranges(const _FwdIt1 _First1, const _FwdIt1 _Last1, _FwdIt2 _First2) { // swap [_First1, _Last1) with [_First2, ...) _Adl_verify_range(_First1, _Last1); - const auto _UFirst1 = _Get_unwrapped(_First1); const auto _ULast1 = _Get_unwrapped(_Last1); const auto _UFirst2 = _Get_unwrapped_n(_First2, _Idl_distance<_FwdIt1>(_UFirst1, _ULast1)); diff --git a/stl/inc/xutility b/stl/inc/xutility index 0f41cac2dbc..717f590b654 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -3670,14 +3670,13 @@ template struct _Iter_copy_cat, _DestIt, false> : _Iter_move_cat<_SourceIt, _DestIt> {}; template -constexpr void _Verify_ranges_do_not_overlap( +_CONSTEXPR20 void _Verify_ranges_do_not_overlap( const _Iter1& _First1, const _Sent1& _Last1, const _Iter2& _First2, const _Sent2& _Last2) { - (void) _First1; - (void) _Last1; - (void) _First2; - (void) _Last2; #if _ITERATOR_DEBUG_LEVEL == 2 - if (!_STD is_constant_evaluated()) { +#if _HAS_CXX20 + if (!_STD is_constant_evaluated()) +#endif // _HAS_CXX20 + { #ifdef __cpp_lib_concepts if constexpr (totally_ordered_with<_Iter1, _Sent2> && totally_ordered_with<_Sent1, _Iter2>) { #else // ^^^ __cpp_lib_concepts / __cpp_lib_concepts vvv @@ -3701,16 +3700,21 @@ constexpr void _Verify_ranges_do_not_overlap( _STL_VERIFY(_PtrLast1 <= _PtrFirst2 || _PtrLast2 <= _PtrFirst1, "ranges should not overlap each other"); } } -#endif // _ITERATOR_DEBUG_LEVEL == 2 -} - -template -constexpr void _Verify_ranges_do_not_overlap(const _Iter1& _First1, const _Sent1& _Last1, const _Iter2& _First2) { +#else // ^^^ _ITERATOR_DEBUG_LEVEL == 2 / _ITERATOR_DEBUG_LEVEL != 2 vvv (void) _First1; (void) _Last1; (void) _First2; + (void) _Last2; +#endif // _ITERATOR_DEBUG_LEVEL != 2 ^^^ +} + +template +_CONSTEXPR20 void _Verify_ranges_do_not_overlap(const _Iter1& _First1, const _Sent1& _Last1, const _Iter2& _First2) { #if _ITERATOR_DEBUG_LEVEL == 2 - if (!_STD is_constant_evaluated()) { +#if _HAS_CXX20 + if (!_STD is_constant_evaluated()) +#endif // _HAS_CXX20 + { #ifdef __cpp_lib_concepts if constexpr (sized_sentinel_for<_Sent1, _Iter1> && random_access_iterator<_Iter2>) { #else // ^^^ __cpp_lib_concepts / __cpp_lib_concepts vvv @@ -3720,7 +3724,11 @@ constexpr void _Verify_ranges_do_not_overlap(const _Iter1& _First1, const _Sent1 _Verify_ranges_do_not_overlap(_First1, _Last1, _First2, _First2 + (_Last1 - _First1)); } } -#endif // _ITERATOR_DEBUG_LEVEL == 2 +#else // ^^^ _ITERATOR_DEBUG_LEVEL == 2 / _ITERATOR_DEBUG_LEVEL != 2 vvv + (void) _First1; + (void) _Last1; + (void) _First2; +#endif // _ITERATOR_DEBUG_LEVEL != 2 ^^^ } template @@ -4412,8 +4420,8 @@ template _INLINE_VAR constexpr bool _Can_memcmp_elements<_Elem1, _Elem2, false> = false; // _Can_memcmp_elements_with_pred<_Elem1, _Elem2, _Pr> reports whether the memcmp optimization is applicable, -// given contiguously stored elements. (This avoids having to repeat the metaprogramming that finds the element -// types.) _Elem1 and _Elem2 aren't top-level const here. +// given contiguously stored elements. (This avoids having to repeat the metaprogramming that finds the element types.) +// _Elem1 and _Elem2 aren't top-level const here. template _INLINE_VAR constexpr bool _Can_memcmp_elements_with_pred = false; @@ -4975,8 +4983,7 @@ _INLINE_VAR constexpr bool _Vector_alg_in_find_is_safe = // Can we activate the #endif // __cpp_lib_byte conjunction, is_integral<_Elem>>, // We're finding an integer in a range of integers. // The integer types can be different, which requires careful handling. - conjunction, is_same<_Ty, _Elem>>>; // We're finding a U* in a range of U* (identical - // types). + conjunction, is_same<_Ty, _Elem>>>; // We're finding a U* in a range of U* (identical types). template _NODISCARD _CONSTEXPR20 _InIt _Find_unchecked(_InIt _First, const _InIt _Last, const _Ty& _Val) { From d5ed82719ad24be83b0178a3587bbd0d29035823 Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Tue, 9 Aug 2022 15:53:44 -0700 Subject: [PATCH 03/12] if -> ifdef --- stl/inc/xutility | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index 717f590b654..677b013ac73 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -3685,7 +3685,7 @@ _CONSTEXPR20 void _Verify_ranges_do_not_overlap( #endif // __cpp_lib_concepts _STL_VERIFY(_Last1 <= _First2 || _Last2 <= _First1, "ranges should not overlap each other"); } else if constexpr (_Iterators_are_contiguous<_Iter1, _Iter2> -#if __cpp_lib_concepts +#ifdef __cpp_lib_concepts && sized_sentinel_for<_Sent1, _Iter1> && sized_sentinel_for<_Sent2, _Iter2> #endif // __cpp_lib_concepts ) { From 83f8d06a190b282952b505803eeef37cfa545f86 Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Wed, 10 Aug 2022 10:27:07 -0700 Subject: [PATCH 04/12] fix tests hopefully? the std tests work --- stl/inc/vector | 3 +++ stl/inc/xutility | 32 +++++++++++++++++--------------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/stl/inc/vector b/stl/inc/vector index 7b66c32ea54..f83fbe1529f 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -1364,6 +1364,9 @@ public: pointer& _Mylast = _My_data._Mylast; #if _ITERATOR_DEBUG_LEVEL == 2 +#if _HAS_CXX20 + if (!_STD is_constant_evaluated()) +#endif { const auto _Valptr = _STD addressof(_Val); _STL_VERIFY(!(_Unfancy(_Myfirst) <= _Valptr && _Valptr < _Unfancy(_Mylast)), diff --git a/stl/inc/xutility b/stl/inc/xutility index 677b013ac73..2e85a89835e 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -11,6 +11,7 @@ #include <__msvc_iter_core.hpp> #include +#include #include #include @@ -3677,16 +3678,9 @@ _CONSTEXPR20 void _Verify_ranges_do_not_overlap( if (!_STD is_constant_evaluated()) #endif // _HAS_CXX20 { + if constexpr (_Iterators_are_contiguous<_Iter1, _Iter2> #ifdef __cpp_lib_concepts - if constexpr (totally_ordered_with<_Iter1, _Sent2> && totally_ordered_with<_Sent1, _Iter2>) { -#else // ^^^ __cpp_lib_concepts / __cpp_lib_concepts vvv - if constexpr (is_same_v<_Iter1, _Sent1> && is_same_v<_Sent1, _Iter2> && is_same_v<_Iter2, _Sent2> // - && _Is_ranges_random_iter_v<_Iter1>) { -#endif // __cpp_lib_concepts - _STL_VERIFY(_Last1 <= _First2 || _Last2 <= _First1, "ranges should not overlap each other"); - } else if constexpr (_Iterators_are_contiguous<_Iter1, _Iter2> -#ifdef __cpp_lib_concepts - && sized_sentinel_for<_Sent1, _Iter1> && sized_sentinel_for<_Sent2, _Iter2> + && sized_sentinel_for<_Sent1, _Iter1> && sized_sentinel_for<_Sent2, _Iter2> #endif // __cpp_lib_concepts ) { const auto _Ptr1Offset = (_Last1 - _First1) * sizeof(*_To_address(_First1)); @@ -3715,13 +3709,21 @@ _CONSTEXPR20 void _Verify_ranges_do_not_overlap(const _Iter1& _First1, const _Se if (!_STD is_constant_evaluated()) #endif // _HAS_CXX20 { + if constexpr (_Iterators_are_contiguous<_Iter1, _Iter2> #ifdef __cpp_lib_concepts - if constexpr (sized_sentinel_for<_Sent1, _Iter1> && random_access_iterator<_Iter2>) { -#else // ^^^ __cpp_lib_concepts / __cpp_lib_concepts vvv - if constexpr (is_same_v<_Iter1, _Sent1> // - && _Is_cpp17_random_iter_v<_Iter1> && _Is_cpp17_random_iter_v<_Iter2>) { + && sized_sentinel_for<_Sent1, _Iter1> #endif // __cpp_lib_concepts - _Verify_ranges_do_not_overlap(_First1, _Last1, _First2, _First2 + (_Last1 - _First1)); + ) { + const auto _Offset = _Last1 - _First1; + const auto _Ptr1Offset = _Offset * sizeof(*_To_address(_First1)); + const auto _Ptr2Offset = _Offset * sizeof(*_To_address(_First2)); + // This cast to `cv char*` allows us to compare pointers to distinct types, + // in case one range provides storage for the other. + const auto _PtrFirst1 = reinterpret_cast(_To_address(_First1)); + const auto _PtrLast1 = _PtrFirst1 + _Ptr1Offset; + const auto _PtrFirst2 = reinterpret_cast(_To_address(_First2)); + const auto _PtrLast2 = _PtrFirst2 + _Ptr2Offset; + _STL_VERIFY(_PtrLast1 <= _PtrFirst2 || _PtrLast2 <= _PtrFirst1, "ranges should not overlap each other"); } } #else // ^^^ _ITERATOR_DEBUG_LEVEL == 2 / _ITERATOR_DEBUG_LEVEL != 2 vvv @@ -3799,7 +3801,7 @@ _CONSTEXPR20 _OutIt _Copy_unchecked(_InIt _First, _Sent _Last, _OutIt _Dest) { // copy [_First, _Last) to [_Dest, ...) // note: _Copy_unchecked has callers other than the copy family - _Verify_ranges_do_not_overlap(_First, _Last, _Dest); + //_Verify_ranges_do_not_overlap(_First, _Last, _Dest); if constexpr (_Sent_copy_cat<_InIt, _Sent, _OutIt>::_Bitcopy_assignable) { #if _HAS_CXX20 From 6dbe1e3a77a62b2a982abc4128e959219ea36f03 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 10 Aug 2022 21:46:47 -0700 Subject: [PATCH 05/12] Add preprocessor comment. --- stl/inc/vector | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/vector b/stl/inc/vector index f83fbe1529f..23f2b1814c8 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -1366,7 +1366,7 @@ public: #if _ITERATOR_DEBUG_LEVEL == 2 #if _HAS_CXX20 if (!_STD is_constant_evaluated()) -#endif +#endif // _HAS_CXX20 { const auto _Valptr = _STD addressof(_Val); _STL_VERIFY(!(_Unfancy(_Myfirst) <= _Valptr && _Valptr < _Unfancy(_Mylast)), From 3dd6d4f4b4a9eed45499091511cea77aedd44935 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 10 Aug 2022 21:47:24 -0700 Subject: [PATCH 06/12] doesn't need to include . --- stl/inc/xutility | 1 - 1 file changed, 1 deletion(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index 2e85a89835e..6408c57dacc 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -11,7 +11,6 @@ #include <__msvc_iter_core.hpp> #include -#include #include #include From fce301681cbcef0924312ed3c9ca02303d986833 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 10 Aug 2022 21:48:14 -0700 Subject: [PATCH 07/12] Early return within `if constexpr`. --- stl/inc/xutility | 68 +++++++++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index 6408c57dacc..5636b43b727 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -3673,25 +3673,26 @@ template _CONSTEXPR20 void _Verify_ranges_do_not_overlap( const _Iter1& _First1, const _Sent1& _Last1, const _Iter2& _First2, const _Sent2& _Last2) { #if _ITERATOR_DEBUG_LEVEL == 2 -#if _HAS_CXX20 - if (!_STD is_constant_evaluated()) -#endif // _HAS_CXX20 - { - if constexpr (_Iterators_are_contiguous<_Iter1, _Iter2> + if constexpr (_Iterators_are_contiguous<_Iter1, _Iter2> #ifdef __cpp_lib_concepts - && sized_sentinel_for<_Sent1, _Iter1> && sized_sentinel_for<_Sent2, _Iter2> + && sized_sentinel_for<_Sent1, _Iter1> && sized_sentinel_for<_Sent2, _Iter2> #endif // __cpp_lib_concepts - ) { - const auto _Ptr1Offset = (_Last1 - _First1) * sizeof(*_To_address(_First1)); - const auto _Ptr2Offset = (_Last2 - _First2) * sizeof(*_To_address(_First2)); - // This cast to `cv char*` allows us to compare pointers to distinct types, - // in case one range provides storage for the other. - const auto _PtrFirst1 = reinterpret_cast(_To_address(_First1)); - const auto _PtrLast1 = _PtrFirst1 + _Ptr1Offset; - const auto _PtrFirst2 = reinterpret_cast(_To_address(_First2)); - const auto _PtrLast2 = _PtrFirst2 + _Ptr2Offset; - _STL_VERIFY(_PtrLast1 <= _PtrFirst2 || _PtrLast2 <= _PtrFirst1, "ranges should not overlap each other"); + ) { +#if _HAS_CXX20 + if (_STD is_constant_evaluated()) { + return; } +#endif // _HAS_CXX20 + + const auto _Ptr1Offset = (_Last1 - _First1) * sizeof(*_To_address(_First1)); + const auto _Ptr2Offset = (_Last2 - _First2) * sizeof(*_To_address(_First2)); + // This cast to `cv char*` allows us to compare pointers to distinct types, + // in case one range provides storage for the other. + const auto _PtrFirst1 = reinterpret_cast(_To_address(_First1)); + const auto _PtrLast1 = _PtrFirst1 + _Ptr1Offset; + const auto _PtrFirst2 = reinterpret_cast(_To_address(_First2)); + const auto _PtrLast2 = _PtrFirst2 + _Ptr2Offset; + _STL_VERIFY(_PtrLast1 <= _PtrFirst2 || _PtrLast2 <= _PtrFirst1, "ranges should not overlap each other"); } #else // ^^^ _ITERATOR_DEBUG_LEVEL == 2 / _ITERATOR_DEBUG_LEVEL != 2 vvv (void) _First1; @@ -3704,26 +3705,27 @@ _CONSTEXPR20 void _Verify_ranges_do_not_overlap( template _CONSTEXPR20 void _Verify_ranges_do_not_overlap(const _Iter1& _First1, const _Sent1& _Last1, const _Iter2& _First2) { #if _ITERATOR_DEBUG_LEVEL == 2 -#if _HAS_CXX20 - if (!_STD is_constant_evaluated()) -#endif // _HAS_CXX20 - { - if constexpr (_Iterators_are_contiguous<_Iter1, _Iter2> + if constexpr (_Iterators_are_contiguous<_Iter1, _Iter2> #ifdef __cpp_lib_concepts - && sized_sentinel_for<_Sent1, _Iter1> + && sized_sentinel_for<_Sent1, _Iter1> #endif // __cpp_lib_concepts - ) { - const auto _Offset = _Last1 - _First1; - const auto _Ptr1Offset = _Offset * sizeof(*_To_address(_First1)); - const auto _Ptr2Offset = _Offset * sizeof(*_To_address(_First2)); - // This cast to `cv char*` allows us to compare pointers to distinct types, - // in case one range provides storage for the other. - const auto _PtrFirst1 = reinterpret_cast(_To_address(_First1)); - const auto _PtrLast1 = _PtrFirst1 + _Ptr1Offset; - const auto _PtrFirst2 = reinterpret_cast(_To_address(_First2)); - const auto _PtrLast2 = _PtrFirst2 + _Ptr2Offset; - _STL_VERIFY(_PtrLast1 <= _PtrFirst2 || _PtrLast2 <= _PtrFirst1, "ranges should not overlap each other"); + ) { +#if _HAS_CXX20 + if (_STD is_constant_evaluated()) { + return; } +#endif // _HAS_CXX20 + + const auto _Offset = _Last1 - _First1; + const auto _Ptr1Offset = _Offset * sizeof(*_To_address(_First1)); + const auto _Ptr2Offset = _Offset * sizeof(*_To_address(_First2)); + // This cast to `cv char*` allows us to compare pointers to distinct types, + // in case one range provides storage for the other. + const auto _PtrFirst1 = reinterpret_cast(_To_address(_First1)); + const auto _PtrLast1 = _PtrFirst1 + _Ptr1Offset; + const auto _PtrFirst2 = reinterpret_cast(_To_address(_First2)); + const auto _PtrLast2 = _PtrFirst2 + _Ptr2Offset; + _STL_VERIFY(_PtrLast1 <= _PtrFirst2 || _PtrLast2 <= _PtrFirst1, "ranges should not overlap each other"); } #else // ^^^ _ITERATOR_DEBUG_LEVEL == 2 / _ITERATOR_DEBUG_LEVEL != 2 vvv (void) _First1; From 6c048d3c1dab854eb4a126d40a82ce25d20064ba Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 10 Aug 2022 21:49:22 -0700 Subject: [PATCH 08/12] Move call from _Swap_ranges_unchecked to swap_ranges. --- stl/inc/algorithm | 1 + stl/inc/xutility | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index a54ef6f99e6..cb816e26aa7 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -3049,6 +3049,7 @@ _CONSTEXPR20 _FwdIt2 swap_ranges(const _FwdIt1 _First1, const _FwdIt1 _Last1, _F const auto _UFirst1 = _Get_unwrapped(_First1); const auto _ULast1 = _Get_unwrapped(_Last1); const auto _UFirst2 = _Get_unwrapped_n(_First2, _Idl_distance<_FwdIt1>(_UFirst1, _ULast1)); + _Verify_ranges_do_not_overlap(_UFirst1, _ULast1, _UFirst2); _Seek_wrapped(_First2, _Swap_ranges_unchecked(_UFirst1, _ULast1, _UFirst2)); return _First2; } diff --git a/stl/inc/xutility b/stl/inc/xutility index 5636b43b727..29010265e7d 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -5888,8 +5888,6 @@ template _CONSTEXPR20 _FwdIt2 _Swap_ranges_unchecked(_FwdIt1 _First1, const _FwdIt1 _Last1, _FwdIt2 _First2) { // swap [_First1, _Last1) with [_First2, ...) - _Verify_ranges_do_not_overlap(_First1, _Last1, _First2); - #if _USE_STD_VECTOR_ALGORITHMS using _Elem1 = remove_reference_t<_Iter_ref_t<_FwdIt1>>; using _Elem2 = remove_reference_t<_Iter_ref_t<_FwdIt2>>; From 3fd1639deb95ff9b172252f78f6af475e1cfae0f Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 10 Aug 2022 21:51:17 -0700 Subject: [PATCH 09/12] Drop commented-out code in _Copy_unchecked. --- stl/inc/xutility | 3 --- 1 file changed, 3 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index 29010265e7d..f85d1aeb1bd 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -3801,9 +3801,6 @@ template _CONSTEXPR20 _OutIt _Copy_unchecked(_InIt _First, _Sent _Last, _OutIt _Dest) { // copy [_First, _Last) to [_Dest, ...) // note: _Copy_unchecked has callers other than the copy family - - //_Verify_ranges_do_not_overlap(_First, _Last, _Dest); - if constexpr (_Sent_copy_cat<_InIt, _Sent, _OutIt>::_Bitcopy_assignable) { #if _HAS_CXX20 if (!_STD is_constant_evaluated()) From f4b8a9d5316f3d75ab2c10d7c3f4f1ede7a892ec Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 10 Aug 2022 21:52:49 -0700 Subject: [PATCH 10/12] Drop unused 4-arg overload. --- stl/inc/xutility | 33 --------------------------------- 1 file changed, 33 deletions(-) diff --git a/stl/inc/xutility b/stl/inc/xutility index f85d1aeb1bd..bebf9cc3882 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -3669,39 +3669,6 @@ struct _Iter_copy_cat<_SourceIt, _DestIt, false> : _False_trivial_cat {}; template struct _Iter_copy_cat, _DestIt, false> : _Iter_move_cat<_SourceIt, _DestIt> {}; -template -_CONSTEXPR20 void _Verify_ranges_do_not_overlap( - const _Iter1& _First1, const _Sent1& _Last1, const _Iter2& _First2, const _Sent2& _Last2) { -#if _ITERATOR_DEBUG_LEVEL == 2 - if constexpr (_Iterators_are_contiguous<_Iter1, _Iter2> -#ifdef __cpp_lib_concepts - && sized_sentinel_for<_Sent1, _Iter1> && sized_sentinel_for<_Sent2, _Iter2> -#endif // __cpp_lib_concepts - ) { -#if _HAS_CXX20 - if (_STD is_constant_evaluated()) { - return; - } -#endif // _HAS_CXX20 - - const auto _Ptr1Offset = (_Last1 - _First1) * sizeof(*_To_address(_First1)); - const auto _Ptr2Offset = (_Last2 - _First2) * sizeof(*_To_address(_First2)); - // This cast to `cv char*` allows us to compare pointers to distinct types, - // in case one range provides storage for the other. - const auto _PtrFirst1 = reinterpret_cast(_To_address(_First1)); - const auto _PtrLast1 = _PtrFirst1 + _Ptr1Offset; - const auto _PtrFirst2 = reinterpret_cast(_To_address(_First2)); - const auto _PtrLast2 = _PtrFirst2 + _Ptr2Offset; - _STL_VERIFY(_PtrLast1 <= _PtrFirst2 || _PtrLast2 <= _PtrFirst1, "ranges should not overlap each other"); - } -#else // ^^^ _ITERATOR_DEBUG_LEVEL == 2 / _ITERATOR_DEBUG_LEVEL != 2 vvv - (void) _First1; - (void) _Last1; - (void) _First2; - (void) _Last2; -#endif // _ITERATOR_DEBUG_LEVEL != 2 ^^^ -} - template _CONSTEXPR20 void _Verify_ranges_do_not_overlap(const _Iter1& _First1, const _Sent1& _Last1, const _Iter2& _First2) { #if _ITERATOR_DEBUG_LEVEL == 2 From 2b20bd183287c7d2a76eb4edbdd9fed3090c65af Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 10 Aug 2022 22:01:03 -0700 Subject: [PATCH 11/12] Add positive test coverage. --- .../test.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/std/tests/P0202R3_constexpr_algorithm_and_exchange/test.cpp b/tests/std/tests/P0202R3_constexpr_algorithm_and_exchange/test.cpp index dc51b5ce3f0..e9a7eb0d11b 100644 --- a/tests/std/tests/P0202R3_constexpr_algorithm_and_exchange/test.cpp +++ b/tests/std/tests/P0202R3_constexpr_algorithm_and_exchange/test.cpp @@ -550,6 +550,21 @@ constexpr void test_permutations() { } } +constexpr void test_adjacent_swap_ranges() { + int arr[8] = {10, 20, 30, 40, 50, 60, 70, 80}; + const int original[8] = {10, 20, 30, 40, 50, 60, 70, 80}; + const int modified[8] = {10, 50, 60, 70, 20, 30, 40, 80}; + + int* const first = arr + 1; + int* const mid = arr + 4; + int* const last = arr + 7; + + swap_ranges(first, mid, mid); + assert(equal(begin(arr), end(arr), begin(modified), end(modified))); + swap_ranges(mid, last, first); + assert(equal(begin(arr), end(arr), begin(original), end(original))); +} + constexpr bool test() { test_copy, output_pointer>(); test_copy(); @@ -603,6 +618,7 @@ constexpr bool test() { test_make_heap_and_sort_heap(); test_pop_heap_and_push_heap(); test_permutations(); + test_adjacent_swap_ranges(); return true; } From 9c070b6ffe56b0cd0d89a75a3e39b9923a72b0d6 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Wed, 10 Aug 2022 22:24:49 -0700 Subject: [PATCH 12/12] Add negative test coverage. --- tests/std/test.lst | 1 + .../GH_000177_forbidden_aliasing/env.lst | 4 ++ .../GH_000177_forbidden_aliasing/test.cpp | 49 +++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 tests/std/tests/GH_000177_forbidden_aliasing/env.lst create mode 100644 tests/std/tests/GH_000177_forbidden_aliasing/test.cpp diff --git a/tests/std/test.lst b/tests/std/test.lst index 89a623e9046..aff3f1fb635 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -155,6 +155,7 @@ tests\Dev11_1140665_unique_ptr_array_conversions tests\Dev11_1150223_shared_mutex tests\Dev11_1158803_regex_thread_safety tests\Dev11_1180290_filesystem_error_code +tests\GH_000177_forbidden_aliasing tests\GH_000342_filebuf_close tests\GH_000431_copy_move_family tests\GH_000431_equal_family diff --git a/tests/std/tests/GH_000177_forbidden_aliasing/env.lst b/tests/std/tests/GH_000177_forbidden_aliasing/env.lst new file mode 100644 index 00000000000..19f025bd0e6 --- /dev/null +++ b/tests/std/tests/GH_000177_forbidden_aliasing/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_matrix.lst diff --git a/tests/std/tests/GH_000177_forbidden_aliasing/test.cpp b/tests/std/tests/GH_000177_forbidden_aliasing/test.cpp new file mode 100644 index 00000000000..25c137c33c3 --- /dev/null +++ b/tests/std/tests/GH_000177_forbidden_aliasing/test.cpp @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include + +#include +using namespace std; + +void test_case_swap_ranges_overlap_1() { + int arr[8] = {10, 20, 30, 40, 50, 60, 70, 80}; + swap_ranges(arr + 1, arr + 4, arr + 3); +} + +void test_case_swap_ranges_overlap_2() { + int arr[8] = {10, 20, 30, 40, 50, 60, 70, 80}; + swap_ranges(arr + 4, arr + 7, arr + 2); +} + +void test_case_vector_assign_front() { + vector vec{11, 22, 33}; + vec.assign(5, vec.front()); +} + +void test_case_vector_assign_mid() { + vector vec{11, 22, 33}; + vec.assign(5, vec[1]); +} + +void test_case_vector_assign_back() { + vector vec{11, 22, 33}; + vec.assign(5, vec.back()); +} + +int main(int argc, char* argv[]) { + std_testing::death_test_executive exec; + +#if _ITERATOR_DEBUG_LEVEL == 2 + exec.add_death_tests({ + test_case_swap_ranges_overlap_1, + test_case_swap_ranges_overlap_2, + test_case_vector_assign_front, + test_case_vector_assign_mid, + test_case_vector_assign_back, + }); +#endif // _ITERATOR_DEBUG_LEVEL == 2 + + return exec.run(argc, argv); +}