]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
libstdc++: Fix std::string construction from volatile char* [PR119748]
authorJonathan Wakely <jwakely@redhat.com>
Fri, 11 Apr 2025 21:13:14 +0000 (22:13 +0100)
committerJonathan Wakely <redi@gcc.gnu.org>
Tue, 15 Apr 2025 08:24:58 +0000 (09:24 +0100)
My recent r15-9381-g648d5c26e25497 change assumes that a contiguous
iterator with the correct value_type can be converted to a const charT*
but that's not true for volatile charT*. The optimization should only be
done if it can be converted to the right pointer type.

Additionally, some generic loops for non-contiguous iterators need an
explicit cast to deal with iterator reference types that do not bind to
the const charT& parameter of traits_type::assign.

libstdc++-v3/ChangeLog:

PR libstdc++/119748
* include/bits/basic_string.h (_S_copy_chars): Only optimize for
contiguous iterators that are convertible to const charT*. Use
explicit conversion to charT after dereferencing iterator.
(_S_copy_range): Likewise for contiguous ranges.
* include/bits/basic_string.tcc (_M_construct): Use explicit
conversion to charT after dereferencing iterator.
* include/bits/cow_string.h (_S_copy_chars): Likewise.
(basic_string(from_range_t, R&&, const Allocator&)): Likewise.
Only optimize for contiguous iterators that are convertible to
const charT*.
* testsuite/21_strings/basic_string/cons/char/119748.cc: New
test.
* testsuite/21_strings/basic_string/cons/wchar_t/119748.cc:
New test.

Reviewed-by: Tomasz Kaminski <tkaminsk@redhat.com>
libstdc++-v3/include/bits/basic_string.h
libstdc++-v3/include/bits/basic_string.tcc
libstdc++-v3/include/bits/cow_string.h
libstdc++-v3/testsuite/21_strings/basic_string/cons/char/119748.cc [new file with mode: 0644]
libstdc++-v3/testsuite/21_strings/basic_string/cons/wchar_t/119748.cc [new file with mode: 0644]

index 9c431c765ab48d1c1e4dac1b48fbd307f5637faa..c90bd099b6391b85533c9140d74441f55ac248ef 100644 (file)
@@ -488,8 +488,11 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
                              is_same<_IterBase, const _CharT*>>::value)
            _S_copy(__p, std::__niter_base(__k1), __k2 - __k1);
 #if __cpp_lib_concepts
-         else if constexpr (contiguous_iterator<_Iterator>
-                              && is_same_v<iter_value_t<_Iterator>, _CharT>)
+         else if constexpr (requires {
+                              requires contiguous_iterator<_Iterator>;
+                              { std::to_address(__k1) }
+                                -> convertible_to<const _CharT*>;
+                            })
            {
              const auto __d = __k2 - __k1;
              (void) (__k1 + __d); // See P3349R1
@@ -499,7 +502,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
          else
 #endif
          for (; __k1 != __k2; ++__k1, (void)++__p)
-           traits_type::assign(*__p, *__k1); // These types are off.
+           traits_type::assign(*__p, static_cast<_CharT>(*__k1));
        }
 #pragma GCC diagnostic pop
 
@@ -527,12 +530,19 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
        static constexpr void
        _S_copy_range(pointer __p, _Rg&& __rg, size_type __n)
        {
-         if constexpr (ranges::contiguous_range<_Rg>
-                         && is_same_v<ranges::range_value_t<_Rg>, _CharT>)
+         if constexpr (requires {
+                         requires ranges::contiguous_range<_Rg>;
+                         { ranges::data(std::forward<_Rg>(__rg)) }
+                           -> convertible_to<const _CharT*>;
+                       })
            _S_copy(__p, ranges::data(std::forward<_Rg>(__rg)), __n);
          else
-           for (auto&& __e : __rg)
-             traits_type::assign(*__p++, std::forward<decltype(__e)>(__e));
+           {
+             auto __first = ranges::begin(__rg);
+             const auto __last = ranges::end(__rg);
+             for (; __first != __last; ++__first)
+               traits_type::assign(*__p++, static_cast<_CharT>(*__first));
+           }
        }
 #endif
 
