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:HiFollowing 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 invalidationRework 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 linenumber.* 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çoisdiff --git a/libstdc++-v3/include/debug/assertions.h b/libstdc++-v3/include/debug/assertions.hindex 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/vectorindex 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 __debugtypename __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 __debugtypename __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 __debugtypename __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) +#endifI 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) +#endifSame 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();
+}
