[PATCH][_GLIBCXX_DEBUG] Enhance __gnu_debug::vector iterator invalidation

Jonathan Wakely jwakely@redhat.com
Tue Jul 7 13:40:32 GMT 2026


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();
>+}



More information about the Libstdc++ mailing list