[PATCH][_GLIBCXX_DEBUG] Enhance COW basic_string checks
Jonathan Wakely
jwakely@redhat.com
Mon Jul 6 20:42:53 GMT 2026
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?
Doesn't this require new symbols to be exported by the linker script?
>
>Note that there is a double requires-valid-range check when _M_replace
>is being called because of the temporary basic_string instantiation.
>
>Ok to commit ?
>
>François
>diff --git a/libstdc++-v3/include/bits/cow_string.h b/libstdc++-v3/include/bits/cow_string.h
>index 7c945f6b998..eb8e80d06db 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
> /**
>@@ -1538,10 +1553,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
> /**
>@@ -1631,10 +1654,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
> /**
>@@ -2117,15 +2149,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:
>@@ -2269,11 +2306,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&
>@@ -2288,6 +2340,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>
>@@ -2319,6 +2372,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)
I was worried that this would break for some of the calls to
_S_construct(size_type, _CharT, const _Alloc&), but I think even in
the case where size_type and _CharT are the same it will be OK. For
C++11 and later, we either call _S_construct with a pair of iterators,
or with exactly size_type and _CharT, never with two arbitrary
integers of other types.
>+ {
>+ return _S_construct(__beg, __end, __a,
>+ std::__iterator_category(__beg));
>+ }
>+#endif
>
> // For Input Iterators, used in istreambuf_iterators, etc.
> template<class _InIterator>
>@@ -3832,7 +3894,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