Skip to content

Commit 80097a1

Browse files
[libc++] Fix input-only range handling for basic_string (#116890)
By calling `std::move` for related functions when the iterator is possibly input-only. Also slightly changes the conditions of branch for contiguous iterators to avoid error. Fixes #116502
1 parent 2d317d9 commit 80097a1

File tree

3 files changed

+50
-5
lines changed

3 files changed

+50
-5
lines changed

libcxx/include/string

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1919,7 +1919,8 @@ private:
19191919
__copy_non_overlapping_range(_ForwardIter __first, _Sent __last, value_type* __dest) {
19201920
# ifndef _LIBCPP_CXX03_LANG
19211921
if constexpr (__libcpp_is_contiguous_iterator<_ForwardIter>::value &&
1922-
is_same<value_type, __iter_value_type<_ForwardIter>>::value && is_same<_ForwardIter, _Sent>::value) {
1922+
is_same<value_type, __remove_cvref_t<decltype(*__first)>>::value &&
1923+
is_same<_ForwardIter, _Sent>::value) {
19231924
_LIBCPP_ASSERT_INTERNAL(
19241925
!std::__is_overlapping_range(std::__to_address(__first), std::__to_address(__last), __dest),
19251926
"__copy_non_overlapping_range called with an overlapping range!");
@@ -1952,7 +1953,7 @@ private:
19521953
__sz += __n;
19531954
__set_size(__sz);
19541955
traits_type::assign(__p[__sz], value_type());
1955-
__copy_non_overlapping_range(__first, __last, __p + __ip);
1956+
__copy_non_overlapping_range(std::move(__first), std::move(__last), __p + __ip);
19561957

19571958
return begin() + __ip;
19581959
}
@@ -2488,7 +2489,7 @@ basic_string<_CharT, _Traits, _Allocator>::__init_with_size(_InputIterator __fir
24882489
# if _LIBCPP_HAS_EXCEPTIONS
24892490
try {
24902491
# endif // _LIBCPP_HAS_EXCEPTIONS
2491-
auto __end = __copy_non_overlapping_range(__first, __last, std::__to_address(__p));
2492+
auto __end = __copy_non_overlapping_range(std::move(__first), std::move(__last), std::__to_address(__p));
24922493
traits_type::assign(*__end, value_type());
24932494
# if _LIBCPP_HAS_EXCEPTIONS
24942495
} catch (...) {
@@ -3080,9 +3081,9 @@ basic_string<_CharT, _Traits, _Allocator>::__insert_with_size(
30803081
return begin() + __ip;
30813082

30823083
if (__string_is_trivial_iterator<_Iterator>::value && !__addr_in_range(*__first)) {
3083-
return __insert_from_safe_copy(__n, __ip, __first, __last);
3084+
return __insert_from_safe_copy(__n, __ip, std::move(__first), std::move(__last));
30843085
} else {
3085-
const basic_string __temp(__init_with_sentinel_tag(), __first, __last, __alloc_);
3086+
const basic_string __temp(__init_with_sentinel_tag(), std::move(__first), std::move(__last), __alloc_);
30863087
return __insert_from_safe_copy(__n, __ip, __temp.begin(), __temp.end());
30873088
}
30883089
}

libcxx/test/std/strings/basic.string/string.cons/from_range.pass.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
// constexpr basic_string(from_range_t, R&& rg, const Allocator& a = Allocator()); // since C++23
1313

1414
#include <algorithm>
15+
#include <sstream>
1516
#include <string>
1617
#include <utility>
1718
#include <vector>
@@ -82,6 +83,13 @@ constexpr void test_with_input(std::vector<char> input) {
8283
assert(std::ranges::equal(input, c));
8384
LIBCPP_ASSERT(is_string_asan_correct(c));
8485
}
86+
87+
{ // Ensure input-only sized ranges are accepted.
88+
using input_iter = cpp20_input_iterator<const char*>;
89+
const char in[]{'q', 'w', 'e', 'r'};
90+
std::string s(std::from_range, std::views::counted(input_iter{std::ranges::begin(in)}, std::ranges::ssize(in)));
91+
assert(s == "qwer");
92+
}
8593
}
8694

8795
void test_string_exception_safety_throwing_allocator() {
@@ -116,6 +124,15 @@ constexpr bool test_inputs() {
116124
return true;
117125
}
118126

127+
#ifndef TEST_HAS_NO_LOCALIZATION
128+
void test_counted_istream_view() {
129+
std::istringstream is{"qwert"};
130+
auto vals = std::views::istream<char>(is);
131+
std::string s(std::from_range, std::views::counted(vals.begin(), 3));
132+
assert(s == "qwe");
133+
}
134+
#endif
135+
119136
int main(int, char**) {
120137
test_inputs();
121138
static_assert(test_inputs());
@@ -125,5 +142,9 @@ int main(int, char**) {
125142
// Note: `test_exception_safety_throwing_copy` doesn't apply because copying a `char` cannot throw.
126143
test_string_exception_safety_throwing_allocator();
127144

145+
#ifndef TEST_HAS_NO_LOCALIZATION
146+
test_counted_istream_view();
147+
#endif
148+
128149
return 0;
129150
}

libcxx/test/std/strings/basic.string/string.modifiers/string_insert/insert_range.pass.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
// template<container-compatible-range<charT> R>
1212
// constexpr iterator insert_range(const_iterator p, R&& rg); // C++23
1313

14+
#include <sstream>
1415
#include <string>
1516

1617
#include "../../../../containers/sequences/insert_range_sequence_containers.h"
@@ -27,9 +28,27 @@ constexpr bool test_constexpr() {
2728
[]([[maybe_unused]] auto&& c) { LIBCPP_ASSERT(c.__invariants()); });
2829
});
2930

31+
{ // Ensure input-only sized ranges are accepted.
32+
using input_iter = cpp20_input_iterator<const char*>;
33+
const char in[]{'q', 'w', 'e', 'r'};
34+
std::string s = "zxcv";
35+
s.insert_range(s.begin(), std::views::counted(input_iter{std::ranges::begin(in)}, std::ranges::ssize(in)));
36+
assert(s == "qwerzxcv");
37+
}
38+
3039
return true;
3140
}
3241

42+
#ifndef TEST_HAS_NO_LOCALIZATION
43+
void test_counted_istream_view() {
44+
std::istringstream is{"qwert"};
45+
auto vals = std::views::istream<char>(is);
46+
std::string s = "zxcv";
47+
s.insert_range(s.begin(), std::views::counted(vals.begin(), 3));
48+
assert(s == "qwezxcv");
49+
}
50+
#endif
51+
3352
int main(int, char**) {
3453
static_assert(test_constraints_insert_range<std::basic_string, char, int>());
3554

@@ -39,6 +58,10 @@ int main(int, char**) {
3958
});
4059
static_assert(test_constexpr());
4160

61+
#ifndef TEST_HAS_NO_LOCALIZATION
62+
test_counted_istream_view();
63+
#endif
64+
4265
// Note: `test_insert_range_exception_safety_throwing_copy` doesn't apply because copying chars cannot throw.
4366
{
4467
#if !defined(TEST_HAS_NO_EXCEPTIONS)

0 commit comments

Comments
 (0)