[libcxx-commits] [libcxx] [libc++] Fix input-only range handling for `basic_string`basic_string (PR #116890)

A. Jiang via libcxx-commits libcxx-commits at lists.llvm.org
Tue Nov 19 15:48:05 PST 2024


https://github.com/frederick-vs-ja created https://github.com/llvm/llvm-project/pull/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.

>From 94ec51af225087b5a0dd25a7161811b5d1ac6bbc Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Wed, 20 Nov 2024 07:47:01 +0800
Subject: [PATCH] [libc++] Fix input-only range handling for
 `basic_string`basic_string

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.
---
 libcxx/include/string                         | 11 +++++-----
 .../string.cons/from_range.pass.cpp           | 19 +++++++++++++++++
 .../string_insert/insert_range.pass.cpp       | 21 +++++++++++++++++++
 3 files changed, 46 insertions(+), 5 deletions(-)

diff --git a/libcxx/include/string b/libcxx/include/string
index bf7fc3c37ecd7a..97951fc9388074 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -1921,7 +1921,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!");
@@ -1954,7 +1955,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;
   }
@@ -2490,7 +2491,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 (...) {
@@ -3082,9 +3083,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());
   }
 }
diff --git a/libcxx/test/std/strings/basic.string/string.cons/from_range.pass.cpp b/libcxx/test/std/strings/basic.string/string.cons/from_range.pass.cpp
index 6a1b45b25ef032..bef8872c2bce25 100644
--- a/libcxx/test/std/strings/basic.string/string.cons/from_range.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.cons/from_range.pass.cpp
@@ -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>
@@ -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() {
@@ -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(v == "qwe");
+}
+#endif
+
 int main(int, char**) {
   test_inputs();
   static_assert(test_inputs());
@@ -125,5 +142,7 @@ 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();
 
+  test_counted_istream_view();
+
   return 0;
 }
diff --git a/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/insert_range.pass.cpp b/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/insert_range.pass.cpp
index 45d1f620e90542..415d78ac1b5223 100644
--- a/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/insert_range.pass.cpp
+++ b/libcxx/test/std/strings/basic.string/string.modifiers/string_insert/insert_range.pass.cpp
@@ -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"
@@ -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>());
 
@@ -39,6 +58,8 @@ int main(int, char**) {
   });
   static_assert(test_constexpr());
 
+  test_counted_istream_view();
+
   // Note: `test_insert_range_exception_safety_throwing_copy` doesn't apply because copying chars cannot throw.
   {
 #if !defined(TEST_HAS_NO_EXCEPTIONS)



More information about the libcxx-commits mailing list