[PATCH][_GLIBCXX_DEBUG] Enhance __gnu_debug::vector iterator invalidation
François Dumont
frs.dumont@gmail.com
Sun May 31 20:17:59 GMT 2026
On 5/28/26 13:46, Jonathan Wakely wrote:
> On Thu, 21 May 2026 at 22:20 +0200, François Dumont wrote:
>> Hi
>>
>> Following your remark Jonathan regarding how iterators are
>> invalidated in vector I reviewed the std::__debug::vector
>> implementation.
>
> Can you remind me where I said that? I remember discussing it for
> inplace_vector::assign (2025-11-03 in the thread about
> __debug::inplace_vector).
>
> I don't remember the comment you're referring to, so I don't know what
> I said :-)
In this message:
https://gcc.gnu.org/pipermail/libstdc++/2025-November/064301.html
You said:
Is it too aggressive to invalidate all iterators?
I know the generic Sequence requirements say that assign(i, j)
invalidates all iterators, but I think maybe that's a defect.
As std::vector is also a Sequence I thought you were talking about both
inplace_vector and vector.
Maybe I over interpreted it but I also think that when you are assigning
a vector to some new content the iterators do not have to be invalidated.
Reading this email again I think I'll have to consider the problem of
the end iterator, in a future patch...
>> I'll do something similar on std::__debug::inplace_vector.
>>
>> libstdc++: [_GLIBCXX_DEBUG] Enhance __gnu_debug::vector iterator
>> invalidation
>>
>> Rework the way std::__debug::vector's iterators are being
>> invalidated. Use a guard
>> approach which on destructor invalidates iterators based on the
>> state of the container.
>>
>> Moreover update invalidation policy in following vector methods:
>> - operator=(initializer_list)
>> - assign(Iterator, Iterator)
>> - assign(size_t, const _Tp&)
>> - assign(initializer_list)
>>
>> Current behavior to invalidate all iterators unconditionally is
>> preserved only if
>> _GLIBCXX_DEBUG_PEDANTIC is defined. Otherwise if a reallocation
>> occurs all iterators
>> are invaliidated else only iterators after the new container size
>> are invalidated.
>>
>> libstdc++-v3/ChangeLog:
>>
>> * include/debug/assertions.h [_GLIBCXX_DEBUG_STD_C]: New
>> macro.
>> * include/debug/vector (_Safe_vector<_Tp, _Allocator>):
>> Update template parameter.
>> (_Safe_vector::_M_requires_reallocation): Remove.
>> (_SafeVectorGuard): New.
>> [_GLIBCXX_DEBUG_PEDANTIC](_SafeVectorPedanticGuard): New.
>> (_SafeVectorInsertGuard): New.
>> (vector): Adapt methods to use new guard type.
>> *
>> testsuite/23_containers/vector/cons/destructible_debug_neg.cc: Adapt
>> error line
>> number.
>> *
>> testsuite/23_containers/vector/debug/initializer_list.cc: New test case.
>> *
>> testsuite/23_containers/vector/debug/initializer_list_pedantic.cc:
>> New test case.
>>
>> Partially tested on x86_64 w/o _GLIBCXX_DEBUG mode.
>>
>> Ok to commit once tests completed ?
>>
>> https://forge.sourceware.org/gcc/gcc-TEST/pulls/159
>>
>> François
>>
>>
>
>> diff --git a/libstdc++-v3/include/debug/assertions.h
>> b/libstdc++-v3/include/debug/assertions.h
>> index c4993dc8596..768a7f05228 100644
>> --- a/libstdc++-v3/include/debug/assertions.h
>> +++ b/libstdc++-v3/include/debug/assertions.h
>> @@ -31,6 +31,12 @@
>>
>> #include <bits/c++config.h>
>>
>> +#ifdef _GLIBCXX_DEBUG
>> +# define _GLIBCXX_DEBUG_STD_C ::std::_GLIBCXX_STD_C
>> +#else
>> +# define _GLIBCXX_DEBUG_STD_C ::std
>> +#endif
>> +
>> #ifndef _GLIBCXX_DEBUG
>> // Verify that [_First, _Last) forms a non-empty iterator range.
>> # define __glibcxx_requires_non_empty_range(_First,_Last) \
>> diff --git a/libstdc++-v3/include/debug/vector
>> b/libstdc++-v3/include/debug/vector
>> index 61e5ff78a7a..1e1a971036e 100644
>> --- a/libstdc++-v3/include/debug/vector
>> +++ b/libstdc++-v3/include/debug/vector
>> @@ -51,11 +51,12 @@ namespace __gnu_debug
>> * detecting code which relies on non-portable implementation
>> details of
>> * the libstdc++ reallocation policy.
>> */
>> - template<typename _SafeSequence,
>> - typename _BaseSequence>
>> + template<class _Tp, typename _Allocator>
>> class _Safe_vector
>> {
>> - typedef typename _BaseSequence::size_type size_type;
>> + typedef std::__debug::vector<_Tp, _Allocator> _SafeSequence;
>> + typedef _GLIBCXX_DEBUG_STD_C::vector<_Tp, _Allocator> _StdVector;
>> + typedef typename _StdVector::size_type size_type;
>>
>> _GLIBCXX20_CONSTEXPR
>> const _SafeSequence&
>> @@ -103,10 +104,6 @@ namespace __gnu_debug
>>
>> size_type _M_guaranteed_capacity;
>>
>> - bool
>> - _M_requires_reallocation(size_type __elements) const
>> _GLIBCXX_NOEXCEPT
>> - { return __elements > _M_seq().capacity(); }
>> -
>> _GLIBCXX20_CONSTEXPR
>> void
>> _M_update_guaranteed_capacity() _GLIBCXX_NOEXCEPT
>> @@ -115,6 +112,66 @@ namespace __gnu_debug
>> _M_guaranteed_capacity = _M_seq().size();
>> }
>> };
>> +
>> + template<class _Tp, typename _Allocator>
>> + class _SafeVectorGuard
>> + {
>> + typedef std::__debug::vector<_Tp, _Allocator> _SafeVector;
>> + typedef _GLIBCXX_DEBUG_STD_C::vector<_Tp, _Allocator> _StdVector;
>> + typedef typename _StdVector::size_type size_type;
>> +
>> + _SafeVector& _M_vect;
>> + size_type _M_capacity, _M_size;
>> +
>> + public:
>> + bool _M_active;
>> +
>> + _GLIBCXX20_CONSTEXPR
>> + _SafeVectorGuard(_SafeVector& __vector);
>> +
>> + _GLIBCXX20_CONSTEXPR
>> + ~_SafeVectorGuard();
>> + };
>> +
>> +#ifdef _GLIBCXX_DEBUG_PEDANTIC
>> + template<class _Tp, typename _Allocator>
>> + class _SafeVectorPedanticGuard
>> + {
>> + typedef std::__debug::vector<_Tp, _Allocator> _SafeVector;
>> + _SafeVector& _M_vect;
>> +
>> + public:
>> + bool _M_active;
>> +
>> + _GLIBCXX20_CONSTEXPR
>> + _SafeVectorPedanticGuard(_SafeVector& __vector);
>> +
>> + _GLIBCXX20_CONSTEXPR
>> + ~_SafeVectorPedanticGuard();
>> + };
>> +#endif
>> +
>> + template<class _Tp, typename _Allocator>
>> + class _SafeVectorInsertGuard
>> + {
>> + typedef std::__debug::vector<_Tp, _Allocator> _SafeVector;
>> + typedef _GLIBCXX_DEBUG_STD_C::vector<_Tp, _Allocator> _StdVector;
>> + typedef typename _StdVector::size_type size_type;
>> + typedef typename _StdVector::difference_type difference_type;
>> +
>> + _SafeVector& _M_vect;
>> + size_type _M_capacity;
>> + difference_type _M_offset;
>> +
>> + public:
>> + bool _M_active;
>> +
>> + _GLIBCXX20_CONSTEXPR
>> + _SafeVectorInsertGuard(_SafeVector& __vector, difference_type
>> __offset);
>> +
>> + _GLIBCXX20_CONSTEXPR
>> + ~_SafeVectorInsertGuard();
>> + };
>> }
>
> All the guard types above need comments explaining what they do. When
> should you use each one? How are they different from each other?
>
> How do we deviate from the standard when not in pedantic mode?
>
> I'm unclear what they actually do, and how that corresponds to the
> standard.
>
> _SafeVectorGuard invalidates all iterators if the size increased to
> greater than capacity. That's used by resize and emplace_back and
> push_back, right? And if the size was reduced, invalidate iterators to
> the erased elements. This must not be used for reserve, because it
> would not invalidate all iterators on reallocation, because the size
> would not change.
>
> _SafeVectorPedanticGuard invalidates all iterators unconditionally,
> which is what the standard says to do for assign.
>
> _SafeVectorInsertGuard seems to be almost the same as _SafeVectorGuard
> but using an arbitrary position for the "invalidates all after pos"
> instead of only invalidating at the end.
Yes, I wonder if I could use only 1 implementation for both but I think
it would make this implementation more complicated to get it right.
Ideally we should have a 4th implementation just invalidating all
iterators in case of reallocation. I preferred to use _SafeVectorGuard
(_ResizeGuard) in this case, which is fine to use when size if always
increased like in push_back or emplace_back.
>
> Would it be more clear to have _ResizeGuard, _AssignGuard (which
> changes behaviour for pedantic vs non-pedantic), and _InsertGuard?
>
I've adopted those names.
And doing so I changed the type of guard used in assign_range and
insert_range.
>>
>> namespace std _GLIBCXX_VISIBILITY(default)
>> @@ -128,14 +185,33 @@ namespace __debug
>> : public __gnu_debug::_Safe_container<
>> vector<_Tp, _Allocator>, _Allocator, __gnu_debug::_Safe_sequence>,
>> public _GLIBCXX_STD_C::vector<_Tp, _Allocator>,
>> - public __gnu_debug::_Safe_vector<
>> - vector<_Tp, _Allocator>,
>> - _GLIBCXX_STD_C::vector<_Tp, _Allocator> >
>> + public __gnu_debug::_Safe_vector<_Tp, _Allocator>
>> {
>> typedef _GLIBCXX_STD_C::vector<_Tp, _Allocator> _Base;
>> typedef __gnu_debug::_Safe_container<
>> vector, _Allocator, __gnu_debug::_Safe_sequence> _Safe;
>> - typedef __gnu_debug::_Safe_vector<vector, _Base>
>> _Safe_vector;
>> + typedef __gnu_debug::_Safe_vector<_Tp, _Allocator>
>> _Safe_vector;
>> +
>> + typedef __gnu_debug::_SafeVectorGuard<_Tp, _Allocator> _Guard;
>> +#ifdef _GLIBCXX_DEBUG_PEDANTIC
>> + typedef __gnu_debug::_SafeVectorPedanticGuard<_Tp, _Allocator>
>> + _MaybePedanticGuard;
>> +#else
>> + typedef _Guard _MaybePedanticGuard;
>
> This name seems focused on the detail of how we decide which type to
> use, not what it actually does. And _SafeVectorGuard is also "maybe
> pedantic" because its destructor changes behaviour for pedantic mode.
>
> Would _AssignGuard be a better name? That reflects where it's actually
> used.
Yes, looks better. Only _ResizeGuard looks a little bit surprising in
push_back/emplace_back/append_Range.
Maybe I should define _ReallocGuard for those methods, even it would
just be another alias of _SafeVectorGuard.
>
>> +#endif
>> + typedef __gnu_debug::_SafeVectorInsertGuard<_Tp, _Allocator>
>> + _InsertGuard;
>> +
>> + template<typename _Tp2, typename _Alloc2>
>> + friend class ::__gnu_debug::_SafeVectorGuard;
>> +
>> +#ifdef _GLIBCXX_DEBUG_PEDANTIC
>> + template<typename _Tp2, typename _Alloc2>
>> + friend class ::__gnu_debug::_SafeVectorPedanticGuard;
>> +#endif
>> +
>> + template<typename _Tp2, typename _Alloc2>
>> + friend class ::__gnu_debug::_SafeVectorInsertGuard;
>>
>> typedef typename _Base::iterator _Base_iterator;
>> typedef typename _Base::const_iterator _Base_const_iterator;
>> @@ -276,12 +352,9 @@ namespace __debug
>> vector&
>> operator=(initializer_list<value_type> __l)
>> {
>> + _MaybePedanticGuard __guard(*this);
>> _Base::operator=(__l);
>> - if (!std::__is_constant_evaluated())
>> - {
>> - this->_M_invalidate_all();
>> - this->_M_update_guaranteed_capacity();
>> - }
>> + __guard._M_active = true;
>> return *this;
>> }
>> #endif
>> @@ -303,26 +376,23 @@ namespace __debug
>> typename __gnu_debug::_Distance_traits<_InputIterator>::__type
>> __dist;
>> __glibcxx_check_valid_range2(__first, __last, __dist);
>>
>> + _MaybePedanticGuard __guard(*this);
>> if (__dist.second >= __gnu_debug::__dp_sign)
>> _Base::assign(__gnu_debug::__unsafe(__first),
>> __gnu_debug::__unsafe(__last));
>> else
>> _Base::assign(__first, __last);
>>
>> - this->_M_invalidate_all();
>> - this->_M_update_guaranteed_capacity();
>> + __guard._M_active = true;
>> }
>>
>> _GLIBCXX20_CONSTEXPR
>> void
>> assign(size_type __n, const _Tp& __u)
>> {
>> + _MaybePedanticGuard __guard(*this);
>> _Base::assign(__n, __u);
>> - if (!std::__is_constant_evaluated())
>> - {
>> - this->_M_invalidate_all();
>> - this->_M_update_guaranteed_capacity();
>> - }
>> + __guard._M_active = true;
>> }
>>
>> #if __cplusplus >= 201103L
>> @@ -330,12 +400,9 @@ namespace __debug
>> void
>> assign(initializer_list<value_type> __l)
>> {
>> + _MaybePedanticGuard __guard(*this);
>> _Base::assign(__l);
>> - if (!std::__is_constant_evaluated())
>> - {
>> - this->_M_invalidate_all();
>> - this->_M_update_guaranteed_capacity();
>> - }
>> + __guard._M_active = true;
>> }
>> #endif
>>
>> @@ -428,13 +495,9 @@ namespace __debug
>> if (std::__is_constant_evaluated())
>> return _Base::resize(__sz);
>>
>> - bool __realloc = this->_M_requires_reallocation(__sz);
>> - if (__sz < this->size())
>> - this->_M_invalidate_after_nth(__sz);
>> + _Guard __guard(*this);
>> _Base::resize(__sz);
>> - if (__realloc)
>> - this->_M_invalidate_all();
>> - this->_M_update_guaranteed_capacity();
>> + __guard._M_active = true;
>> }
>>
>> _GLIBCXX20_CONSTEXPR
>> @@ -444,25 +507,17 @@ namespace __debug
>> if (std::__is_constant_evaluated())
>> return _Base::resize(__sz, __c);
>>
>> - bool __realloc = this->_M_requires_reallocation(__sz);
>> - if (__sz < this->size())
>> - this->_M_invalidate_after_nth(__sz);
>> + _Guard __guard(*this);
>> _Base::resize(__sz, __c);
>> - if (__realloc)
>> - this->_M_invalidate_all();
>> - this->_M_update_guaranteed_capacity();
>> + __guard._M_active = true;
>> }
>> #else
>> void
>> resize(size_type __sz, _Tp __c = _Tp())
>> {
>> - bool __realloc = this->_M_requires_reallocation(__sz);
>> - if (__sz < this->size())
>> - this->_M_invalidate_after_nth(__sz);
>> + _Guard __guard(*this);
>> _Base::resize(__sz, __c);
>> - if (__realloc)
>> - this->_M_invalidate_all();
>> - this->_M_update_guaranteed_capacity();
>> + __guard._M_active = true;
>> }
>> #endif
>>
>> @@ -506,7 +561,7 @@ namespace __debug
>> if (std::__is_constant_evaluated())
>> return _Base::reserve(__n);
>>
>> - bool __realloc = this->_M_requires_reallocation(__n);
>> + bool __realloc = __n > capacity();
>> _Base::reserve(__n);
>> if (__n > this->_M_guaranteed_capacity)
>> this->_M_guaranteed_capacity = __n;
>> @@ -583,11 +638,9 @@ namespace __debug
>> if (std::__is_constant_evaluated())
>> return _Base::push_back(__x);
>>
>> - bool __realloc = this->_M_requires_reallocation(this->size() + 1);
>> + _Guard __guard(*this);
>> _Base::push_back(__x);
>> - if (__realloc)
>> - this->_M_invalidate_all();
>> - this->_M_update_guaranteed_capacity();
>> + __guard._M_active = true;
>> }
>>
>> #if __cplusplus >= 201103L
>> @@ -610,11 +663,9 @@ namespace __debug
>> if (std::__is_constant_evaluated())
>> return _Base::emplace_back(std::forward<_Args>(__args)...);
>>
>> - bool __realloc = this->_M_requires_reallocation(this->size() +
>> 1);
>> + _Guard __guard(*this);
>> _Base::emplace_back(std::forward<_Args>(__args)...);
>> - if (__realloc)
>> - this->_M_invalidate_all();
>> - this->_M_update_guaranteed_capacity();
>> + __guard._M_active = true;
>> #if __cplusplus > 201402L
>> return back();
>> #endif
>> @@ -645,15 +696,14 @@ namespace __debug
>> this);
>>
>> __glibcxx_check_insert(__position);
>> - bool __realloc = this->_M_requires_reallocation(this->size() +
>> 1);
>> + _Base_iterator __res;
>> + {
>> difference_type __offset = __position.base() - _Base::cbegin();
>> - _Base_iterator __res = _Base::emplace(__position.base(),
>> + _InsertGuard __guard(*this, __offset);
>> + __res = _Base::emplace(__position.base(),
>> std::forward<_Args>(__args)...);
>> - if (__realloc)
>> - this->_M_invalidate_all();
>> - else
>> - this->_M_invalidate_after_nth(__offset);
>> - this->_M_update_guaranteed_capacity();
>> + __guard._M_active = true;
>> + }
>> return { __res, this };
>> }
>> #endif
>> @@ -670,14 +720,13 @@ namespace __debug
>> return iterator(_Base::insert(__position.base(), __x), this);
>>
>> __glibcxx_check_insert(__position);
>> - bool __realloc = this->_M_requires_reallocation(this->size() + 1);
>> + _Base_iterator __res;
>> + {
>> difference_type __offset = __position.base() - _Base::begin();
>> - _Base_iterator __res = _Base::insert(__position.base(), __x);
>> - if (__realloc)
>> - this->_M_invalidate_all();
>> - else
>> - this->_M_invalidate_after_nth(__offset);
>> - this->_M_update_guaranteed_capacity();
>> + _InsertGuard __guard(*this, __offset);
>> + __res = _Base::insert(__position.base(), __x);
>> + __guard._M_active = true;
>> + }
>> return iterator(__res, this);
>> }
>>
>> @@ -704,14 +753,13 @@ namespace __debug
>> return iterator(_Base::insert(__position.base(), __n, __x), this);
>>
>> __glibcxx_check_insert(__position);
>> - bool __realloc = this->_M_requires_reallocation(this->size() +
>> __n);
>> + _Base_iterator __res;
>> + {
>> difference_type __offset = __position.base() - _Base::cbegin();
>> - _Base_iterator __res = _Base::insert(__position.base(), __n, __x);
>> - if (__realloc)
>> - this->_M_invalidate_all();
>> - else
>> - this->_M_invalidate_after_nth(__offset);
>> - this->_M_update_guaranteed_capacity();
>> + _InsertGuard __guard(*this, __offset);
>> + __res = _Base::insert(__position.base(), __n, __x);
>> + __guard._M_active = true;
>> + }
>> return { __res, this };
>> }
>> #else
>> @@ -719,14 +767,10 @@ namespace __debug
>> insert(iterator __position, size_type __n, const _Tp& __x)
>> {
>> __glibcxx_check_insert(__position);
>> - bool __realloc = this->_M_requires_reallocation(this->size() +
>> __n);
>> difference_type __offset = __position.base() - _Base::begin();
>> + _InsertGuard __guard(*this, __offset);
>> _Base::insert(__position.base(), __n, __x);
>> - if (__realloc)
>> - this->_M_invalidate_all();
>> - else
>> - this->_M_invalidate_after_nth(__offset);
>> - this->_M_update_guaranteed_capacity();
>> + __guard._M_active = true;
>> }
>> #endif
>>
>> @@ -746,12 +790,10 @@ namespace __debug
>> typename __gnu_debug::_Distance_traits<_InputIterator>::__type
>> __dist;
>> __glibcxx_check_insert_range(__position, __first, __last, __dist);
>>
>> - /* Hard to guess if invalidation will occur, because __last
>> - - __first can't be calculated in all cases, so we just
>> - punt here by checking if it did occur. */
>> - _Base_iterator __old_begin = _M_base().begin();
>> - difference_type __offset = __position.base() - _Base::cbegin();
>> _Base_iterator __res;
>> + {
>> + difference_type __offset = __position.base() - _Base::cbegin();
>> + _InsertGuard __guard(*this, __offset);
>> if (__dist.second >= __gnu_debug::__dp_sign)
>> __res = _Base::insert(__position.base(),
>> __gnu_debug::__unsafe(__first),
>> @@ -759,11 +801,8 @@ namespace __debug
>> else
>> __res = _Base::insert(__position.base(), __first, __last);
>>
>> - if (_M_base().begin() != __old_begin)
>> - this->_M_invalidate_all();
>> - else
>> - this->_M_invalidate_after_nth(__offset);
>> - this->_M_update_guaranteed_capacity();
>> + __guard._M_active = true;
>> + }
>> return { __res, this };
>> }
>> #else
>> @@ -775,22 +814,15 @@ namespace __debug
>> typename __gnu_debug::_Distance_traits<_InputIterator>::__type
>> __dist;
>> __glibcxx_check_insert_range(__position, __first, __last, __dist);
>>
>> - /* Hard to guess if invalidation will occur, because __last
>> - - __first can't be calculated in all cases, so we just
>> - punt here by checking if it did occur. */
>> - _Base_iterator __old_begin = _M_base().begin();
>> difference_type __offset = __position.base() - _Base::begin();
>> + _InsertGuard __guard(*this, __offset);
>> if (__dist.second >= __gnu_debug::__dp_sign)
>> _Base::insert(__position.base(), __gnu_debug::__unsafe(__first),
>> __gnu_debug::__unsafe(__last));
>> else
>> _Base::insert(__position.base(), __first, __last);
>>
>> - if (_M_base().begin() != __old_begin)
>> - this->_M_invalidate_all();
>> - else
>> - this->_M_invalidate_after_nth(__offset);
>> - this->_M_update_guaranteed_capacity();
>> + __guard._M_active = true;
>> }
>> #endif
>>
>> @@ -876,30 +908,20 @@ namespace __debug
>> constexpr void
>> assign_range(_Rg&& __rg)
>> {
>> - auto __old_capacity = _Base::capacity();
>> - auto __old_size = _Base::size();
>> + _Guard __guard(*this);
>> _Base::assign_range(__rg);
>> - if (!std::__is_constant_evaluated())
>> - {
>> - if (_Base::capacity() != __old_capacity)
>> - this->_M_invalidate_all();
>> - else if (_Base::size() < __old_size)
>> - this->_M_invalidate_after_nth(_Base::size());
>> - this->_M_update_guaranteed_capacity();
>> - }
>> + __guard._M_active = true;
>> }
>>
>> template<__detail::__container_compatible_range<_Tp> _Rg>
>> constexpr iterator
>> insert_range(const_iterator __pos, _Rg&& __rg)
>> {
>> - auto __old_capacity = _Base::capacity();
>> - auto __res = _Base::insert_range(__pos.base(), __rg);
>> - if (!std::__is_constant_evaluated())
>> + _Base_iterator __res;
>> {
>> - if (_Base::capacity() != __old_capacity)
>> - this->_M_invalidate_all();
>> - this->_M_update_guaranteed_capacity();
>> + _Guard __guard(*this);
>> + __res = _Base::insert_range(__pos.base(), __rg);
>> + __guard._M_active = true;
>> }
>> return iterator(__res, this);
>> }
>> @@ -908,14 +930,9 @@ namespace __debug
>> constexpr void
>> append_range(_Rg&& __rg)
>> {
>> - auto __old_capacity = _Base::capacity();
>> + _Guard __guard(*this);
>> _Base::append_range(__rg);
>> - if (!std::__is_constant_evaluated())
>> - {
>> - if (_Base::capacity() != __old_capacity)
>> - this->_M_invalidate_all();
>> - this->_M_update_guaranteed_capacity();
>> - }
>> + __guard._M_active = true;
>> }
>> #endif
>>
>> @@ -1057,6 +1074,84 @@ _GLIBCXX_END_NAMESPACE_VERSION
>>
>> namespace __gnu_debug
>> {
>> + template<class _Tp, typename _Allocator>
>> + _GLIBCXX20_CONSTEXPR
>> + _SafeVectorGuard<_Tp, _Allocator>::_SafeVectorGuard(
>> + std::__debug::vector<_Tp, _Allocator>& __vector)
>> + : _M_vect(__vector)
>> + , _M_capacity(__vector.capacity())
>> + , _M_size(__vector.size())
>> + , _M_active(false)
>> + { }
>> +
>> + template<class _Tp, typename _Allocator>
>> + _GLIBCXX20_CONSTEXPR
>> + _SafeVectorGuard<_Tp, _Allocator>::~_SafeVectorGuard()
>> + {
>> + if (!std::__is_constant_evaluated() && _M_active)
>> + {
>> +#ifdef _GLIBCXX_DEBUG_PEDANTIC
>> + if (_M_vect.size() > _M_capacity)
>> +#else
>> + if (_M_vect.capacity() != _M_capacity)
>> +#endif
>
> I don't understand this condition. What difference does it make, and
> why do we want it?
>
> For resize and emplace_back/push_back the two conditions are
> equivalent, right? _M_vect.capacity() would not change (i.e.
> reallocation would not occur) unless v.size() == v.capacity(),
> and in that case v.size() would become greater than capacity.
>
> When are these different?
When _GLIBCXX_DEBUG_PEDANTIC _M_vect.capacity() returns a 'guaranteed
capacity' and not the actual underlying vector capacity.
This guaranteed capacity can change on emplace_back/push_back. The
problem is that it is updated later in the guard destructor, on the call
to _M_update_guaranteed_capacity().
Thinking about it to reply I realized that I should not preserve
original code order and just update guaranteed capacity first so that I
only need (_M_vect.capacity() != _M_capacity) check.
>
>> + _M_vect._M_invalidate_all();
>> + else if (_M_vect.size() < _M_size)
>> + _M_vect._M_invalidate_after_nth(_M_vect.size());
>> +
>> + _M_vect._M_update_guaranteed_capacity();
>> + }
>> + }
>> +
>> +#ifdef _GLIBCXX_DEBUG_PEDANTIC
>> + template<class _Tp, typename _Allocator>
>> + _GLIBCXX20_CONSTEXPR
>> + _SafeVectorPedanticGuard<_Tp,
>> _Allocator>::_SafeVectorPedanticGuard(
>> + std::__debug::vector<_Tp, _Allocator>& __vector)
>> + : _M_vect(__vector), _M_active(false)
>> + { }
>> +
>> + template<class _Tp, typename _Allocator>
>> + _GLIBCXX20_CONSTEXPR
>> + _SafeVectorPedanticGuard<_Tp,
>> _Allocator>::~_SafeVectorPedanticGuard()
>> + {
>> + if (!std::__is_constant_evaluated() && _M_active)
>> + {
>> + _M_vect._M_invalidate_all();
>> + _M_vect._M_update_guaranteed_capacity();
>> + }
>> + }
>> +#endif
>> +
>> + template<class _Tp, typename _Allocator>
>> + _GLIBCXX20_CONSTEXPR
>> + _SafeVectorInsertGuard<_Tp, _Allocator>::_SafeVectorInsertGuard(
>> + std::__debug::vector<_Tp, _Allocator>& __vector, difference_type
>> __offset)
>> + : _M_vect(__vector)
>> + , _M_capacity(__vector.capacity())
>> + , _M_offset(__offset)
>> + , _M_active(false)
>> + { }
>> +
>> + template<class _Tp, typename _Allocator>
>> + _GLIBCXX20_CONSTEXPR
>> + _SafeVectorInsertGuard<_Tp, _Allocator>::~_SafeVectorInsertGuard()
>> + {
>> + if (!std::__is_constant_evaluated() && _M_active)
>> + {
>> +#ifdef _GLIBCXX_DEBUG_PEDANTIC
>> + if (_M_vect.size() > _M_capacity)
>> +#else
>> + if (_M_vect.capacity() != _M_capacity)
>> +#endif
>
> Same question here.
>
>> + _M_vect._M_invalidate_all();
>> + else
>> + _M_vect._M_invalidate_after_nth(_M_offset);
>> +
>> + _M_vect._M_update_guaranteed_capacity();
>> + }
>> + }
>> +
>> template<typename _Tp, typename _Alloc>
>> struct _Is_contiguous_sequence<std::__debug::vector<_Tp, _Alloc> >
>> : std::__true_type
>> diff --git
>> a/libstdc++-v3/testsuite/23_containers/vector/cons/destructible_debug_neg.cc
>> b/libstdc++-v3/testsuite/23_containers/vector/cons/destructible_debug_neg.cc
>>
>> index 2c9abcdbb91..0a29c0432e6 100644
>> ---
>> a/libstdc++-v3/testsuite/23_containers/vector/cons/destructible_debug_neg.cc
>> +++
>> b/libstdc++-v3/testsuite/23_containers/vector/cons/destructible_debug_neg.cc
>> @@ -47,4 +47,4 @@ test02()
>> // { dg-error "PrivateDtor.* is private" "" { target *-*-* } 0 }
>>
>> // In Debug Mode the "required from here" errors come from
>> <debug/vector>
>> -// { dg-error "required from here" "" { target *-*-* } 182 }
>> +// { dg-error "required from here" "" { target *-*-* } 258 }
>> diff --git
>> a/libstdc++-v3/testsuite/23_containers/vector/debug/initializer_list.cc
>> b/libstdc++-v3/testsuite/23_containers/vector/debug/initializer_list.cc
>> new file mode 100644
>> index 00000000000..318f6b98de9
>> --- /dev/null
>> +++
>> b/libstdc++-v3/testsuite/23_containers/vector/debug/initializer_list.cc
>> @@ -0,0 +1,26 @@
>> +// { dg-do compile { target c++11 } }
>> +
>> +#include <debug/vector>
>> +#include <testsuite_hooks.h>
>> +
>> +void
>> +test01()
>> +{
>> + __gnu_debug::vector<int> v = { 0, 1, 2, 3, 4, 5 };
>> +
>> + auto it1 = v.begin() + 1;
>> + auto it4 = v.begin() + 4;
>> +
>> + VERIFY( !it1._M_singular() );
>> + VERIFY( !it4._M_singular() );
>> +
>> + v = { 10, 11, 12, 13 };
>> +
>> + VERIFY( !it1._M_singular() );
>> + VERIFY( it4._M_singular() );
>> +}
>> +
>> +int main()
>> +{
>> + test01();
>> +}
>> diff --git
>> a/libstdc++-v3/testsuite/23_containers/vector/debug/initializer_list_pedantic.cc
>> b/libstdc++-v3/testsuite/23_containers/vector/debug/initializer_list_pedantic.cc
>>
>> new file mode 100644
>> index 00000000000..c8b3af25bd1
>> --- /dev/null
>> +++
>> b/libstdc++-v3/testsuite/23_containers/vector/debug/initializer_list_pedantic.cc
>> @@ -0,0 +1,27 @@
>> +// { dg-do compile { target c++11 } }
>> +// { dg-options "-D_GLIBCXX_DEBUG_PEDANTIC" }
>> +
>> +#include <debug/vector>
>> +#include <testsuite_hooks.h>
>> +
>> +void
>> +test01()
>> +{
>> + __gnu_debug::vector<int> v = { 0, 1, 2, 3, 4, 5 };
>> +
>> + auto it1 = v.begin() + 1;
>> + auto it4 = v.begin() + 4;
>> +
>> + VERIFY( !it1._M_singular() );
>> + VERIFY( !it4._M_singular() );
>> +
>> + v = { 10, 11, 12, 13 };
>> +
>> + VERIFY( it1._M_singular() );
>> + VERIFY( it4._M_singular() );
>> +}
>> +
>> +int main()
>> +{
>> + test01();
>> +}
>
Here is the new patch.
Tested under Linux x86_64 w/o _GLIBCXX_DEBUG mode.
Ok to commit ?
François
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20260531/21229353/attachment-0001.htm>
-------------- next part --------------
diff --git a/libstdc++-v3/include/debug/assertions.h b/libstdc++-v3/include/debug/assertions.h
index c4993dc8596..768a7f05228 100644
--- a/libstdc++-v3/include/debug/assertions.h
+++ b/libstdc++-v3/include/debug/assertions.h
@@ -31,6 +31,12 @@
#include <bits/c++config.h>
+#ifdef _GLIBCXX_DEBUG
+# define _GLIBCXX_DEBUG_STD_C ::std::_GLIBCXX_STD_C
+#else
+# define _GLIBCXX_DEBUG_STD_C ::std
+#endif
+
#ifndef _GLIBCXX_DEBUG
// Verify that [_First, _Last) forms a non-empty iterator range.
# define __glibcxx_requires_non_empty_range(_First,_Last) \
diff --git a/libstdc++-v3/include/debug/vector b/libstdc++-v3/include/debug/vector
index 56645d1e92c..ad79ec00919 100644
--- a/libstdc++-v3/include/debug/vector
+++ b/libstdc++-v3/include/debug/vector
@@ -51,11 +51,12 @@ namespace __gnu_debug
* detecting code which relies on non-portable implementation details of
* the libstdc++ reallocation policy.
*/
- template<typename _SafeSequence,
- typename _BaseSequence>
+ template<class _Tp, typename _Allocator>
class _Safe_vector
{
- typedef typename _BaseSequence::size_type size_type;
+ typedef std::__debug::vector<_Tp, _Allocator> _SafeSequence;
+ typedef _GLIBCXX_DEBUG_STD_C::vector<_Tp, _Allocator> _StdVector;
+ typedef typename _StdVector::size_type size_type;
_GLIBCXX20_CONSTEXPR
const _SafeSequence&
@@ -103,10 +104,6 @@ namespace __gnu_debug
size_type _M_guaranteed_capacity;
- bool
- _M_requires_reallocation(size_type __elements) const _GLIBCXX_NOEXCEPT
- { return __elements > _M_seq().capacity(); }
-
_GLIBCXX20_CONSTEXPR
void
_M_update_guaranteed_capacity() _GLIBCXX_NOEXCEPT
@@ -115,6 +112,81 @@ namespace __gnu_debug
_M_guaranteed_capacity = _M_seq().size();
}
};
+
+ /** @brief Guard class for iterators of vector invalidation.
+ *
+ * This guard invalidates on destroy, if activated, all iterators if
+ * reallocation took place and all iterators after updated size otherwise.
+ */
+ template<class _Tp, typename _Allocator>
+ class _SafeVectorGuard
+ {
+ typedef std::__debug::vector<_Tp, _Allocator> _SafeVector;
+ typedef _GLIBCXX_DEBUG_STD_C::vector<_Tp, _Allocator> _StdVector;
+ typedef typename _StdVector::size_type size_type;
+
+ _SafeVector& _M_vect;
+ size_type _M_capacity, _M_size;
+
+ public:
+ bool _M_active;
+
+ _GLIBCXX20_CONSTEXPR
+ _SafeVectorGuard(_SafeVector& __vector);
+
+ _GLIBCXX20_CONSTEXPR
+ ~_SafeVectorGuard();
+ };
+
+#ifdef _GLIBCXX_DEBUG_PEDANTIC
+ /** @brief Guard class for iterators of vector invalidation.
+ *
+ * This guard invalidates on destroy, if activated, all iterators
+ * no matter what happenned to the vector.
+ */
+ template<class _Tp, typename _Allocator>
+ class _SafeVectorPedanticGuard
+ {
+ typedef std::__debug::vector<_Tp, _Allocator> _SafeVector;
+ _SafeVector& _M_vect;
+
+ public:
+ bool _M_active;
+
+ _GLIBCXX20_CONSTEXPR
+ _SafeVectorPedanticGuard(_SafeVector& __vector);
+
+ _GLIBCXX20_CONSTEXPR
+ ~_SafeVectorPedanticGuard();
+ };
+#endif
+
+ /** @brief Guard class for iterators of vector invalidation.
+ *
+ * This guard invalidates on destroy, if activated, all iterators if
+ * reallocation took place and all iterators after an offset otherwise.
+ */
+ template<class _Tp, typename _Allocator>
+ class _SafeVectorInsertGuard
+ {
+ typedef std::__debug::vector<_Tp, _Allocator> _SafeVector;
+ typedef _GLIBCXX_DEBUG_STD_C::vector<_Tp, _Allocator> _StdVector;
+ typedef typename _StdVector::size_type size_type;
+ typedef typename _StdVector::difference_type difference_type;
+
+ _SafeVector& _M_vect;
+ size_type _M_capacity;
+ difference_type _M_offset;
+
+ public:
+ bool _M_active;
+
+ _GLIBCXX20_CONSTEXPR
+ _SafeVectorInsertGuard(_SafeVector& __vector, difference_type __offset);
+
+ _GLIBCXX20_CONSTEXPR
+ ~_SafeVectorInsertGuard();
+ };
}
namespace std _GLIBCXX_VISIBILITY(default)
@@ -128,14 +200,33 @@ namespace __debug
: public __gnu_debug::_Safe_container<
vector<_Tp, _Allocator>, _Allocator, __gnu_debug::_Safe_sequence>,
public _GLIBCXX_STD_C::vector<_Tp, _Allocator>,
- public __gnu_debug::_Safe_vector<
- vector<_Tp, _Allocator>,
- _GLIBCXX_STD_C::vector<_Tp, _Allocator> >
+ public __gnu_debug::_Safe_vector<_Tp, _Allocator>
{
typedef _GLIBCXX_STD_C::vector<_Tp, _Allocator> _Base;
typedef __gnu_debug::_Safe_container<
vector, _Allocator, __gnu_debug::_Safe_sequence> _Safe;
- typedef __gnu_debug::_Safe_vector<vector, _Base> _Safe_vector;
+ typedef __gnu_debug::_Safe_vector<_Tp, _Allocator> _Safe_vector;
+
+ typedef __gnu_debug::_SafeVectorGuard<_Tp, _Allocator> _ResizeGuard;
+#ifdef _GLIBCXX_DEBUG_PEDANTIC
+ typedef __gnu_debug::_SafeVectorPedanticGuard<_Tp, _Allocator>
+ _AssignGuard;
+#else
+ typedef _ResizeGuard _AssignGuard;
+#endif
+ typedef __gnu_debug::_SafeVectorInsertGuard<_Tp, _Allocator>
+ _InsertGuard;
+
+ template<typename _Tp2, typename _Alloc2>
+ friend class ::__gnu_debug::_SafeVectorGuard;
+
+#ifdef _GLIBCXX_DEBUG_PEDANTIC
+ template<typename _Tp2, typename _Alloc2>
+ friend class ::__gnu_debug::_SafeVectorPedanticGuard;
+#endif
+
+ template<typename _Tp2, typename _Alloc2>
+ friend class ::__gnu_debug::_SafeVectorInsertGuard;
typedef typename _Base::iterator _Base_iterator;
typedef typename _Base::const_iterator _Base_const_iterator;
@@ -276,12 +367,9 @@ namespace __debug
vector&
operator=(initializer_list<value_type> __l)
{
+ _AssignGuard __guard(*this);
_Base::operator=(__l);
- if (!std::__is_constant_evaluated())
- {
- this->_M_invalidate_all();
- this->_M_update_guaranteed_capacity();
- }
+ __guard._M_active = true;
return *this;
}
#endif
@@ -303,26 +391,23 @@ namespace __debug
typename __gnu_debug::_Distance_traits<_InputIterator>::__type __dist;
__glibcxx_check_valid_range2(__first, __last, __dist);
+ _AssignGuard __guard(*this);
if (__dist.second >= __gnu_debug::__dp_sign)
_Base::assign(__gnu_debug::__unsafe(__first),
__gnu_debug::__unsafe(__last));
else
_Base::assign(__first, __last);
- this->_M_invalidate_all();
- this->_M_update_guaranteed_capacity();
+ __guard._M_active = true;
}
_GLIBCXX20_CONSTEXPR
void
assign(size_type __n, const _Tp& __u)
{
+ _AssignGuard __guard(*this);
_Base::assign(__n, __u);
- if (!std::__is_constant_evaluated())
- {
- this->_M_invalidate_all();
- this->_M_update_guaranteed_capacity();
- }
+ __guard._M_active = true;
}
#if __cplusplus >= 201103L
@@ -330,12 +415,9 @@ namespace __debug
void
assign(initializer_list<value_type> __l)
{
+ _AssignGuard __guard(*this);
_Base::assign(__l);
- if (!std::__is_constant_evaluated())
- {
- this->_M_invalidate_all();
- this->_M_update_guaranteed_capacity();
- }
+ __guard._M_active = true;
}
#endif
@@ -428,13 +510,9 @@ namespace __debug
if (std::__is_constant_evaluated())
return _Base::resize(__sz);
- bool __realloc = this->_M_requires_reallocation(__sz);
- if (__sz < this->size())
- this->_M_invalidate_after_nth(__sz);
+ _ResizeGuard __guard(*this);
_Base::resize(__sz);
- if (__realloc)
- this->_M_invalidate_all();
- this->_M_update_guaranteed_capacity();
+ __guard._M_active = true;
}
_GLIBCXX20_CONSTEXPR
@@ -444,25 +522,17 @@ namespace __debug
if (std::__is_constant_evaluated())
return _Base::resize(__sz, __c);
- bool __realloc = this->_M_requires_reallocation(__sz);
- if (__sz < this->size())
- this->_M_invalidate_after_nth(__sz);
+ _ResizeGuard __guard(*this);
_Base::resize(__sz, __c);
- if (__realloc)
- this->_M_invalidate_all();
- this->_M_update_guaranteed_capacity();
+ __guard._M_active = true;
}
#else
void
resize(size_type __sz, _Tp __c = _Tp())
{
- bool __realloc = this->_M_requires_reallocation(__sz);
- if (__sz < this->size())
- this->_M_invalidate_after_nth(__sz);
+ _ResizeGuard __guard(*this);
_Base::resize(__sz, __c);
- if (__realloc)
- this->_M_invalidate_all();
- this->_M_update_guaranteed_capacity();
+ __guard._M_active = true;
}
#endif
@@ -506,7 +576,7 @@ namespace __debug
if (std::__is_constant_evaluated())
return _Base::reserve(__n);
- bool __realloc = this->_M_requires_reallocation(__n);
+ bool __realloc = __n > capacity();
_Base::reserve(__n);
if (__n > this->_M_guaranteed_capacity)
this->_M_guaranteed_capacity = __n;
@@ -583,11 +653,9 @@ namespace __debug
if (std::__is_constant_evaluated())
return _Base::push_back(__x);
- bool __realloc = this->_M_requires_reallocation(this->size() + 1);
+ _ResizeGuard __guard(*this);
_Base::push_back(__x);
- if (__realloc)
- this->_M_invalidate_all();
- this->_M_update_guaranteed_capacity();
+ __guard._M_active = true;
}
#if __cplusplus >= 201103L
@@ -610,11 +678,9 @@ namespace __debug
if (std::__is_constant_evaluated())
return _Base::emplace_back(std::forward<_Args>(__args)...);
- bool __realloc = this->_M_requires_reallocation(this->size() + 1);
+ _ResizeGuard __guard(*this);
_Base::emplace_back(std::forward<_Args>(__args)...);
- if (__realloc)
- this->_M_invalidate_all();
- this->_M_update_guaranteed_capacity();
+ __guard._M_active = true;
#if __cplusplus > 201402L
return back();
#endif
@@ -645,15 +711,14 @@ namespace __debug
this);
__glibcxx_check_insert(__position);
- bool __realloc = this->_M_requires_reallocation(this->size() + 1);
- difference_type __offset = __position.base() - _Base::cbegin();
- _Base_iterator __res = _Base::emplace(__position.base(),
- std::forward<_Args>(__args)...);
- if (__realloc)
- this->_M_invalidate_all();
- else
- this->_M_invalidate_after_nth(__offset);
- this->_M_update_guaranteed_capacity();
+ _Base_iterator __res;
+ {
+ difference_type __offset = __position.base() - _Base::cbegin();
+ _InsertGuard __guard(*this, __offset);
+ __res = _Base::emplace(__position.base(),
+ std::forward<_Args>(__args)...);
+ __guard._M_active = true;
+ }
return { __res, this };
}
#endif
@@ -670,14 +735,13 @@ namespace __debug
return iterator(_Base::insert(__position.base(), __x), this);
__glibcxx_check_insert(__position);
- bool __realloc = this->_M_requires_reallocation(this->size() + 1);
- difference_type __offset = __position.base() - _Base::begin();
- _Base_iterator __res = _Base::insert(__position.base(), __x);
- if (__realloc)
- this->_M_invalidate_all();
- else
- this->_M_invalidate_after_nth(__offset);
- this->_M_update_guaranteed_capacity();
+ _Base_iterator __res;
+ {
+ difference_type __offset = __position.base() - _Base::begin();
+ _InsertGuard __guard(*this, __offset);
+ __res = _Base::insert(__position.base(), __x);
+ __guard._M_active = true;
+ }
return iterator(__res, this);
}
@@ -704,14 +768,13 @@ namespace __debug
return iterator(_Base::insert(__position.base(), __n, __x), this);
__glibcxx_check_insert(__position);
- bool __realloc = this->_M_requires_reallocation(this->size() + __n);
- difference_type __offset = __position.base() - _Base::cbegin();
- _Base_iterator __res = _Base::insert(__position.base(), __n, __x);
- if (__realloc)
- this->_M_invalidate_all();
- else
- this->_M_invalidate_after_nth(__offset);
- this->_M_update_guaranteed_capacity();
+ _Base_iterator __res;
+ {
+ difference_type __offset = __position.base() - _Base::cbegin();
+ _InsertGuard __guard(*this, __offset);
+ __res = _Base::insert(__position.base(), __n, __x);
+ __guard._M_active = true;
+ }
return { __res, this };
}
#else
@@ -719,14 +782,10 @@ namespace __debug
insert(iterator __position, size_type __n, const _Tp& __x)
{
__glibcxx_check_insert(__position);
- bool __realloc = this->_M_requires_reallocation(this->size() + __n);
difference_type __offset = __position.base() - _Base::begin();
+ _InsertGuard __guard(*this, __offset);
_Base::insert(__position.base(), __n, __x);
- if (__realloc)
- this->_M_invalidate_all();
- else
- this->_M_invalidate_after_nth(__offset);
- this->_M_update_guaranteed_capacity();
+ __guard._M_active = true;
}
#endif
@@ -746,24 +805,19 @@ namespace __debug
typename __gnu_debug::_Distance_traits<_InputIterator>::__type __dist;
__glibcxx_check_insert_range(__position, __first, __last, __dist);
- /* Hard to guess if invalidation will occur, because __last
- - __first can't be calculated in all cases, so we just
- punt here by checking if it did occur. */
- _Base_iterator __old_begin = _M_base().begin();
- difference_type __offset = __position.base() - _Base::cbegin();
_Base_iterator __res;
- if (__dist.second >= __gnu_debug::__dp_sign)
- __res = _Base::insert(__position.base(),
- __gnu_debug::__unsafe(__first),
- __gnu_debug::__unsafe(__last));
- else
- __res = _Base::insert(__position.base(), __first, __last);
-
- if (_M_base().begin() != __old_begin)
- this->_M_invalidate_all();
- else
- this->_M_invalidate_after_nth(__offset);
- this->_M_update_guaranteed_capacity();
+ {
+ difference_type __offset = __position.base() - _Base::cbegin();
+ _InsertGuard __guard(*this, __offset);
+ if (__dist.second >= __gnu_debug::__dp_sign)
+ __res = _Base::insert(__position.base(),
+ __gnu_debug::__unsafe(__first),
+ __gnu_debug::__unsafe(__last));
+ else
+ __res = _Base::insert(__position.base(), __first, __last);
+
+ __guard._M_active = true;
+ }
return { __res, this };
}
#else
@@ -775,22 +829,15 @@ namespace __debug
typename __gnu_debug::_Distance_traits<_InputIterator>::__type __dist;
__glibcxx_check_insert_range(__position, __first, __last, __dist);
- /* Hard to guess if invalidation will occur, because __last
- - __first can't be calculated in all cases, so we just
- punt here by checking if it did occur. */
- _Base_iterator __old_begin = _M_base().begin();
difference_type __offset = __position.base() - _Base::begin();
+ _InsertGuard __guard(*this, __offset);
if (__dist.second >= __gnu_debug::__dp_sign)
_Base::insert(__position.base(), __gnu_debug::__unsafe(__first),
__gnu_debug::__unsafe(__last));
else
_Base::insert(__position.base(), __first, __last);
- if (_M_base().begin() != __old_begin)
- this->_M_invalidate_all();
- else
- this->_M_invalidate_after_nth(__offset);
- this->_M_update_guaranteed_capacity();
+ __guard._M_active = true;
}
#endif
@@ -876,31 +923,22 @@ namespace __debug
constexpr void
assign_range(_Rg&& __rg)
{
- auto __old_capacity = _Base::capacity();
- auto __old_size = _Base::size();
+ _AssignGuard __guard(*this);
_Base::assign_range(__rg);
- if (!std::__is_constant_evaluated())
- {
- if (_Base::capacity() != __old_capacity)
- this->_M_invalidate_all();
- else if (_Base::size() < __old_size)
- this->_M_invalidate_after_nth(_Base::size());
- this->_M_update_guaranteed_capacity();
- }
+ __guard._M_active = true;
}
template<__detail::__container_compatible_range<_Tp> _Rg>
constexpr iterator
insert_range(const_iterator __pos, _Rg&& __rg)
{
- auto __old_capacity = _Base::capacity();
- auto __res = _Base::insert_range(__pos.base(), __rg);
- if (!std::__is_constant_evaluated())
- {
- if (_Base::capacity() != __old_capacity)
- this->_M_invalidate_all();
- this->_M_update_guaranteed_capacity();
- }
+ _Base_iterator __res;
+ {
+ difference_type __offset = __pos.base() - _Base::cbegin();
+ _InsertGuard __guard(*this, __offset);
+ __res = _Base::insert_range(__pos.base(), __rg);
+ __guard._M_active = true;
+ }
return iterator(__res, this);
}
@@ -908,14 +946,9 @@ namespace __debug
constexpr void
append_range(_Rg&& __rg)
{
- auto __old_capacity = _Base::capacity();
+ _ResizeGuard __guard(*this);
_Base::append_range(__rg);
- if (!std::__is_constant_evaluated())
- {
- if (_Base::capacity() != __old_capacity)
- this->_M_invalidate_all();
- this->_M_update_guaranteed_capacity();
- }
+ __guard._M_active = true;
}
#endif
@@ -1057,6 +1090,76 @@ _GLIBCXX_END_NAMESPACE_VERSION
namespace __gnu_debug
{
+ template<class _Tp, typename _Allocator>
+ _GLIBCXX20_CONSTEXPR
+ _SafeVectorGuard<_Tp, _Allocator>::_SafeVectorGuard(
+ std::__debug::vector<_Tp, _Allocator>& __vector)
+ : _M_vect(__vector)
+ , _M_capacity(__vector.capacity())
+ , _M_size(__vector.size())
+ , _M_active(false)
+ { }
+
+ template<class _Tp, typename _Allocator>
+ _GLIBCXX20_CONSTEXPR
+ _SafeVectorGuard<_Tp, _Allocator>::~_SafeVectorGuard()
+ {
+ if (!std::__is_constant_evaluated() && _M_active)
+ {
+ _M_vect._M_update_guaranteed_capacity();
+
+ if (_M_vect.capacity() != _M_capacity)
+ _M_vect._M_invalidate_all();
+ else if (_M_vect.size() < _M_size)
+ _M_vect._M_invalidate_after_nth(_M_vect.size());
+ }
+ }
+
+#ifdef _GLIBCXX_DEBUG_PEDANTIC
+ template<class _Tp, typename _Allocator>
+ _GLIBCXX20_CONSTEXPR
+ _SafeVectorPedanticGuard<_Tp, _Allocator>::_SafeVectorPedanticGuard(
+ std::__debug::vector<_Tp, _Allocator>& __vector)
+ : _M_vect(__vector), _M_active(false)
+ { }
+
+ template<class _Tp, typename _Allocator>
+ _GLIBCXX20_CONSTEXPR
+ _SafeVectorPedanticGuard<_Tp, _Allocator>::~_SafeVectorPedanticGuard()
+ {
+ if (!std::__is_constant_evaluated() && _M_active)
+ {
+ _M_vect._M_update_guaranteed_capacity();
+ _M_vect._M_invalidate_all();
+ }
+ }
+#endif
+
+ template<class _Tp, typename _Allocator>
+ _GLIBCXX20_CONSTEXPR
+ _SafeVectorInsertGuard<_Tp, _Allocator>::_SafeVectorInsertGuard(
+ std::__debug::vector<_Tp, _Allocator>& __vector, difference_type __offset)
+ : _M_vect(__vector)
+ , _M_capacity(__vector.capacity())
+ , _M_offset(__offset)
+ , _M_active(false)
+ { }
+
+ template<class _Tp, typename _Allocator>
+ _GLIBCXX20_CONSTEXPR
+ _SafeVectorInsertGuard<_Tp, _Allocator>::~_SafeVectorInsertGuard()
+ {
+ if (!std::__is_constant_evaluated() && _M_active)
+ {
+ _M_vect._M_update_guaranteed_capacity();
+
+ if (_M_vect.capacity() != _M_capacity)
+ _M_vect._M_invalidate_all();
+ else
+ _M_vect._M_invalidate_after_nth(_M_offset);
+ }
+ }
+
template<typename _Tp, typename _Alloc>
struct _Is_contiguous_sequence<std::__debug::vector<_Tp, _Alloc> >
: std::__true_type
diff --git a/libstdc++-v3/testsuite/23_containers/vector/cons/destructible_debug_neg.cc b/libstdc++-v3/testsuite/23_containers/vector/cons/destructible_debug_neg.cc
index 2c9abcdbb91..ccd5dc38e33 100644
--- a/libstdc++-v3/testsuite/23_containers/vector/cons/destructible_debug_neg.cc
+++ b/libstdc++-v3/testsuite/23_containers/vector/cons/destructible_debug_neg.cc
@@ -47,4 +47,4 @@ test02()
// { dg-error "PrivateDtor.* is private" "" { target *-*-* } 0 }
// In Debug Mode the "required from here" errors come from <debug/vector>
-// { dg-error "required from here" "" { target *-*-* } 182 }
+// { dg-error "required from here" "" { target *-*-* } 273 }
diff --git a/libstdc++-v3/testsuite/23_containers/vector/debug/initializer_list.cc b/libstdc++-v3/testsuite/23_containers/vector/debug/initializer_list.cc
new file mode 100644
index 00000000000..318f6b98de9
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/vector/debug/initializer_list.cc
@@ -0,0 +1,26 @@
+// { dg-do compile { target c++11 } }
+
+#include <debug/vector>
+#include <testsuite_hooks.h>
+
+void
+test01()
+{
+ __gnu_debug::vector<int> v = { 0, 1, 2, 3, 4, 5 };
+
+ auto it1 = v.begin() + 1;
+ auto it4 = v.begin() + 4;
+
+ VERIFY( !it1._M_singular() );
+ VERIFY( !it4._M_singular() );
+
+ v = { 10, 11, 12, 13 };
+
+ VERIFY( !it1._M_singular() );
+ VERIFY( it4._M_singular() );
+}
+
+int main()
+{
+ test01();
+}
diff --git a/libstdc++-v3/testsuite/23_containers/vector/debug/initializer_list_pedantic.cc b/libstdc++-v3/testsuite/23_containers/vector/debug/initializer_list_pedantic.cc
new file mode 100644
index 00000000000..c8b3af25bd1
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/vector/debug/initializer_list_pedantic.cc
@@ -0,0 +1,27 @@
+// { dg-do compile { target c++11 } }
+// { dg-options "-D_GLIBCXX_DEBUG_PEDANTIC" }
+
+#include <debug/vector>
+#include <testsuite_hooks.h>
+
+void
+test01()
+{
+ __gnu_debug::vector<int> v = { 0, 1, 2, 3, 4, 5 };
+
+ auto it1 = v.begin() + 1;
+ auto it4 = v.begin() + 4;
+
+ VERIFY( !it1._M_singular() );
+ VERIFY( !it4._M_singular() );
+
+ v = { 10, 11, 12, 13 };
+
+ VERIFY( it1._M_singular() );
+ VERIFY( it4._M_singular() );
+}
+
+int main()
+{
+ test01();
+}
More information about the Libstdc++
mailing list