Skip to content

Commit 426462c

Browse files
[libc++] Fix assignment in insertion into vector
Changes: - Avoid direct assignment in iterator-pair `insert` overload and `insert_range`, except when the assignment is move assignment.
1 parent 17bc738 commit 426462c

File tree

3 files changed

+79
-13
lines changed

3 files changed

+79
-13
lines changed

libcxx/include/__vector/vector.h

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <__algorithm/min.h>
1616
#include <__algorithm/move.h>
1717
#include <__algorithm/move_backward.h>
18+
#include <__algorithm/ranges_copy_n.h>
1819
#include <__algorithm/rotate.h>
1920
#include <__assert>
2021
#include <__config>
@@ -23,6 +24,7 @@
2324
#include <__fwd/vector.h>
2425
#include <__iterator/advance.h>
2526
#include <__iterator/bounded_iter.h>
27+
#include <__iterator/concepts.h>
2628
#include <__iterator/distance.h>
2729
#include <__iterator/iterator_traits.h>
2830
#include <__iterator/move_iterator.h>
@@ -54,6 +56,7 @@
5456
#include <__type_traits/is_same.h>
5557
#include <__type_traits/is_trivially_relocatable.h>
5658
#include <__type_traits/type_identity.h>
59+
#include <__utility/declval.h>
5760
#include <__utility/exception_guard.h>
5861
#include <__utility/forward.h>
5962
#include <__utility/is_pointer_in_range.h>
@@ -594,6 +597,30 @@ class _LIBCPP_TEMPLATE_VIS vector {
594597
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void
595598
__assign_with_size(_ForwardIterator __first, _Sentinel __last, difference_type __n);
596599

600+
template <class _Iterator,
601+
__enable_if_t<!is_same<decltype(*std::declval<_Iterator&>())&&, value_type&&>::value, int> = 0>
602+
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void
603+
__insert_assign_n_unchecked(_Iterator __first, difference_type __n, pointer __position) {
604+
for (pointer __end_position = __position + __n; __position != __end_position; (void)++__position, ++__first) {
605+
__temp_value<value_type, _Allocator> __tmp(this->__alloc(), *__first);
606+
*__position = std::move(__tmp.get());
607+
}
608+
}
609+
610+
template <class _Iterator,
611+
__enable_if_t<is_same<decltype(*std::declval<_Iterator&>())&&, value_type&&>::value, int> = 0>
612+
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void
613+
__insert_assign_n_unchecked(_Iterator __first, difference_type __n, pointer __position) {
614+
#if _LIBCPP_STD_VER >= 23
615+
if constexpr (!forward_iterator<_Iterator>) {
616+
ranges::copy_n(std::move(__first), __n, __position);
617+
} else
618+
#endif
619+
{
620+
std::copy_n(__first, __n, __position);
621+
}
622+
}
623+
597624
template <class _InputIterator, class _Sentinel>
598625
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI iterator
599626
__insert_with_sentinel(const_iterator __position, _InputIterator __first, _Sentinel __last);
@@ -1297,24 +1324,29 @@ template <class _Iterator, class _Sentinel>
12971324
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI typename vector<_Tp, _Allocator>::iterator
12981325
vector<_Tp, _Allocator>::__insert_with_size(
12991326
const_iterator __position, _Iterator __first, _Sentinel __last, difference_type __n) {
1300-
auto __insertion_size = __n;
1301-
pointer __p = this->__begin_ + (__position - begin());
1327+
pointer __p = this->__begin_ + (__position - begin());
13021328
if (__n > 0) {
13031329
if (__n <= this->__end_cap() - this->__end_) {
1304-
size_type __old_n = __n;
13051330
pointer __old_last = this->__end_;
1306-
_Iterator __m = std::next(__first, __n);
13071331
difference_type __dx = this->__end_ - __p;
13081332
if (__n > __dx) {
1309-
__m = __first;
1310-
difference_type __diff = this->__end_ - __p;
1311-
std::advance(__m, __diff);
1312-
__construct_at_end(__m, __last, __n - __diff);
1313-
__n = __dx;
1314-
}
1315-
if (__n > 0) {
1316-
__move_range(__p, __old_last, __p + __old_n);
1317-
std::copy(__first, __m, __p);
1333+
#if _LIBCPP_STD_VER >= 23
1334+
if constexpr (!forward_iterator<_Iterator>) {
1335+
__construct_at_end(std::move(__first), std::move(__last), __n);
1336+
std::rotate(__p, __old_last, this->__end_);
1337+
} else
1338+
#endif
1339+
{
1340+
_Iterator __m = std::next(__first, __dx);
1341+
__construct_at_end(__m, __last, __n - __dx);
1342+
if (__dx > 0) {
1343+
__move_range(__p, __old_last, __p + __n);
1344+
__insert_assign_n_unchecked(__first, __dx, __p);
1345+
}
1346+
}
1347+
} else {
1348+
__move_range(__p, __old_last, __p + __n);
1349+
__insert_assign_n_unchecked(std::move(__first), __n, __p);
13181350
}
13191351
} else {
13201352
allocator_type& __a = this->__alloc();

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,24 @@ TEST_CONSTEXPR_CXX20 bool tests()
138138
for (; j < 105; ++j)
139139
assert(v[j] == 0);
140140
}
141+
{
142+
struct Wrapper {
143+
TEST_CONSTEXPR Wrapper(int n) : n_(n) {}
144+
145+
int n_;
146+
147+
private:
148+
void operator=(int);
149+
};
150+
151+
int a[] = {1, 2, 3, 4, 5};
152+
const std::size_t count = sizeof(a) / sizeof(a[0]);
153+
std::vector<Wrapper> v;
154+
v.insert(v.end(), a, a + count);
155+
assert(v.size() == count);
156+
for (std::size_t i = 0; i != count; ++i)
157+
assert(v[i].n_ == a[i]);
158+
}
141159
#if TEST_STD_VER >= 11
142160
{
143161
typedef std::vector<int, min_allocator<int> > V;

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,22 @@ constexpr bool test() {
5555
}
5656
}
5757

58+
{ // Ensure that insert_range doesn't use unexpected assignment.
59+
struct Wrapper {
60+
constexpr Wrapper(int n) : n_(n) {}
61+
void operator=(int) = delete;
62+
63+
int n_;
64+
};
65+
66+
int a[]{1, 2, 3, 4, 5};
67+
std::vector<Wrapper> v;
68+
v.insert_range(v.end(), a);
69+
assert(v.size() == std::size(a));
70+
for (std::size_t i = 0; i != std::size(a); ++i)
71+
assert(v[i].n_ == a[i]);
72+
}
73+
5874
return true;
5975
}
6076

0 commit comments

Comments
 (0)