index 02230aca5d273a76819d901bc844eb401f3028e5..bca55bc56581401f851a12070a83ebf2c3c36e28 100644 (file)
@@ -210,7 +210,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
                _M_data(__another);
                _M_capacity(__capacity);
              }
-           traits_type::assign(_M_data()[__len++], *__beg);
+           traits_type::assign(_M_data()[__len++],
+                               static_cast<_CharT>(*__beg));
            ++__beg;
          }
 
index b250397151bbe6e78a9b9775184efdad443c3094..f9df2be20bed292747f0b89e5634baa1d3b6f366 100644 (file)
@@ -423,7 +423,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        _S_copy_chars(_CharT* __p, _Iterator __k1, _Iterator __k2)
        {
          for (; __k1 != __k2; ++__k1, (void)++__p)
-           traits_type::assign(*__p, *__k1); // These types are off.
+           traits_type::assign(*__p, static_cast<_CharT>(*__k1));
        }
 
       static void
@@ -656,12 +656,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
              reserve(__n);
              pointer __p = _M_data();
-             if constexpr (ranges::contiguous_range<_Rg>
-                             && is_same_v<ranges::range_value_t<_Rg>, _CharT>)
+             if constexpr (requires {
+                             requires ranges::contiguous_range<_Rg>;
+                             { ranges::data(std::forward<_Rg>(__rg)) }
+                               -> convertible_to<const _CharT*>;
+                           })
                _M_copy(__p, ranges::data(std::forward<_Rg>(__rg)), __n);
              else
-               for (auto&& __e : __rg)
-                 traits_type::assign(*__p++, std::forward<decltype(__e)>(__e));
+               {
+                 auto __first = ranges::begin(__rg);
+                 const auto __last = ranges::end(__rg);
+                 for (; __first != __last; ++__first)
+                   traits_type::assign(*__p++, static_cast<_CharT>(*__first));
+               }
              _M_rep()->_M_set_length_and_sharable(__n);
            }
          else
diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/cons/char/119748.cc b/libstdc++-v3/testsuite/21_strings/basic_string/cons/char/119748.cc
new file mode 100644 (file)
index 0000000..301ca5d
--- /dev/null
@@ -0,0 +1,35 @@
+// { dg-do compile }
+
+// Bug 119748
+// string(InputIterator, InputIterator) rejects volatile charT* as iterator
+
+#ifndef TEST_CHAR_TYPE
+#define TEST_CHAR_TYPE char
+#endif
+
+#include <string>
+#include <testsuite_iterators.h>
+
+typedef TEST_CHAR_TYPE C;
+
+volatile C vs[42] = {};
+std::basic_string<C> s(vs+0, vs+42);
+#ifdef __cpp_lib_containers_ranges
+std::basic_string<C> s2(std::from_range, vs);
+#endif
+
+using namespace __gnu_test;
+
+test_container<volatile C, input_iterator_wrapper> input_cont(vs);
+std::basic_string<C> s3(input_cont.begin(), input_cont.end());
+
+test_container<volatile C, forward_iterator_wrapper> fwd_cont(vs);
+std::basic_string<C> s4(fwd_cont.begin(), fwd_cont.end());
+
+#ifdef __cpp_lib_containers_ranges
+test_input_range<volatile C> input_range(vs);
+std::basic_string<C> s5(std::from_range, input_range);
+
+test_forward_range<volatile C> fwd_range(vs);
+std::basic_string<C> s6(std::from_range, fwd_range);
+#endif
diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/cons/wchar_t/119748.cc b/libstdc++-v3/testsuite/21_strings/basic_string/cons/wchar_t/119748.cc
new file mode 100644 (file)
index 0000000..7d3ba10
--- /dev/null
@@ -0,0 +1,7 @@
+// { dg-do compile }
+
+// Bug 119748
+// string(InputIterator, InputIterator) rejects volatile charT* as iterator
+
+#define TEST_CHAR_TYPE wchar_t
+#include "../char/119748.cc"