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, the generic loop for non-contiguous iterators needs an
explicit cast to deal with iterators that have a reference type that is
not explicitly convertible to charT.
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 input 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.
---
Changes in v2:
- Added static_assert in _M_construct overload for input iterators.
- Added tests using non-contiguous iterators.
libstdc++-v3/include/bits/basic_string.h | 24 +++++---
libstdc++-v3/include/bits/basic_string.tcc | 3 +-
libstdc++-v3/include/bits/cow_string.h | 17 ++++--
.../basic_string/cons/char/119748.cc | 55 +++++++++++++++++++
.../basic_string/cons/wchar_t/119748.cc | 7 +++
5 files changed, 93 insertions(+), 13 deletions(-)
create mode 100644
libstdc++-v3/testsuite/21_strings/basic_string/cons/char/119748.cc
create mode 100644
libstdc++-v3/testsuite/21_strings/basic_string/cons/wchar_t/119748.cc
diff --git a/libstdc++-v3/include/bits/basic_string.h
b/libstdc++-v3/include/bits/basic_string.h
index 9c431c765ab..c90bd099b63 100644
--- a/libstdc++-v3/include/bits/basic_string.h
+++ b/libstdc++-v3/include/bits/basic_string.h
@@ -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
diff --git a/libstdc++-v3/include/bits/basic_string.tcc
b/libstdc++-v3/include/bits/basic_string.tcc
index 02230aca5d2..bca55bc5658 100644
--- a/libstdc++-v3/include/bits/basic_string.tcc
+++ b/libstdc++-v3/include/bits/basic_string.tcc
@@ -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;
}
diff --git a/libstdc++-v3/include/bits/cow_string.h
b/libstdc++-v3/include/bits/cow_string.h
index b250397151b..f9df2be20be 100644
--- a/libstdc++-v3/include/bits/cow_string.h
+++ b/libstdc++-v3/include/bits/cow_string.h
@@ -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
index 00000000000..c8be6f4ec0a
--- /dev/null
+++ b/libstdc++-v3/testsuite/21_strings/basic_string/cons/char/119748.cc
@@ -0,0 +1,55 @@
+// { 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>
+
+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
+
+template<class Cat>
+struct Iterator
+{
+ typedef C value_type;
+ typedef volatile C& reference;
+ typedef volatile C* pointer;
+ typedef long difference_type;
+ typedef Cat iterator_category;
+
+ reference operator*() const { return const_cast<reference>(*c); };
+
+ Iterator& operator++() { ++c; return *this; }
+ Iterator operator++(int) { Iterator i = { c++ }; return i; }
+
+ bool operator==(const Iterator& i) const { return c == i.c; }
+ bool operator!=(const Iterator& i) const { return !(*this == i); }
+
+ C* c;
+};
+
+typedef Iterator<std::input_iterator_tag> InputIterator;
+C chars[] = { C('a'), C('b'), C('c') };
+InputIterator first = { chars + 0 };
+InputIterator last = { chars + 3 };
+std::basic_string<C> s3(first, last);
+#ifdef __cpp_lib_containers_ranges
+std::basic_string<C> s4(std::from_range, std::ranges::subrange(first, last));
+#endif
+
+typedef Iterator<std::forward_iterator_tag> ForwardIterator;
+ForwardIterator firstf = { chars+0 };
+ForwardIterator lastf = { chars + 3 };
+std::basic_string<C> s5(firstf, lastf);
+#ifdef __cpp_lib_containers_ranges
+std::basic_string<C> s6(std::from_range, std::ranges::subrange(firstf, lastf));
+#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
index 00000000000..7d3ba10d2d4
--- /dev/null
+++ b/libstdc++-v3/testsuite/21_strings/basic_string/cons/wchar_t/119748.cc
@@ -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"
--
2.49.0