[PATCH][_GLIBCXX_DEBUG] Fix COW basic_string checks

François Dumont frs.dumont@gmail.com
Mon Mar 30 20:33:36 GMT 2026


In this case your approach is indeed the simplest fix to do.

     libstdc++: [_GLIBCXX_DEBUG] Fix COW string valid range check

     In revision 698a6af5dcbae5d935bcda8a461dea8458c658dc the 
_GLIBCXX_DEBUG code
     for the Library Defect 438 has been removed for C++11 and after.

     But the COW basic_string implementation used when 
_GLIBCXX_USE_CXX11_ABI=0 is
     missing the _RequireInputIter constraint on a number method 
resulting in test
     failures.

     For the moment move the culprit __glibcxx_requires_valid_range call 
in a method
     where the iterator type has already been checked.

     libstdc++-v3/ChangeLog:

             * include/bits/cow_string.h
             (basic_string::replace(iterator, iterator, _InputIte, 
_InputIte)): Move
             __glibcxx_requires_valid_range to...
             (basic_string::_M_replace_dispatch(iterator, iterator, 
_InputIte,
             _InputIte, __fase_type)): ...here.
             * testsuite/21_strings/basic_string/debug/append_neg.cc: 
New test case.
             * testsuite/21_strings/basic_string/debug/assign_neg.cc: 
New test case.
             * testsuite/21_strings/basic_string/debug/construct_neg.cc: 
New test case.
             * testsuite/21_strings/basic_string/debug/insert_neg.cc: 
New test case.
             * testsuite/21_strings/basic_string/debug/replace_neg.cc: 
New test case.

Tested under Linux x86_64 _GLIBCXX_DEBUG mode C++98 and C++11.

Ok to commit ?

François


