[PATCH][_GLIBCXX_DEBUG] Enhance __gnu_debug::vector iterator invalidation
François Dumont
frs.dumont@gmail.com
Thu Sep 3 20:24:19 GMT 2026
Gentle reminder
libstdc++: [_GLIBCXX_DEBUG] Enhance std::__debug::vector iterator
invalidation
Rework the way std::__debug::vector's iterators are being
invalidated. Use a RAII
approach so that iterators are invalidated at guard destruction
based on the state
of the container.
libstdc++-v3/ChangeLog:
* include/debug/assertions.h [_GLIBCXX_DEBUG_STD_C]: New macro.
* include/debug/vector (_Safe_vector<_Tp, _Allocator>):
Update template parameters.
(_Safe_vector::_M_seq): Remove const qualification.
(_Safe_vector::_M_requires_reallocation): Remove.
(_Safe_vector::_Guard): New.
(vector::_ResizeInvalidationPolicy): New.
(vector::_InvalidateAllPolicy): New.
(vector::_InsertInvalidationPolicy): New.
(vector::_ReallocInvalidationPolicy): New.
(vector::_ResizeGuard): New.
(vector::_AssignGuard): New.
(vector::_InsertGuard): New.
(vector::_ReallocGuard): New.
(vector): Adapt methods to use new guard types.
* testsuite/23_containers/vector/capacity/shrink_to_fit.cc:
Adapt check, size shall
be lower-or-equal to capacity, not strictly lower.
*
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/* tested under Linux x86_64
_GLIBCXX_DEBUG C++98 & default C++22.
Ok to commit ?
François
On 7/10/26 11:26, François Dumont wrote:
> Thanks for confirming Standard behavior and here is the updated patch
> then:
>
> libstdc++: [_GLIBCXX_DEBUG] Enhance std::__debug::vector iterator
> invalidation
>
> Rework the way std::__debug::vector's iterators are being
> invalidated. Use a RAII
> approach so that iterators are invalidated at guard destruction
> based on the state
> of the container.
>
> libstdc++-v3/ChangeLog:
>
> * include/debug/assertions.h [_GLIBCXX_DEBUG_STD_C]: New
> macro.
> * include/debug/vector (_Safe_vector<_Tp, _Allocator>):
> Update template parameters.
> (_Safe_vector::_M_seq): Remove const qualification.
> (_Safe_vector::_M_requires_reallocation): Remove.
> (_Safe_vector::_Guard): New.
> (vector::_ResizeInvalidationPolicy): New.
> (vector::_InvalidateAllPolicy): New.
> (vector::_InsertInvalidationPolicy): New.
> (vector::_ReallocInvalidationPolicy): New.
> (vector::_ResizeGuard): New.
> (vector::_AssignGuard): New.
> (vector::_InsertGuard): New.
> (vector::_ReallocGuard): New.
> (vector): Adapt methods to use new guard types.
> *
> testsuite/23_containers/vector/capacity/shrink_to_fit.cc: Adapt check,
> size shall
> be lower-or-equal to capacity, not strictly lower.
> *
> testsuite/23_containers/vector/cons/destructible_debug_neg.cc: Adapt
> error line
> number.
> *
> testsuite/23_containers/vector/debug/initializer_list.cc: New test case.
>
> Tested under Linux x86_64 _GLIBCXX_DEBUG, C++98 and default modes.
>
> Ok to commit ?
>
> François
>
> On 7/7/26 15:40, Jonathan Wakely wrote:
>> On Sun, 31 May 2026 at 22:17 +0200, François Dumont wrote:
>>>
>>> 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.
>>
>> I was only talking about inplace_vector, not all sequences.
>>
>> And I did raise this with LWG and I was convinced by Tim Song and
>> Tomasz that I'm wrong. The invalidation rules for inplace_vector
>> should be the same as for vector, and it makes sense to invalidate all
>> iterators on assignment.
>>
>> Tim pointed out that the objects have been overwritten, so they're not
>> the "same" elements after the assignment.
>>
>> So pointers to the old elements are still pointers to something, but
>> not the same elements. Iterators are not pointers, and should be
>> invalidated.
>>
>> Tomasz pointed out that some assignments are implemented via trivial
>> copies (i.e. memcpy) and so logically we actually replace the objects
>> with new obejcts, i.e. the lifetime of the old object ends and a new
>> object begins its lifetime in the same storage.
>>
>> So we do not want to relax the invalidation rules for assignment (this
>> applies to your inplace_vector patch as well as this one).
>>
>>
>>> 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
>>>
>>
>>> 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();
>>> +}
>>
-------------- 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..317b0fe9e25 100644
--- a/libstdc++-v3/include/debug/vector
+++ b/libstdc++-v3/include/debug/vector
@@ -51,15 +51,16 @@ namespace __gnu_debug
* detecting code which relies on non-portable implementation details of
* the libstdc++ reallocation policy.
*/
- template<typename _SafeSequence,
- typename _BaseSequence>
+ template<typename _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;
+ typedef typename _StdVector::difference_type difference_type;
- _GLIBCXX20_CONSTEXPR
- const _SafeSequence&
- _M_seq() const { return *static_cast<const _SafeSequence*>(this); }
+ _GLIBCXX20_CONSTEXPR _SafeSequence&
+ _M_seq() { return *static_cast<_SafeSequence*>(this); }
protected:
_GLIBCXX20_CONSTEXPR
@@ -91,8 +92,7 @@ namespace __gnu_debug
: _Safe_vector()
{ __x._M_guaranteed_capacity = 0; }
- _GLIBCXX20_CONSTEXPR
- _Safe_vector&
+ _GLIBCXX20_CONSTEXPR _Safe_vector&
operator=(_Safe_vector&& __x) noexcept
{
_M_update_guaranteed_capacity();
@@ -103,17 +103,37 @@ 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
+ _GLIBCXX20_CONSTEXPR void
_M_update_guaranteed_capacity() _GLIBCXX_NOEXCEPT
{
if (_M_seq().size() > _M_guaranteed_capacity)
_M_guaranteed_capacity = _M_seq().size();
}
+
+ template<typename _InvalidationPolicy>
+ struct _Guard
+ {
+ bool _M_active;
+
+ _GLIBCXX20_CONSTEXPR
+ _Guard(_Safe_vector& __vector, difference_type __offset = 0)
+ : _M_active(false), _M_vector(__vector)
+ , _M_invalidation_policy(__vector._M_seq(), __offset) { }
+
+ _GLIBCXX20_CONSTEXPR
+ ~_Guard()
+ {
+ if (!std::__is_constant_evaluated() && _M_active)
+ {
+ _M_vector._M_update_guaranteed_capacity();
+ _M_invalidation_policy(_M_vector._M_seq());
+ }
+ }
+
+ private:
+ _Safe_vector& _M_vector;
+ _InvalidationPolicy _M_invalidation_policy;
+ };
};
}
@@ -128,14 +148,93 @@ 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;
+
+ public:
+ typedef typename _Base::size_type size_type;
+ typedef typename _Base::difference_type difference_type;
+
+ private:
+ struct _ResizeInvalidationPolicy
+ {
+ size_type _M_capacity, _M_size;
+
+ _GLIBCXX20_CONSTEXPR
+ _ResizeInvalidationPolicy(const vector& __vector, difference_type)
+ : _M_capacity(__vector.capacity()), _M_size(__vector.size()) { }
+
+ void
+ operator()(vector& __vector) const _GLIBCXX_NOEXCEPT
+ {
+ if (__vector.capacity() != _M_capacity)
+ __vector._M_invalidate_all();
+ else if (__vector.size() < _M_size)
+ __vector._M_invalidate_after_nth(__vector.size());
+ }
+ };
+
+ struct _InvalidateAllPolicy
+ {
+ _GLIBCXX20_CONSTEXPR
+ _InvalidateAllPolicy(const vector&, difference_type) { }
+
+ void
+ operator()(vector& __vector) const _GLIBCXX_NOEXCEPT
+ { __vector._M_invalidate_all(); }
+ };
+
+ struct _InsertInvalidationPolicy
+ {
+ size_type _M_capacity;
+ difference_type _M_offset;
+
+ _GLIBCXX20_CONSTEXPR
+ _InsertInvalidationPolicy(const vector& __vector,
+ difference_type __offset)
+ : _M_capacity(__vector.capacity()), _M_offset(__offset) { }
+
+ void
+ operator()(vector& __vector) const _GLIBCXX_NOEXCEPT
+ {
+ if (__vector.capacity() != _M_capacity)
+ __vector._M_invalidate_all();
+ else
+ __vector._M_invalidate_after_nth(_M_offset);
+ }
+ };
+
+ struct _ReallocInvalidationPolicy
+ {
+ size_type _M_capacity;
+
+ _GLIBCXX20_CONSTEXPR
+ _ReallocInvalidationPolicy(const vector& __vector, difference_type)
+ : _M_capacity(__vector.capacity()) { }
+
+ void
+ operator()(vector& __vector) const _GLIBCXX_NOEXCEPT
+ {
+ if (__vector.capacity() != _M_capacity)
+ __vector._M_invalidate_all();
+ }
+ };
+
+ typedef typename _Safe_vector::
+ template _Guard<_ResizeInvalidationPolicy> _ResizeGuard;
+
+ typedef typename _Safe_vector::
+ template _Guard<_InvalidateAllPolicy> _AssignGuard;
+
+ typedef typename _Safe_vector::
+ template _Guard<_InsertInvalidationPolicy> _InsertGuard;
+
+ typedef typename _Safe_vector::
+ template _Guard<_ReallocInvalidationPolicy> _ReallocGuard;
typedef typename _Base::iterator _Base_iterator;
typedef typename _Base::const_iterator _Base_const_iterator;
@@ -163,9 +262,6 @@ namespace __debug
typedef __gnu_debug::_Safe_iterator<
_Base_const_iterator, vector> const_iterator;
- typedef typename _Base::size_type size_type;
- typedef typename _Base::difference_type difference_type;
-
typedef _Tp value_type;
typedef _Allocator allocator_type;
typedef typename _Base::pointer pointer;
@@ -276,12 +372,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 +396,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 +420,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 +515,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 +527,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 +581,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 +658,9 @@ namespace __debug
if (std::__is_constant_evaluated())
return _Base::push_back(__x);
- bool __realloc = this->_M_requires_reallocation(this->size() + 1);
+ _ReallocGuard __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 +683,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);
+ _ReallocGuard __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 +716,15 @@ 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;
+ {
+ // Scoped guard to avoid invalidation of the returned iterator.
+ _InsertGuard __guard(*this, __offset);
+ __res = _Base::emplace(__position.base(),
+ std::forward<_Args>(__args)...);
+ __guard._M_active = true;
+ }
return { __res, this };
}
#endif
@@ -670,14 +741,14 @@ 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;
+ {
+ // Scoped guard to avoid invalidation of the returned iterator.
+ _InsertGuard __guard(*this, __offset);
+ __res = _Base::insert(__position.base(), __x);
+ __guard._M_active = true;
+ }
return iterator(__res, this);
}
@@ -704,14 +775,14 @@ 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;
+ {
+ // Scoped guard to avoid invalidation of the returned iterator.
+ _InsertGuard __guard(*this, __offset);
+ __res = _Base::insert(__position.base(), __n, __x);
+ __guard._M_active = true;
+ }
return { __res, this };
}
#else
@@ -719,14 +790,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 +813,20 @@ 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();
+ {
+ // Scoped guard to avoid invalidation of the returned iterator.
+ _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 +838,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 +932,23 @@ 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();
- }
+ difference_type __offset = __pos.base() - _Base::cbegin();
+ _Base_iterator __res;
+ {
+ // Scoped guard to avoid invalidation of the returned iterator.
+ _InsertGuard __guard(*this, __offset);
+ __res = _Base::insert_range(__pos.base(), __rg);
+ __guard._M_active = true;
+ }
return iterator(__res, this);
}
@@ -908,14 +956,9 @@ namespace __debug
constexpr void
append_range(_Rg&& __rg)
{
- auto __old_capacity = _Base::capacity();
+ _ReallocGuard __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
diff --git a/libstdc++-v3/testsuite/23_containers/vector/capacity/shrink_to_fit.cc b/libstdc++-v3/testsuite/23_containers/vector/capacity/shrink_to_fit.cc
index d4aa4d89b56..6ca1ac8822a 100644
--- a/libstdc++-v3/testsuite/23_containers/vector/capacity/shrink_to_fit.cc
+++ b/libstdc++-v3/testsuite/23_containers/vector/capacity/shrink_to_fit.cc
@@ -28,7 +28,7 @@ void test01()
std::vector<int> v(100);
v.push_back(1);
v.push_back(1);
- VERIFY( v.size() < v.capacity() );
+ VERIFY( v.size() <= v.capacity() );
v.shrink_to_fit();
#ifdef __glibcxx_allocate_at_least
unsigned limit = __STDCPP_DEFAULT_NEW_ALIGNMENT__ / sizeof(int);
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..64b7c7c945f 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 *-*-* } 278 }
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..67146290d8a
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/vector/debug/initializer_list.cc
@@ -0,0 +1,26 @@
+// { dg-do run { 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();
+}
More information about the Libstdc++
mailing list