Skip to content

[libc++] Fix input-only range handling for basic_string #116890

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions libcxx/include/string
Original file line number Diff line number Diff line change
Expand Up @@ -1919,7 +1919,8 @@ private:
__copy_non_overlapping_range(_ForwardIter __first, _Sent __last, value_type* __dest) {
# ifndef _LIBCPP_CXX03_LANG
if constexpr (__libcpp_is_contiguous_iterator<_ForwardIter>::value &&
is_same<value_type, __iter_value_type<_ForwardIter>>::value && is_same<_ForwardIter, _Sent>::value) {
is_same<value_type, __remove_cvref_t<decltype(*__first)>>::value &&
is_same<_ForwardIter, _Sent>::value) {
_LIBCPP_ASSERT_INTERNAL(
!std::__is_overlapping_range(std::__to_address(__first), std::__to_address(__last), __dest),
"__copy_non_overlapping_range called with an overlapping range!");
Expand Down Expand Up @@ -1952,7 +1953,7 @@ private:
__sz += __n;
__set_size(__sz);
traits_type::assign(__p[__sz], value_type());
__copy_non_overlapping_range(__first, __last, __p + __ip);
__copy_non_overlapping_range(std::move(__first), std::move(__last), __p + __ip);

return begin() + __ip;
}
Expand Down Expand Up @@ -2488,7 +2489,7 @@ basic_string<_CharT, _Traits, _Allocator>::__init_with_size(_InputIterator __fir
# if _LIBCPP_HAS_EXCEPTIONS
try {
# endif // _LIBCPP_HAS_EXCEPTIONS
auto __end = __copy_non_overlapping_range(__first, __last, std::__to_address(__p));
auto __end = __copy_non_overlapping_range(std::move(__first), std::move(__last), std::__to_address(__p));
traits_type::assign(*__end, value_type());
# if _LIBCPP_HAS_EXCEPTIONS
} catch (...) {
Expand Down Expand Up @@ -3080,9 +3081,9 @@ basic_string<_CharT, _Traits, _Allocator>::__insert_with_size(
return begin() + __ip;

if (__string_is_trivial_iterator<_Iterator>::value && !__addr_in_range(*__first)) {
return __insert_from_safe_copy(__n, __ip, __first, __last);
return __insert_from_safe_copy(__n, __ip, std::move(__first), std::move(__last));
} else {
const basic_string __temp(__init_with_sentinel_tag(), __first, __last, __alloc_);
const basic_string __temp(__init_with_sentinel_tag(), std::move(__first), std::move(__last), __alloc_);
return __insert_from_safe_copy(__n, __ip, __temp.begin(), __temp.end());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// constexpr basic_string(from_range_t, R&& rg, const Allocator& a = Allocator()); // since C++23

#include <algorithm>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
Expand Down Expand Up @@ -82,6 +83,13 @@ constexpr void test_with_input(std::vector<char> input) {
assert(std::ranges::equal(input, c));
LIBCPP_ASSERT(is_string_asan_correct(c));
}

{ // Ensure input-only sized ranges are accepted.
using input_iter = cpp20_input_iterator<const char*>;
const char in[]{'q', 'w', 'e', 'r'};
std::string s(std::from_range, std::views::counted(input_iter{std::ranges::begin(in)}, std::ranges::ssize(in)));
assert(s == "qwer");
}
}

void test_string_exception_safety_throwing_allocator() {
Expand Down Expand Up @@ -116,6 +124,15 @@ constexpr bool test_inputs() {
return true;
}

#ifndef TEST_HAS_NO_LOCALIZATION
void test_counted_istream_view() {
std::istringstream is{"qwert"};
auto vals = std::views::istream<char>(is);
std::string s(std::from_range, std::views::counted(vals.begin(), 3));
assert(s == "qwe");
}
#endif

int main(int, char**) {
test_inputs();
static_assert(test_inputs());
Expand All @@ -125,5 +142,9 @@ int main(int, char**) {
// Note: `test_exception_safety_throwing_copy` doesn't apply because copying a `char` cannot throw.
test_string_exception_safety_throwing_allocator();

#ifndef TEST_HAS_NO_LOCALIZATION
test_counted_istream_view();
#endif

return 0;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
// template<container-compatible-range<charT> R>
// constexpr iterator insert_range(const_iterator p, R&& rg); // C++23

#include <sstream>
#include <string>

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

{ // Ensure input-only sized ranges are accepted.
using input_iter = cpp20_input_iterator<const char*>;
const char in[]{'q', 'w', 'e', 'r'};
std::string s = "zxcv";
s.insert_range(s.begin(), std::views::counted(input_iter{std::ranges::begin(in)}, std::ranges::ssize(in)));
assert(s == "qwerzxcv");
}

return true;
}

#ifndef TEST_HAS_NO_LOCALIZATION
void test_counted_istream_view() {
std::istringstream is{"qwert"};
auto vals = std::views::istream<char>(is);
std::string s = "zxcv";
s.insert_range(s.begin(), std::views::counted(vals.begin(), 3));
assert(s == "qwezxcv");
}
#endif

int main(int, char**) {
static_assert(test_constraints_insert_range<std::basic_string, char, int>());

Expand All @@ -39,6 +58,10 @@ int main(int, char**) {
});
static_assert(test_constexpr());

#ifndef TEST_HAS_NO_LOCALIZATION
test_counted_istream_view();
#endif

// Note: `test_insert_range_exception_safety_throwing_copy` doesn't apply because copying chars cannot throw.
{
#if !defined(TEST_HAS_NO_EXCEPTIONS)
Expand Down
Loading