On 3/30/26 19:13, Jonathan Wakely wrote:
> On Thu, 26 Mar 2026 at 21:46, François Dumont <frs.dumont@gmail.com> wrote:
>> Hi
>>
>> Here is the fix to the current COW basic_string issue now that I've
>> removed the DR438 _GLIBCXX_DEBUG code.
>>
>> I've also revisited the location of the debug checks so that for example
>> on the append_neg.cc test case we have this assertion message with the
>> cxx11 string:
>>
>> In function:
>>       constexpr std::cxx11::basic_string<_CharT, _Traits, _Alloc>& std::
>>       cxx11::basic_string<_CharT, _Traits, _Alloc>::replace(const_iterator,
>>       const_iterator, _InputIterator, _InputIterator) [with _InputIterator =
>>
>> so refering to the replace method. Whereas on the cow string:
>>
>> In function:
>>       std::basic_string<_CharT, _Traits, _Alloc>& std::basic_string<_CharT,
>>       _Traits, _Alloc>::append(_InputIterator, _InputIterator) [with
>>       _InputIterator = gnu_debug::_Safe_iterator<gnu_cxx::
>>
>> I plan to do something similar on cxx11 string. Let me know if useless.
>>
>>       libstdc++: [_GLIBCXX_DEBUG] Fix COW basic_string checks
>>
>>       In revision 698a6af5dcbae5d935bcda8a461dea8458c658dc the
>> _GLIBCXX_DEBUG code
>>       for the Library Defect 438 has been removed for C++11 and after.
>> But the COW
>>       basic_string implementation used when _GLIBCXX_USE_CXX11_ABI=0 was
>> missing
>>       the _RequireInputIter constraint on a number method resulting in
>> test failures.
>>
>>       _RequireInputIter is now added where necessary. And _GLIBCXX_DEBUG
>> checks have
>>       been added at the right place to benefit from more accurate
>> assertion messages.
>>
>>       libstdc++-v3/ChangeLog:
>>
>>               * include/bits/cow_string.h [__cplusplus >= 201103L]
>>               (basic_string(_InputIte, _InputIte, const _Alloc&)): Add
>> _RequireInputIter
>>               template parameter. Add
>> __glibcxx_requires_valid_constructor_range call.
>>               (_M_replace_dispatch(_InputIte, _InputIte): New.
>>               (_M_replace_dispatch(iterator, iterator, _InputIte,
>> _InputIte): New.
>>               [__cplusplus >= 201103L]
>>               (append(_InputIte, _InputIte)): Add _RequireInputIter
>> template parameter.
>>               Add __glibcxx_requires_valid_constructor_range call. Use
>> latter.
>>               (insert(iterator, _InputIte, _InputIte)): Likewise.
>>               (replace(iterator, iterator, _InputIte, _InputIte)): Likewise.
>>               (_S_construct(_InputIte, _InputIte, const _Alloc&)):  Add
>> _RequireInputIter
>>               template parameter.
>>               * include/debug/debug.h
>> (__glibcxx_requires_valid_construtor_range): New.
>>               * testsuite/21_strings/basic_string/debug/append_neg.cc:
>> New test case.
>>               * testsuite/21_strings/basic_string/debug/assign_neg.cc:
>> New test case.
>>               * testsuite/21_strings/basic_string/debug/construct_neg.cc:
>> New test case.
>>               * testsuite/21_strings/basic_string/debug/insert_neg.cc:
>> New test case.
>>               * testsuite/21_strings/basic_string/debug/replace_neg.cc:
>> New test case.
>>
>> Tested under Linux x64_86.
>>
>> Ok to commit ?
>
> This seems like a large change to stable std::string code, which is
> inappropriate when we're just a few weeks from releasing GCC 16.
>
> I would prefer to just move the assertion macro to
> _M_replace_dispatch, or only check the assertions for !_Integral, as
> in the patches I showed.
>
> For GCC 17 we can revisit this and apply your fixes.
-------------- next part --------------
diff --git a/libstdc++-v3/include/bits/cow_string.h b/libstdc++-v3/include/bits/cow_string.h
index 6cf00224372..df71185e556 100644
--- a/libstdc++-v3/include/bits/cow_string.h
+++ b/libstdc++-v3/include/bits/cow_string.h
@@ -2124,7 +2124,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	{
 	  _GLIBCXX_DEBUG_PEDASSERT(_M_ibegin() <= __i1 && __i1 <= __i2
 				   && __i2 <= _M_iend());
-	  __glibcxx_requires_valid_range(__k1, __k2);
 	  typedef typename std::__is_integer<_InputIterator>::__type _Integral;
 	  return _M_replace_dispatch(__i1, __i2, __k1, __k2, _Integral());
 	}
@@ -3833,6 +3832,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _M_replace_dispatch(iterator __i1, iterator __i2, _InputIterator __k1,
 			  _InputIterator __k2, __false_type)
       {
+	__glibcxx_requires_valid_range(__k1, __k2);
 	const basic_string __s(__k1, __k2);
 	const size_type __n1 = __i2 - __i1;
 	_M_check_length(__n1, __s.size(), "basic_string::_M_replace_dispatch");
diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/debug/append_neg.cc b/libstdc++-v3/testsuite/21_strings/basic_string/debug/append_neg.cc
new file mode 100644
index 00000000000..befe83a84f3
--- /dev/null
+++ b/libstdc++-v3/testsuite/21_strings/basic_string/debug/append_neg.cc
@@ -0,0 +1,19 @@
+// { dg-do run { xfail *-*-* } }
+// { dg-require-debug-mode "" }
+
+#include <vector>
+#include <string>
+
+void test01()
+{
+  std::vector<char> v1(10, 'a');
+  std::string s2;
+
+  s2.append(v1.begin() + 7, v1.begin() + 2);
+}
+
+int main()
+{
+  test01();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/debug/assign_neg.cc b/libstdc++-v3/testsuite/21_strings/basic_string/debug/assign_neg.cc
new file mode 100644
index 00000000000..5f32c170f50
--- /dev/null
+++ b/libstdc++-v3/testsuite/21_strings/basic_string/debug/assign_neg.cc
@@ -0,0 +1,19 @@
+// { dg-do run { xfail *-*-* } }
+// { dg-require-debug-mode "" }
+
+#include <vector>
+#include <string>
+
+void test01()
+{
+  std::vector<char> v1(10, 'a');
+  std::string s2;
+
+  s2.assign(v1.begin() + 7, v1.begin() + 2);
+}
+
+int main()
+{
+  test01();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/debug/construct_neg.cc b/libstdc++-v3/testsuite/21_strings/basic_string/debug/construct_neg.cc
new file mode 100644
index 00000000000..1ac933d4309
--- /dev/null
+++ b/libstdc++-v3/testsuite/21_strings/basic_string/debug/construct_neg.cc
@@ -0,0 +1,17 @@
+// { dg-do run { xfail *-*-* } }
+// { dg-require-debug-mode "" }
+
+#include <vector>
+#include <string>
+
+void test01()
+{
+  std::vector<char> v1(10, 'a');
+  std::string s2(v1.begin() + 7, v1.begin() + 2);
+}
+
+int main()
+{
+  test01();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/debug/insert_neg.cc b/libstdc++-v3/testsuite/21_strings/basic_string/debug/insert_neg.cc
new file mode 100644
index 00000000000..95530ef2a51
--- /dev/null
+++ b/libstdc++-v3/testsuite/21_strings/basic_string/debug/insert_neg.cc
@@ -0,0 +1,19 @@
+// { dg-do run { xfail *-*-* } }
+// { dg-require-debug-mode "" }
+
+#include <vector>
+#include <string>
+
+void test01()
+{
+  std::vector<char> v1(10, 'a');
+  std::string s2;
+
+  s2.insert(s2.begin(), v1.begin() + 7, v1.begin() + 2);
+}
+
+int main()
+{
+  test01();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/21_strings/basic_string/debug/replace_neg.cc b/libstdc++-v3/testsuite/21_strings/basic_string/debug/replace_neg.cc
new file mode 100644
index 00000000000..fd3df9e8539
--- /dev/null
+++ b/libstdc++-v3/testsuite/21_strings/basic_string/debug/replace_neg.cc
@@ -0,0 +1,20 @@
+// { dg-do run { xfail *-*-* } }
+// { dg-require-debug-mode "" }
+
+#include <vector>
+#include <string>
+
+void test01()
+{
+  std::vector<char> v1(10, 'a');
+  std::string s2 = "bbbbbbbbbb";
+
+  s2.replace(s2.begin(), s2.begin() + 5,
+	     v1.begin() + 7, v1.begin() + 2);
+}
+
+int main()
+{
+  test01();
+  return 0;
+}


More information about the Libstdc++ mailing list