Skip to content

[libc++] Fix assignment in insertion into vector #116001

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Mar 4, 2025
36 changes: 27 additions & 9 deletions libcxx/include/__vector/vector.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
#include <__type_traits/is_same.h>
#include <__type_traits/is_trivially_relocatable.h>
#include <__type_traits/type_identity.h>
#include <__utility/declval.h>
#include <__utility/exception_guard.h>
#include <__utility/forward.h>
#include <__utility/is_pointer_in_range.h>
Expand Down Expand Up @@ -602,6 +603,30 @@ class _LIBCPP_TEMPLATE_VIS vector {
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void
__assign_with_size(_Iterator __first, _Sentinel __last, difference_type __n);

template <class _Iterator,
__enable_if_t<!is_same<decltype(*std::declval<_Iterator&>())&&, value_type&&>::value, int> = 0>
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void
__insert_assign_n_unchecked(_Iterator __first, difference_type __n, pointer __position) {
for (pointer __end_position = __position + __n; __position != __end_position; ++__position, (void)++__first) {
__temp_value<value_type, _Allocator> __tmp(this->__alloc_, *__first);
*__position = std::move(__tmp.get());
}
}

template <class _Iterator,
__enable_if_t<is_same<decltype(*std::declval<_Iterator&>())&&, value_type&&>::value, int> = 0>
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void
__insert_assign_n_unchecked(_Iterator __first, difference_type __n, pointer __position) {
#if _LIBCPP_STD_VER >= 23
if constexpr (!forward_iterator<_Iterator>) { // Handles input-only sized ranges for insert_range
ranges::copy_n(std::move(__first), __n, __position);
} else
#endif
{
std::copy_n(__first, __n, __position);
}
}

template <class _InputIterator, class _Sentinel>
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator
__insert_with_sentinel(const_iterator __position, _InputIterator __first, _Sentinel __last);
Expand Down Expand Up @@ -1322,19 +1347,12 @@ vector<_Tp, _Allocator>::__insert_with_size(
__construct_at_end(__m, __last, __n - __dx);
if (__dx > 0) {
__move_range(__p, __old_last, __p + __n);
std::copy(__first, __m, __p);
__insert_assign_n_unchecked(__first, __dx, __p);
}
}
} else {
__move_range(__p, __old_last, __p + __n);
#if _LIBCPP_STD_VER >= 23
if constexpr (!forward_iterator<_Iterator>) {
ranges::copy_n(std::move(__first), __n, __p);
} else
#endif
{
std::copy_n(__first, __n, __p);
}
__insert_assign_n_unchecked(std::move(__first), __n, __p);
}
} else {
__split_buffer<value_type, allocator_type&> __v(__recommend(size() + __n), __p - this->__begin_, this->__alloc_);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
// template <class Iter>
// iterator insert(const_iterator position, Iter first, Iter last);

// XFAIL: FROZEN-CXX03-HEADERS-FIXME

#include <vector>
#include <cassert>
#include <cstddef>
Expand Down Expand Up @@ -161,6 +163,24 @@ TEST_CONSTEXPR_CXX20 bool tests() {
for (; j < 105; ++j)
assert(v[j] == 0);
}
{ // Ensure that iterator-pair insert() doesn't use unexpected assignment.
struct Wrapper {
TEST_CONSTEXPR Wrapper(int n) : n_(n) {}

int n_;

private:
void operator=(int);
};

int a[] = {1, 2, 3, 4, 5};
const std::size_t count = sizeof(a) / sizeof(a[0]);
std::vector<Wrapper> v;
v.insert(v.end(), a, a + count);
assert(v.size() == count);
for (std::size_t i = 0; i != count; ++i)
assert(v[i].n_ == a[i]);
}
#if TEST_STD_VER >= 11
{
typedef std::vector<int, min_allocator<int> > V;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,22 @@ constexpr bool test() {
}
}

{ // Ensure that insert_range doesn't use unexpected assignment.
struct Wrapper {
constexpr Wrapper(int n) : n_(n) {}
void operator=(int) = delete;

int n_;
};

int a[]{1, 2, 3, 4, 5};
std::vector<Wrapper> v;
v.insert_range(v.end(), a);
assert(v.size() == std::size(a));
for (std::size_t i = 0; i != std::size(a); ++i)
assert(v[i].n_ == a[i]);
}

return true;
}

Expand Down