The overlapping range, will lead to move from moved-from object, which I think is generally expected to work. My preference is to land this fix for us misbehaving for correct inputs, and consider any additional assertions separately.
On Fri, Aug 15, 2025 at 12:27 PM Jonathan Wakely <jwakely....@gmail.com> wrote: > Maybe we should add a non-overlapping assertion to that algo (and similar > ones). For random access iterators we can check the full precondition, > otherwise just check that the iterator for the output range isn't equal to > the bound of the input range. > > On Thu, 14 Aug 2025, 14:30 Tomasz Kamiński, <tkami...@redhat.com> wrote: > >> For __n == 0, the elements where self move-assigned by >> std::move_backward(__ins, __old_finish - __n, __old_finish). >> >> PR libstdc++/121313 >> >> libstdc++-v3/ChangeLog: >> >> * include/bits/vector.tcc (vector::insert_range): Add check for >> empty size. >> * testsuite/23_containers/vector/modifiers/insert/insert_range.cc: >> New tests. >> --- >> Testing on x86_64-linux. Newly added test passed. >> >> OK for trunk and v15. >> >> libstdc++-v3/include/bits/vector.tcc | 10 ++-- >> .../vector/modifiers/insert/insert_range.cc | 50 +++++++++++++++++++ >> 2 files changed, 57 insertions(+), 3 deletions(-) >> >> diff --git a/libstdc++-v3/include/bits/vector.tcc >> b/libstdc++-v3/include/bits/vector.tcc >> index 70ead1d7083..1543ce9cd77 100644 >> --- a/libstdc++-v3/include/bits/vector.tcc >> +++ b/libstdc++-v3/include/bits/vector.tcc >> @@ -1007,17 +1007,21 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER >> >> if constexpr (ranges::forward_range<_Rg>) >> { >> + const auto __ins_idx = __pos - cbegin(); >> + // Number of new elements to insert: >> + const auto __n = size_type(ranges::distance(__rg)); >> + if (__n == 0) >> + return begin() + __ins_idx; >> + >> // Start of existing elements: >> pointer __old_start = this->_M_impl._M_start; >> // End of existing elements: >> pointer __old_finish = this->_M_impl._M_finish; >> // Insertion point: >> - const auto __ins_idx = __pos - cbegin(); >> pointer __ins = __old_start + __ins_idx; >> - // Number of new elements to insert: >> - const auto __n = size_type(ranges::distance(__rg)); >> // Number of elements that can fit in unused capacity: >> const auto __cap = this->_M_impl._M_end_of_storage - >> __old_finish; >> + >> if (__cap >= __n) >> { >> // Number of existing elements after insertion point: >> diff --git >> a/libstdc++-v3/testsuite/23_containers/vector/modifiers/insert/insert_range.cc >> b/libstdc++-v3/testsuite/23_containers/vector/modifiers/insert/insert_range.cc >> index 506bebbe519..e4b5982188f 100644 >> --- >> a/libstdc++-v3/testsuite/23_containers/vector/modifiers/insert/insert_range.cc >> +++ >> b/libstdc++-v3/testsuite/23_containers/vector/modifiers/insert/insert_range.cc >> @@ -99,8 +99,58 @@ test_ranges() >> return true; >> } >> >> +struct SelfAssignChecker { >> + static int moveCounter; >> + static int copyCounter; >> + >> + SelfAssignChecker() = default; >> + constexpr SelfAssignChecker(int v) : val(v) { } >> + SelfAssignChecker(const SelfAssignChecker&) = default; >> + SelfAssignChecker(SelfAssignChecker&&) = default; >> + >> + SelfAssignChecker operator=(const SelfAssignChecker& rhs) >> + { >> + if (this == &rhs) >> + ++copyCounter; >> + this->val = rhs.val; >> + return *this; >> + } >> + >> + SelfAssignChecker operator=(SelfAssignChecker&& rhs) >> + { >> + if (this == &rhs) >> + ++moveCounter; >> + this->val = rhs.val; >> + return *this; >> + } >> + >> + int val; >> + >> + friend bool operator==(SelfAssignChecker, SelfAssignChecker) = default; >> +}; >> + >> +int SelfAssignChecker::moveCounter = 0; >> +int SelfAssignChecker::copyCounter = 0; >> + >> +void >> +test_pr121313() >> +{ >> + using namespace __gnu_test; >> + >> + SelfAssignChecker::copyCounter = SelfAssignChecker::moveCounter = 0; >> + do_test<test_forward_range<int>, std::allocator<SelfAssignChecker>>(); >> + VERIFY( SelfAssignChecker::moveCounter == 0 ); >> + VERIFY( SelfAssignChecker::copyCounter == 0 ); >> + >> + SelfAssignChecker::copyCounter = SelfAssignChecker::moveCounter = 0; >> + do_test<test_input_range<int>, std::allocator<SelfAssignChecker>>(); >> + VERIFY( SelfAssignChecker::moveCounter == 0 ); >> + VERIFY( SelfAssignChecker::copyCounter == 0 ); >> +} >> + >> int main() >> { >> test_ranges(); >> + test_pr121313(); >> static_assert( test_ranges() ); >> } >> -- >> 2.49.0 >> >>