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