Skip to content

Commit 5bf1f03

Browse files
[libc++] Fix assignment in insertion into vector (#116001)
Changes: - Avoid direct assignment in iterator-pair `insert` overload and `insert_range`, except when the assignment is move assignment.
1 parent d654d37 commit 5bf1f03

File tree

3 files changed

+63
-9
lines changed

3 files changed

+63
-9
lines changed

libcxx/include/__vector/vector.h

+27-9
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
#include <__type_traits/is_same.h>
5959
#include <__type_traits/is_trivially_relocatable.h>
6060
#include <__type_traits/type_identity.h>
61+
#include <__utility/declval.h>
6162
#include <__utility/exception_guard.h>
6263
#include <__utility/forward.h>
6364
#include <__utility/is_pointer_in_range.h>
@@ -602,6 +603,30 @@ class _LIBCPP_TEMPLATE_VIS vector {
602603
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void
603604
__assign_with_size(_Iterator __first, _Sentinel __last, difference_type __n);
604605

606+
template <class _Iterator,
607+
__enable_if_t<!is_same<decltype(*std::declval<_Iterator&>())&&, value_type&&>::value, int> = 0>
608+
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void
609+
__insert_assign_n_unchecked(_Iterator __first, difference_type __n, pointer __position) {
610+
for (pointer __end_position = __position + __n; __position != __end_position; ++__position, (void)++__first) {
611+
__temp_value<value_type, _Allocator> __tmp(this->__alloc_, *__first);
612+
*__position = std::move(__tmp.get());
613+
}
614+
}
615+
616+
template <class _Iterator,
617+
__enable_if_t<is_same<decltype(*std::declval<_Iterator&>())&&, value_type&&>::value, int> = 0>
618+
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void
619+
__insert_assign_n_unchecked(_Iterator __first, difference_type __n, pointer __position) {
620+
#if _LIBCPP_STD_VER >= 23
621+
if constexpr (!forward_iterator<_Iterator>) { // Handles input-only sized ranges for insert_range
622+
ranges::copy_n(std::move(__first), __n, __position);
623+
} else
624+
#endif
625+
{
626+
std::copy_n(__first, __n, __position);
627+
}
628+
}
629+
605630
template <class _InputIterator, class _Sentinel>
606631
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator
607632
__insert_with_sentinel(const_iterator __position, _InputIterator __first, _Sentinel __last);
@@ -1322,19 +1347,12 @@ vector<_Tp, _Allocator>::__insert_with_size(
13221347
__construct_at_end(__m, __last, __n - __dx);
13231348
if (__dx > 0) {
13241349
__move_range(__p, __old_last, __p + __n);
1325-
std::copy(__first, __m, __p);
1350+
__insert_assign_n_unchecked(__first, __dx, __p);
13261351
}
13271352
}
13281353
} else {
13291354
__move_range(__p, __old_last, __p + __n);
1330-
#if _LIBCPP_STD_VER >= 23
1331-
if constexpr (!forward_iterator<_Iterator>) {
1332-
ranges::copy_n(std::move(__first), __n, __p);
1333-
} else
1334-
#endif
1335-
{
1336-
std::copy_n(__first, __n, __p);
1337-
}
1355+
__insert_assign_n_unchecked(std::move(__first), __n, __p);
13381356
}
13391357
} else {
13401358
__split_buffer<value_type, allocator_type&> __v(__recommend(size() + __n), __p - this->__begin_, this->__alloc_);

libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_iter_iter.pass.cpp

+20
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
// template <class Iter>
1212
// iterator insert(const_iterator position, Iter first, Iter last);
1313

14+
// XFAIL: FROZEN-CXX03-HEADERS-FIXME
15+
1416
#include <vector>
1517
#include <cassert>
1618
#include <cstddef>
@@ -161,6 +163,24 @@ TEST_CONSTEXPR_CXX20 bool tests() {
161163
for (; j < 105; ++j)
162164
assert(v[j] == 0);
163165
}
166+
{ // Ensure that iterator-pair insert() doesn't use unexpected assignment.
167+
struct Wrapper {
168+
TEST_CONSTEXPR Wrapper(int n) : n_(n) {}
169+
170+
int n_;
171+
172+
private:
173+
void operator=(int);
174+
};
175+
176+
int a[] = {1, 2, 3, 4, 5};
177+
const std::size_t count = sizeof(a) / sizeof(a[0]);
178+
std::vector<Wrapper> v;
179+
v.insert(v.end(), a, a + count);
180+
assert(v.size() == count);
181+
for (std::size_t i = 0; i != count; ++i)
182+
assert(v[i].n_ == a[i]);
183+
}
164184
#if TEST_STD_VER >= 11
165185
{
166186
typedef std::vector<int, min_allocator<int> > V;

libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_range.pass.cpp

+16
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,22 @@ constexpr bool test() {
6464
}
6565
}
6666

67+
{ // Ensure that insert_range doesn't use unexpected assignment.
68+
struct Wrapper {
69+
constexpr Wrapper(int n) : n_(n) {}
70+
void operator=(int) = delete;
71+
72+
int n_;
73+
};
74+
75+
int a[]{1, 2, 3, 4, 5};
76+
std::vector<Wrapper> v;
77+
v.insert_range(v.end(), a);
78+
assert(v.size() == std::size(a));
79+
for (std::size_t i = 0; i != std::size(a); ++i)
80+
assert(v[i].n_ == a[i]);
81+
}
82+
6783
return true;
6884
}
6985

0 commit comments

Comments
 (0)