[libcxx-commits] [libcxx] [libc++] Fix {deque, vector}::append_range assuming too much about the types (PR #162438)
Nikolas Klauser via libcxx-commits
libcxx-commits at lists.llvm.org
Wed Oct 8 01:22:03 PDT 2025
https://github.com/philnik777 created https://github.com/llvm/llvm-project/pull/162438
Currently, `deque` and `vector`'s `append_range` is implemented in terms of `insert_range`. The problem with that is that `insert_range` has more preconditions, resulting in us rejecting valid code.
>From 1116f846c7681c8f5dd13b9edd5643f0028f0140 Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Wed, 8 Oct 2025 10:20:55 +0200
Subject: [PATCH] [libc++] Fix {deque,vector}::append_range assuming too much
about the types
---
libcxx/include/__vector/vector.h | 17 ++++++++-
libcxx/include/deque | 6 +++-
.../deque.modifiers/append_range.pass.cpp | 1 +
.../deque.modifiers/prepend_range.pass.cpp | 2 ++
.../prepend_range.pass.cpp | 1 +
.../insert_range_sequence_containers.h | 36 +++++++++++++++++++
.../list/list.modifiers/append_range.pass.cpp | 1 +
.../list.modifiers/prepend_range.pass.cpp | 1 +
.../vector.modifiers/append_range.pass.cpp | 1 +
9 files changed, 64 insertions(+), 2 deletions(-)
diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 316d3a9d10eff..247b4c2833e28 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -42,6 +42,7 @@
#include <__memory/temp_value.h>
#include <__memory/uninitialized_algorithms.h>
#include <__ranges/access.h>
+#include <__ranges/as_rvalue_view.h>
#include <__ranges/concepts.h>
#include <__ranges/container_compatible_range.h>
#include <__ranges/from_range.h>
@@ -489,7 +490,21 @@ class vector {
#if _LIBCPP_STD_VER >= 23
template <_ContainerCompatibleRange<_Tp> _Range>
_LIBCPP_HIDE_FROM_ABI constexpr void append_range(_Range&& __range) {
- insert_range(end(), std::forward<_Range>(__range));
+ if constexpr (ranges::forward_range<_Range> || ranges::sized_range<_Range>) {
+ auto __size = ranges::distance(__range);
+ if (__size < __cap_ - __end_) {
+ __construct_at_end(ranges::begin(__range), ranges::end(__range), __size);
+ } else {
+ __split_buffer<value_type, allocator_type&> __buffer(__recommend(size() + __size), size(), __alloc_);
+ __buffer.__construct_at_end_with_size(ranges::begin(__range), __size);
+ __swap_out_circular_buffer(__buffer);
+ }
+ } else {
+ vector __buffer(__alloc_);
+ for (auto&& __val : __range)
+ __buffer.emplace_back(std::forward<decltype(__val)>(__val));
+ append_range(ranges::as_rvalue_view(__buffer));
+ }
}
#endif
diff --git a/libcxx/include/deque b/libcxx/include/deque
index c8e1025eb5dd5..3638abc729091 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -807,7 +807,11 @@ public:
template <_ContainerCompatibleRange<_Tp> _Range>
_LIBCPP_HIDE_FROM_ABI void append_range(_Range&& __range) {
- insert_range(end(), std::forward<_Range>(__range));
+ if constexpr (ranges::forward_range<_Range> || ranges::sized_range<_Range>) {
+ __append_with_size(ranges::begin(__range), ranges::distance(__range));
+ } else {
+ __append_with_sentinel(ranges::begin(__range), ranges::end(__range));
+ }
}
# endif
diff --git a/libcxx/test/std/containers/sequences/deque/deque.modifiers/append_range.pass.cpp b/libcxx/test/std/containers/sequences/deque/deque.modifiers/append_range.pass.cpp
index 56a1d226db46f..752b0d898f477 100644
--- a/libcxx/test/std/containers/sequences/deque/deque.modifiers/append_range.pass.cpp
+++ b/libcxx/test/std/containers/sequences/deque/deque.modifiers/append_range.pass.cpp
@@ -31,6 +31,7 @@ int main(int, char**) {
});
});
test_sequence_append_range_move_only<std::deque>();
+ test_sequence_append_range_only_move_construct<std::deque>();
test_append_range_exception_safety_throwing_copy<std::deque>();
test_append_range_exception_safety_throwing_allocator<std::deque, int>();
diff --git a/libcxx/test/std/containers/sequences/deque/deque.modifiers/prepend_range.pass.cpp b/libcxx/test/std/containers/sequences/deque/deque.modifiers/prepend_range.pass.cpp
index 3154cd389d2f0..2b7d889c8945a 100644
--- a/libcxx/test/std/containers/sequences/deque/deque.modifiers/prepend_range.pass.cpp
+++ b/libcxx/test/std/containers/sequences/deque/deque.modifiers/prepend_range.pass.cpp
@@ -31,6 +31,8 @@ int main(int, char**) {
});
});
test_sequence_prepend_range_move_only<std::deque>();
+ // FIXME: This should work
+ // test_sequence_prepend_range_only_move_construct<std::deque>();
test_prepend_range_exception_safety_throwing_copy<std::deque>();
test_prepend_range_exception_safety_throwing_allocator<std::deque, int>();
diff --git a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/prepend_range.pass.cpp b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/prepend_range.pass.cpp
index c4b9cd9bdfc41..3aba10af458cc 100644
--- a/libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/prepend_range.pass.cpp
+++ b/libcxx/test/std/containers/sequences/forwardlist/forwardlist.modifiers/prepend_range.pass.cpp
@@ -30,6 +30,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
});
});
test_sequence_prepend_range_move_only<std::forward_list>();
+ test_sequence_prepend_range_only_move_construct<std::forward_list>();
if (!TEST_IS_CONSTANT_EVALUATED) {
test_prepend_range_exception_safety_throwing_copy<std::forward_list>();
diff --git a/libcxx/test/std/containers/sequences/insert_range_sequence_containers.h b/libcxx/test/std/containers/sequences/insert_range_sequence_containers.h
index 9f404c46df778..4212c43ed7af9 100644
--- a/libcxx/test/std/containers/sequences/insert_range_sequence_containers.h
+++ b/libcxx/test/std/containers/sequences/insert_range_sequence_containers.h
@@ -643,6 +643,42 @@ constexpr void test_sequence_assign_range_move_only() {
c.assign_range(in);
}
+struct InPlaceOnly {
+ InPlaceOnly(const InPlaceOnly&) = delete;
+ InPlaceOnly(InPlaceOnly&&) = delete;
+ InPlaceOnly& operator=(const InPlaceOnly&) = delete;
+ InPlaceOnly& operator=(InPlaceOnly&&) = delete;
+ constexpr InPlaceOnly() {}
+};
+
+struct MoveConstructOnly {
+ MoveConstructOnly(const MoveConstructOnly&) = delete;
+ MoveConstructOnly& operator=(const MoveConstructOnly&) = delete;
+ MoveConstructOnly& operator=(MoveConstructOnly&&) = delete;
+ constexpr MoveConstructOnly(MoveConstructOnly&&) {}
+ constexpr MoveConstructOnly(const InPlaceOnly&) {}
+};
+
+template <template <class...> class Container>
+constexpr void test_sequence_append_range_only_move_construct() {
+ InPlaceOnly input[5];
+ types::for_each(types::cpp20_input_iterator_list<InPlaceOnly*>{}, [&]<class Iter> {
+ std::ranges::subrange in(Iter(input), sentinel_wrapper<Iter>(Iter(input + 5)));
+ Container<MoveConstructOnly> c;
+ c.append_range(in);
+ });
+}
+
+template <template <class...> class Container>
+constexpr void test_sequence_prepend_range_only_move_construct() {
+ InPlaceOnly input[5];
+ types::for_each(types::cpp20_input_iterator_list<InPlaceOnly*>{}, [&]<class Iter> {
+ std::ranges::subrange in(Iter(input), sentinel_wrapper<Iter>(Iter(input + 5)));
+ Container<MoveConstructOnly> c;
+ c.prepend_range(in);
+ });
+}
+
// Exception safety.
template <template <class...> class Container>
diff --git a/libcxx/test/std/containers/sequences/list/list.modifiers/append_range.pass.cpp b/libcxx/test/std/containers/sequences/list/list.modifiers/append_range.pass.cpp
index 4b47a8738e525..8ef3eca86243d 100644
--- a/libcxx/test/std/containers/sequences/list/list.modifiers/append_range.pass.cpp
+++ b/libcxx/test/std/containers/sequences/list/list.modifiers/append_range.pass.cpp
@@ -31,6 +31,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
});
});
test_sequence_append_range_move_only<std::list>();
+ test_sequence_append_range_only_move_construct<std::list>();
if (!TEST_IS_CONSTANT_EVALUATED) {
test_append_range_exception_safety_throwing_copy<std::list>();
diff --git a/libcxx/test/std/containers/sequences/list/list.modifiers/prepend_range.pass.cpp b/libcxx/test/std/containers/sequences/list/list.modifiers/prepend_range.pass.cpp
index 41f7061c09d28..3d3a25fc448df 100644
--- a/libcxx/test/std/containers/sequences/list/list.modifiers/prepend_range.pass.cpp
+++ b/libcxx/test/std/containers/sequences/list/list.modifiers/prepend_range.pass.cpp
@@ -31,6 +31,7 @@ TEST_CONSTEXPR_CXX26 bool test() {
});
});
test_sequence_prepend_range_move_only<std::list>();
+ test_sequence_prepend_range_only_move_construct<std::list>();
if (!TEST_IS_CONSTANT_EVALUATED) {
test_prepend_range_exception_safety_throwing_copy<std::list>();
diff --git a/libcxx/test/std/containers/sequences/vector/vector.modifiers/append_range.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.modifiers/append_range.pass.cpp
index 7a5031e4676f2..c8f2a5ae009e8 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.modifiers/append_range.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.modifiers/append_range.pass.cpp
@@ -33,6 +33,7 @@ constexpr bool test() {
});
});
test_sequence_append_range_move_only<std::vector>();
+ test_sequence_append_range_only_move_construct<std::vector>();
{ // Vector may or may not need to reallocate because of the insertion -- make sure to test both cases.
{ // Ensure reallocation happens.
More information about the libcxx-commits
mailing list