[PATCH][_GLIBCXX_DEBUG] Enhance COW basic_string checks

François Dumont frs.dumont@gmail.com
Tue Jul 21 04:57:43 GMT 2026


Here is the new commit message:

     libstdc++: [_GLIBCXX_DEBUG] Improve COW basic_string checks

     Put the __glibcxx_requires_valid_range checks at public member level
     so that diagnostics are giving directly the culprit member name.

     Add the std::_RequireInputIter constraints on members taking template
     iterator range to avoid the "do the right thing" ambiguity.

     libstdc++v3/ChangeLog:

             * include/bits/cow_string.h
             (basic_string::_M_replace): New.
             (basic_string(_InputIterator, _InputIterator, const _Alloc&):
             Add std::_RequireInputIter constraint and
             __glibcxx_requires_valid_constructor_range check.
             (basic_string::append(_InputIte, _InputIte): Add 
std::_RequireInputIter
             constraint and __glibcxx_requires_valid_range check. Call 
_M_replace.
             (basic_string::assign(_InputIte, _InputIte): Likewise.
             (basic_string::insert(iterator, _InputIte, _InputIte)): 
Likewise.
             (basic_string::replace(iterator, iterator, _InputIte, 
_InputIte)): Likewise.
             [__cplusplus >= 201103L](basic_string::_S_construct): New.
             (basic_string::_M_replace_dispatch(iterator, iterator, 
_InputIte, _InputIte,
             __false_type)): Remove __glibcxx_requires_valid_range check.
             * include/debug/debug.h 
(__glibcxx_requires_valid_constructor_range): New.

On 7/6/26 22:42, Jonathan Wakely wrote:
> On Mon, 15 Jun 2026 at 07:07 +0200, François Dumont wrote:
>> Hi
>>
>>    libstdc++: [_GLIBCXX_DEBUG] Improve COW basic_string checks
>>
>>     Put the __glibcxx_requires_valid_range checks at public member level
>>     so that diagnostics are giving directly the invoked member name.
>>
>>     Add the std::_RequireInputIter constraints on members taking 
>> template
>>     iterator range to avoid the "do the right thing" ambiguity 
>> without the help of __is_integer.
>>
>>     libstdc++v3/ChangeLog:
>>
>>             * include/bits/cow_string.h
>>             (basic_string::_M_replace): New.
>>             (basic_string(_InputIterator, _InputIterator, const 
>> _Alloc&): Add
>>             std::_RequireInputIter constraint and 
>> __glibcxx_requires_valid_constructor_range
>
> Please fix the line-wrapping here. Try to keep the lines to < 72
> characters so that when indented by 'git log' they fit below 80.
>
>>             check.
>>             (basic_string::append(_InputIte, _InputIte): Add 
>> std::_RequireInputIter
>>             constraint and __glibcxx_requires_valid_range check. Call 
>> _M_replace.
>>             (basic_string::assign(_InputIte, _InputIte): Likewise.
>>             (basic_string::insert(iterator, _InputIte, _InputIte)): 
>> Likewise.
>>             (basic_string::replace(iterator, iterator, _InputIte, 
>> _InputIte)): Likewise.
>>             [__cplusplus >= 201103L](basic_string::_S_construct): New.
>>             (basic_string::_M_replace_dispatch(iterator, iterator, 
>> _InputIte, _InputIte,
>>             __false_type)): Remove __glibcxx_requires_valid_range check.
>>             * include/debug/debug.h 
>> (__glibcxx_requires_valid_constructor_range): New.
>>
>> Tested under Linux x86_64 with _GLIBCXX_USE_CXX11_ABI=0 and 
>> _GLIBCXX_DEBUG.
>
> Did you test C++98 as well as the default -std modes?

I hadn't but now did, ex:

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::
     normal_iterator<char*, std::vector<char, std::allocator<char> > >, 
std::
     debug::vector<char>, std::random_access_iterator_tag>; _CharT = char;
     _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]

Error: function requires a valid iterator range [first, last).

Objects involved in the operation:
     iterator "first" @ 0x7ffc9027e5a0 {
       type = gnu_cxx::normal_iterator<char*, std::vector<char, 
std::allocator<char> > > (mutable iterator);
       state = dereferenceable;
       references sequence with type 'std::debug::vector<char, 
std::allocator<char> >' @ 0x7ffc9027e750
     }
     iterator "last" @ 0x7ffc9027e600 {
       type = gnu_cxx::normal_iterator<char*, std::vector<char, 
std::allocator<char> > > (mutable iterator);
       state = dereferenceable;
       references sequence with type 'std::debug::vector<char, 
std::allocator<char> >' @ 0x7ffc9027e750
     }
XFAIL: 21_strings/basic_string/debug/append_neg.cc  -std=gnu++98 
execution test

>
> Doesn't this require new symbols to be exported by the linker script?
>
I don't think so. Maybe you had in mind the 
__gnu_debug::__check_valid_range but that's a template function 
implemented inline.

Ok to commit ?
-------------- next part --------------
diff --git a/libstdc++-v3/include/bits/cow_string.h b/libstdc++-v3/include/bits/cow_string.h
index 0f1a2e082d0..1e088f22241 100644
--- a/libstdc++-v3/include/bits/cow_string.h
+++ b/libstdc++-v3/include/bits/cow_string.h
@@ -721,10 +721,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        *  @param  __end  End of range.
        *  @param  __a  Allocator to use (default is default allocator).
        */
-      template<class _InputIterator>
+#if __cplusplus >= 201103L
+      template<typename _InputIterator,
+	       typename = std::_RequireInputIter<_InputIterator>>
+#else
+      template<typename _InputIterator>
+#endif
 	basic_string(_InputIterator __beg, _InputIterator __end,
 		     const _Alloc& __a = _Alloc())
-	: _M_dataplus(_S_construct(__beg, __end, __a), __a)
+	: _M_dataplus(_S_construct(
+	  __glibcxx_requires_valid_constructor_range(__beg, __end),
+	  __end, __a), __a)
 	{ }
 
 #ifdef __glibcxx_string_view // >= C++17
@@ -1391,10 +1398,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        *
        *  Appends characters in the range [__first,__last) to this string.
        */
-      template<class _InputIterator>
+#if __cplusplus >= 201103L
+      template<typename _InputIterator,
+	       typename = std::_RequireInputIter<_InputIterator>>
+#else
+      template<typename _InputIterator>
+#endif
 	basic_string&
 	append(_InputIterator __first, _InputIterator __last)
-	{ return this->replace(_M_iend(), _M_iend(), __first, __last); }
+	{
+	  __glibcxx_requires_valid_range(__first, __last);
+	  return _M_replace(_M_iend(), _M_iend(), __first, __last);
+	}
 
 #ifdef __glibcxx_string_view // >= C++17
       /**
@@ -1551,10 +1566,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        *
        *  Sets value of string to characters in the range [__first,__last).
       */
-      template<class _InputIterator>
+#if __cplusplus >= 201103L
+      template<typename _InputIterator,
+	       typename = std::_RequireInputIter<_InputIterator>>
+#else
+      template<typename _InputIterator>
+#endif
 	basic_string&
 	assign(_InputIterator __first, _InputIterator __last)
-	{ return this->replace(_M_ibegin(), _M_iend(), __first, __last); }
+	{
+	  __glibcxx_requires_valid_range(__first, __last);
+	  return _M_replace(_M_ibegin(), _M_iend(), __first, __last);
+	}
 
 #if __glibcxx_containers_ranges // C++ >= 23
       /**
@@ -1657,10 +1680,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        *  length_error is thrown.  The value of the string doesn't
        *  change if an error is thrown.
       */
-      template<class _InputIterator>
+#if __cplusplus >= 201103L
+      template<typename _InputIterator,
+	       typename = std::_RequireInputIter<_InputIterator>>
+#else
+      template<typename _InputIterator>
+#endif
 	void
 	insert(iterator __p, _InputIterator __beg, _InputIterator __end)
-	{ this->replace(__p, __p, __beg, __end); }
+	{
+	  _GLIBCXX_DEBUG_PEDASSERT(_M_ibegin() <= __p && __p <= _M_iend());
+	  __glibcxx_requires_valid_range(__beg, __end);
+	  _M_replace(__p, __p, __beg, __end);
+	}
 
 #if __glibcxx_containers_ranges // C++ >= 23
       /**
@@ -2143,15 +2175,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        *  The value of the string doesn't change if an error is
        *  thrown.
       */
-      template<class _InputIterator>
+#if __cplusplus >= 201103L
+      template<typename _InputIterator,
+	       typename = std::_RequireInputIter<_InputIterator>>
+#else
+      template<typename _InputIterator>
+#endif
 	basic_string&
 	replace(iterator __i1, iterator __i2,
 		_InputIterator __k1, _InputIterator __k2)
 	{
 	  _GLIBCXX_DEBUG_PEDASSERT(_M_ibegin() <= __i1 && __i1 <= __i2
 				   && __i2 <= _M_iend());
-	  typedef typename std::__is_integer<_InputIterator>::__type _Integral;
-	  return _M_replace_dispatch(__i1, __i2, __k1, __k2, _Integral());
+	  __glibcxx_requires_valid_range(__k1, __k2);
+	  return _M_replace(__i1, __i2, __k1, __k2);
 	}
 
       // Specializations for the common case of pointer and iterator:
@@ -2295,11 +2332,26 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif // C++17
 
     private:
+      template<class _InputIterator>
+	basic_string&
+	_M_replace(iterator __i1, iterator __i2,
+		   _InputIterator __k1, _InputIterator __k2)
+	{
+#if __cplusplus < 201103L
+	  typedef typename std::__is_integer<_InputIterator>::__type _Integral;
+#else
+	  using _Integral = std::__false_type;
+#endif
+	  return _M_replace_dispatch(__i1, __i2, __k1, __k2, _Integral());
+	}
+
+#if __cplusplus < 201103L
       template<class _Integer>
 	basic_string&
 	_M_replace_dispatch(iterator __i1, iterator __i2, _Integer __n,
 			    _Integer __val, __true_type)
 	{ return _M_replace_aux(__i1 - _M_ibegin(), __i2 - __i1, __n, __val); }
+#endif
 
       template<class _InputIterator>
 	basic_string&
@@ -2314,6 +2366,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _M_replace_safe(size_type __pos1, size_type __n1, const _CharT* __s,
 		      size_type __n2);
 
+#if __cplusplus < 201103L
       // _S_construct_aux is used to implement the 21.3.1 para 15 which
       // requires special behaviour if _InIter is an integral type
       template<class _InIterator>
@@ -2345,6 +2398,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  typedef typename std::__is_integer<_InIterator>::__type _Integral;
 	  return _S_construct_aux(__beg, __end, __a, _Integral());
 	}
+#else
+      template<typename _InIterator>
+	static _CharT*
+	_S_construct(_InIterator __beg, _InIterator __end, const _Alloc& __a)
+	{
+	  return _S_construct(__beg, __end, __a,
+			      std::__iterator_category(__beg));
+	}
+#endif
 
       // For Input Iterators, used in istreambuf_iterators, etc.
       template<class _InIterator>
@@ -3858,7 +3920,6 @@ _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/include/debug/debug.h b/libstdc++-v3/include/debug/debug.h
index 5604f98e4d3..aabb9af2271 100644
--- a/libstdc++-v3/include/debug/debug.h
+++ b/libstdc++-v3/include/debug/debug.h
@@ -65,6 +65,8 @@ namespace __gnu_debug
 
 # define __glibcxx_requires_cond(_Cond,_Msg)
 # define __glibcxx_requires_valid_range(_First,_Last)
+# define __glibcxx_requires_valid_constructor_range(_First,_Last) \
+  _First
 # define __glibcxx_requires_can_increment(_First,_Size)
 # define __glibcxx_requires_can_increment_range(_First1,_Last1,_First2)
 # define __glibcxx_requires_can_decrement_range(_First1,_Last1,_First2)
@@ -92,6 +94,8 @@ namespace __gnu_debug
 # define __glibcxx_requires_cond(_Cond,_Msg) _GLIBCXX_DEBUG_VERIFY(_Cond,_Msg)
 # define __glibcxx_requires_valid_range(_First,_Last)	\
   __glibcxx_check_valid_range(_First,_Last)
+# define __glibcxx_requires_valid_constructor_range(_First,_Last) \
+  __glibcxx_check_valid_constructor_range(_First,_Last)
 # define __glibcxx_requires_can_increment(_First,_Size)	\
   __glibcxx_check_can_increment(_First,_Size)
 # define __glibcxx_requires_can_increment_range(_First1,_Last1,_First2)	\


More information about the Libstdc++ mailing list