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/vector b/stl/inc/vector index 9689a5862c0..23f2b1814c8 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -1363,6 +1363,17 @@ public: pointer& _Myfirst = _My_data._Myfirst; pointer& _Mylast = _My_data._Mylast; +#if _ITERATOR_DEBUG_LEVEL == 2 +#if _HAS_CXX20 + if (!_STD is_constant_evaluated()) +#endif // _HAS_CXX20 + { + 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..bebf9cc3882 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -3669,6 +3669,38 @@ 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) { +#if _ITERATOR_DEBUG_LEVEL == 2 + if constexpr (_Iterators_are_contiguous<_Iter1, _Iter2> +#ifdef __cpp_lib_concepts + && sized_sentinel_for<_Sent1, _Iter1> +#endif // __cpp_lib_concepts + ) { +#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; + (void) _Last1; + (void) _First2; +#endif // _ITERATOR_DEBUG_LEVEL != 2 ^^^ +} + template _OutCtgIt _Copy_memmove(_CtgIt _First, _CtgIt _Last, _OutCtgIt _Dest) { auto _FirstPtr = _To_address(_First); 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); +} 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; } 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