[libcxx-commits] [libcxx] [libc++] Fix assignment in insertion into `vector` (PR #116001)
A. Jiang via libcxx-commits
libcxx-commits at lists.llvm.org
Thu Nov 14 17:31:55 PST 2024
https://github.com/frederick-vs-ja updated https://github.com/llvm/llvm-project/pull/116001
>From 426462c3c54ea8050d0a78cb8592a53347a36e98 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Fri, 15 Nov 2024 09:31:33 +0800
Subject: [PATCH] [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.
---
libcxx/include/__vector/vector.h | 58 ++++++++++++++-----
.../insert_iter_iter_iter.pass.cpp | 18 ++++++
.../vector.modifiers/insert_range.pass.cpp | 16 +++++
3 files changed, 79 insertions(+), 13 deletions(-)
diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index d2d707d8c913c0..39befce1ac39bf 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -15,6 +15,7 @@
#include <__algorithm/min.h>
#include <__algorithm/move.h>
#include <__algorithm/move_backward.h>
+#include <__algorithm/ranges_copy_n.h>
#include <__algorithm/rotate.h>
#include <__assert>
#include <__config>
@@ -23,6 +24,7 @@
#include <__fwd/vector.h>
#include <__iterator/advance.h>
#include <__iterator/bounded_iter.h>
+#include <__iterator/concepts.h>
#include <__iterator/distance.h>
#include <__iterator/iterator_traits.h>
#include <__iterator/move_iterator.h>
@@ -54,6 +56,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>
@@ -594,6 +597,30 @@ class _LIBCPP_TEMPLATE_VIS vector {
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI void
__assign_with_size(_ForwardIterator __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; (void)++__position, ++__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>) {
+ 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);
@@ -1297,24 +1324,29 @@ template <class _Iterator, class _Sentinel>
_LIBCPP_CONSTEXPR_SINCE_CXX20 _LIBCPP_HIDE_FROM_ABI typename vector<_Tp, _Allocator>::iterator
vector<_Tp, _Allocator>::__insert_with_size(
const_iterator __position, _Iterator __first, _Sentinel __last, difference_type __n) {
- auto __insertion_size = __n;
- pointer __p = this->__begin_ + (__position - begin());
+ pointer __p = this->__begin_ + (__position - begin());
if (__n > 0) {
if (__n <= this->__end_cap() - this->__end_) {
- size_type __old_n = __n;
pointer __old_last = this->__end_;
- _Iterator __m = std::next(__first, __n);
difference_type __dx = this->__end_ - __p;
if (__n > __dx) {
- __m = __first;
- difference_type __diff = this->__end_ - __p;
- std::advance(__m, __diff);
- __construct_at_end(__m, __last, __n - __diff);
- __n = __dx;
- }
- if (__n > 0) {
- __move_range(__p, __old_last, __p + __old_n);
- std::copy(__first, __m, __p);
+#if _LIBCPP_STD_VER >= 23
+ if constexpr (!forward_iterator<_Iterator>) {
+ __construct_at_end(std::move(__first), std::move(__last), __n);
+ std::rotate(__p, __old_last, this->__end_);
+ } else
+#endif
+ {
+ _Iterator __m = std::next(__first, __dx);
+ __construct_at_end(__m, __last, __n - __dx);
+ if (__dx > 0) {
+ __move_range(__p, __old_last, __p + __n);
+ __insert_assign_n_unchecked(__first, __dx, __p);
+ }
+ }
+ } else {
+ __move_range(__p, __old_last, __p + __n);
+ __insert_assign_n_unchecked(std::move(__first), __n, __p);
}
} else {
allocator_type& __a = this->__alloc();
diff --git a/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_iter_iter.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_iter_iter.pass.cpp
index 934b85ce01c67b..4b7c47cc6388d9 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_iter_iter.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_iter_iter_iter.pass.cpp
@@ -138,6 +138,24 @@ TEST_CONSTEXPR_CXX20 bool tests()
for (; j < 105; ++j)
assert(v[j] == 0);
}
+ {
+ 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;
diff --git a/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_range.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_range.pass.cpp
index 0e26cb1546277b..d56493e0029f25 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_range.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_range.pass.cpp
@@ -55,6 +55,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;
}
More information about the libcxx-commits
mailing list