libstdc++: [_GLIBCXX_DEBUG] Enhance __gnu_debug::inplace_vector implementation

    Review invalidation of iterators to use a RAII approach. A guard type has been     implemented that is in charge of running an invalidation policy on destruction
    when not in a constant evaluation context.

    Invalidation of iterators on assign operation has been reviewed. All iterators     are being invalidated when using the _GLIBCXX_DEBUG_PEDANTIC mode. Otherwise     invalidate only iterators that were removed by the assignment operation.

    libstdc++-v3/ChangeLog:

            * include/debug/assertions.h (_GLIBCXX_DEBUG_STD_C): New.
            * include/debug/inplace_vector (__gnu_debug::_Inplace_vector_guard): New.
            (inplace_vector::_InvalidatePolicy): New.
            (inplace_vector::_InvalidateGuard): New.
            (inplace_vector::_InvalidateAfterNthPolicy): New.
            (inplace_vector::_InvalidateAfterNthGuard): New.
[_GLIBCXX_DEBUG_PEDANTIC](inplace_vector::_PedanticInvalidatePolicy): New.
            (inplace_vector::_AssignGuard): New.
            (inplace_vector): Adapt using latter guards.
            * testsuite/23_containers/inplace_vector/debug/initializer_list.cc: New test case.             * testsuite/23_containers/inplace_vector/debug/initializer_list_pedantic.cc:
            New test case.

Tested under Linux x64_86 _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/inplace_vector 
b/libstdc++-v3/include/debug/inplace_vector
index e579e8636f9..6ce4d006c90 100644
--- a/libstdc++-v3/include/debug/inplace_vector
+++ b/libstdc++-v3/include/debug/inplace_vector
@@ -46,6 +46,33 @@ namespace std _GLIBCXX_VISIBILITY(default) { namespace 
__debug {
 #include <debug/safe_sequence.h>
 #include <debug/safe_iterator.h>
 
+namespace __gnu_debug
+{
+  template<typename _Tp, size_t _Nm, typename _InvalidationPolicy>
+    struct _Inplace_vector_guard
+    {
+      using _SafeInplaceVector = std::__debug::inplace_vector<_Tp, _Nm>;
+      using _StdInplaceVector = _GLIBCXX_DEBUG_STD_C::inplace_vector<_Tp, _Nm>;
+      using difference_type = typename _StdInplaceVector::difference_type;
+
+      constexpr
+      _Inplace_vector_guard(_SafeInplaceVector& __vector,
+                           difference_type __offset = 0)
+      : _M_vector(__vector), _M_invalidation_policy(__vector, __offset) { }
+
+      constexpr
+      ~_Inplace_vector_guard()
+      {
+       if (!std::is_constant_evaluated())
+         _M_invalidation_policy(_M_vector);
+      }
+
+    private:
+      _SafeInplaceVector& _M_vector;
+      _InvalidationPolicy _M_invalidation_policy;
+    };
+}
+
 namespace std _GLIBCXX_VISIBILITY(default)
 {
 namespace __debug
@@ -80,6 +107,70 @@ namespace __debug
       using reverse_iterator       = std::reverse_iterator<iterator>;
       using const_reverse_iterator = std::reverse_iterator<const_iterator>;
 
+    private:
+      struct _InvalidatePolicy
+      {
+       size_type _M_size;
+       _Base_const_iterator _M_end;
+
+       constexpr
+       _InvalidatePolicy(const inplace_vector& __vector,
+                                 difference_type)
+       : _M_size(__vector.size()), _M_end(__vector._M_base().end()) { }
+
+       void
+       operator()(inplace_vector& __vector) const noexcept
+       {
+         const size_type __size = __vector.size();
+         if (__size < _M_size)
+           __vector._M_invalidate_after_nth(__size);
+         else if (__size > _M_size)
+           __vector._M_invalidate_if(_Equal(_M_end));
+       }
+      };
+
+      using _InvalidateGuard =
+       __gnu_debug::_Inplace_vector_guard<_Tp, _Nm, _InvalidatePolicy>;
+
+      struct _InvalidateAfterNthPolicy
+      {
+       size_type _M_size;
+       difference_type _M_offset;
+
+       constexpr
+       _InvalidateAfterNthPolicy(const inplace_vector& __vector,
+                                 difference_type __offset)
+       : _M_size(__vector.size()), _M_offset(__offset) { }
+
+       void
+       operator()(inplace_vector& __vector) const noexcept
+       {
+         if (__vector.size() != _M_size)
+           __vector._M_invalidate_after_nth(_M_offset);
+       }
+      };
+
+      using _InvalidateAfterNthGuard =
+       __gnu_debug::_Inplace_vector_guard<_Tp, _Nm, _InvalidateAfterNthPolicy>;
+
+#ifdef _GLIBCXX_DEBUG_PEDANTIC
+      struct _PedanticInvalidatePolicy
+      {
+       constexpr
+       _PedanticInvalidatePolicy(const inplace_vector&, difference_type) { }
+
+       void
+       operator()(inplace_vector& __vector) const noexcept
+       { __vector._M_invalidate_all(); }
+      };
+
+      using _AssignGuard =
+       __gnu_debug::_Inplace_vector_guard<_Tp, _Nm, _PedanticInvalidatePolicy>;
+#else
+      using _AssignGuard = _InvalidateGuard;
+#endif
+
+    public:
       constexpr
       inplace_vector() noexcept = default;
 
@@ -120,8 +211,8 @@ namespace __debug
       constexpr inplace_vector&
       operator=(initializer_list<_Tp> __il)
       {
+       _AssignGuard __guard(*this);
        _Base::operator=(__il);
-       this->_M_invalidate_all();
        return *this;
       }
 
@@ -129,65 +220,63 @@ namespace __debug
        constexpr void
        assign(_InputIterator __first, _InputIterator __last)
        {
+         if (std::is_constant_evaluated())
+           return _Base::assign(__gnu_debug::__unsafe(__first),
+                                __gnu_debug::__unsafe(__last));
+
          typename __gnu_debug::_Distance_traits<_InputIterator>::__type __dist;
          __glibcxx_check_valid_range2(__first, __last, __dist);
 
-         const auto __size = size();
-         const auto __end = _Base::end();
+         _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);
-
-         if (size() < __size)
-           _M_invalidate_after_nth(size());
-         else if (size() > __size)
-           this->_M_invalidate_if(_Equal(__end));
        }
 
       template<__detail::__container_compatible_range<_Tp> _Rg>
        constexpr void
        assign_range(_Rg&& __rg)
        {
+         _AssignGuard __guard(*this);
          _Base::assign_range(std::forward<_Rg>(__rg));
-         this->_M_invalidate_all();
        }
 
       constexpr void
       assign(size_type __n, const _Tp& __u)
       {
+       _AssignGuard __guard(*this);
        _Base::assign(__n, __u);
-       this->_M_invalidate_all();
       }
 
       constexpr void
       assign(initializer_list<_Tp> __il)
       {
+       _AssignGuard __guard(*this);
        _Base::assign(__il);
-       this->_M_invalidate_all();
       }
 
       // iterators
       [[nodiscard]]
       constexpr iterator
       begin() noexcept
-      { return { _Base::begin(), this }; }
+      { return iterator(_Base::begin(), this); }
 
       [[nodiscard]]
       constexpr const_iterator
       begin() const noexcept
-      { return { _Base::begin(), this }; }
+      { return const_iterator(_Base::begin(), this); }
 
       [[nodiscard]]
       constexpr iterator
       end() noexcept
-      { return { _Base::end(), this }; }
+      { return iterator(_Base::end(), this); }
 
       [[nodiscard]]
       constexpr const_iterator
       end() const noexcept
-      { return { _Base::end(), this }; }
+      { return const_iterator(_Base::end(), this); }
 
       [[nodiscard]]
       constexpr reverse_iterator
@@ -210,12 +299,12 @@ namespace __debug
       [[nodiscard]]
       constexpr const_iterator
       cbegin() const noexcept
-      { return { _Base::cbegin(), this }; }
+      { return const_iterator(_Base::cbegin(), this); }
 
       [[nodiscard]]
       constexpr const_iterator
       cend() const noexcept
-      { return { _Base::cend(), this }; }
+      { return const_iterator(_Base::cend(), this); }
 
       [[nodiscard]]
       constexpr const_reverse_iterator
@@ -233,15 +322,15 @@ namespace __debug
       constexpr void
       resize(size_type __n)
       {
+       _InvalidateGuard __guard(*this);
        _Base::resize(__n);
-       _M_invalidate_after_nth(__n);
       }
 
       constexpr void
       resize(size_type __n, const _Tp& __c)
       {
+       _InvalidateGuard __guard(*this);
        _Base::resize(__n, __c);
-       _M_invalidate_after_nth(__n);
       }
 
       using _Base::reserve;
@@ -304,10 +393,8 @@ namespace __debug
        constexpr _Tp&
        emplace_back(_Args&&... __args)
        {
-         const auto __end = _Base::cend();
-         _Tp& __res =  _Base::emplace_back(std::forward<_Args>(__args)...);
-         this->_M_invalidate_if(_Equal(__end));
-         return __res;
+         _InvalidateGuard __guard(*this);
+         return _Base::emplace_back(std::forward<_Args>(__args)...);
        }
 
       constexpr _Tp&
@@ -322,18 +409,15 @@ namespace __debug
        constexpr void
        append_range(_Rg&& __rg)
        {
-         const auto __size = size();
-         const auto __end = _Base::cend();
+         _InvalidateGuard __guard(*this);
          _Base::append_range(__rg);
-         if (size() != __size)
-           this->_M_invalidate_if(_Equal(__end));
        }
 
       constexpr void
       pop_back()
       {
        __glibcxx_check_nonempty();
-       _M_invalidate_after_nth(_Base::size() - 1);
+       _InvalidateGuard __guard(*this);
        _Base::pop_back();
       }
 
@@ -341,51 +425,30 @@ namespace __debug
        constexpr optional<_Tp&>
        try_emplace_back(_Args&&... __args)
        {
-         auto __end = _Base::cend();
-         optional<_Tp&> __res
-           = _Base::try_emplace_back(std::forward<_Args>(__args)...);
-
-         if (__res)
-           this->_M_invalidate_if(_Equal(__end));
-
-         return __res;
+         _InvalidateGuard __guard(*this);
+         return _Base::try_emplace_back(std::forward<_Args>(__args)...);
        }
 
       constexpr optional<_Tp&>
       try_push_back(const _Tp& __x)
       {
-       const auto __end = _Base::cend();
-       optional<_Tp&> __res = _Base::try_push_back(__x);
-
-       if (__res)
-         this->_M_invalidate_if(_Equal(__end));
-
-       return __res;
+       _InvalidateGuard __guard(*this);
+       return _Base::try_push_back(__x);
       }
 
       constexpr optional<_Tp&>
       try_push_back(_Tp&& __x)
       {
-       const auto __end = _Base::cend();
-       optional<_Tp&> __res = _Base::try_push_back(std::move(__x));
-
-       if (__res)
-         this->_M_invalidate_if(_Equal(__end));
-
-       return __res;
+       _InvalidateGuard __guard(*this);
+       return _Base::try_push_back(std::move(__x));
       }
 
       template<typename... _Args>
        constexpr _Tp&
        unchecked_emplace_back(_Args&&... __args)
        {
-         const auto __end = _Base::cend();
-         _Tp& __res =
-           _Base::unchecked_emplace_back(std::forward<_Args>(__args)...);
-
-         this->_M_invalidate_if(_Equal(__end));
-
-         return __res;
+         _InvalidateGuard __guard(*this);
+         return _Base::unchecked_emplace_back(std::forward<_Args>(__args)...);
        }
 
       constexpr _Tp&
@@ -407,9 +470,12 @@ namespace __debug
 
          __glibcxx_check_insert(__position);
          const difference_type __offset = __position.base() - _Base::cbegin();
-         _Base_iterator __res = _Base::emplace(__position.base(),
-                                               std::forward<_Args>(__args)...);
-         _M_invalidate_after_nth(__offset);
+         _Base_iterator __res;
+         {
+           _InvalidateAfterNthGuard __guard(*this, __offset);
+           __res = _Base::emplace(__position.base(),
+                                  std::forward<_Args>(__args)...);
+         }
          return { __res, this };
        }
 
@@ -427,11 +493,14 @@ namespace __debug
        if (std::is_constant_evaluated())
          return iterator(_Base::insert(__position.base(), __n, __x), this);
 
-         __glibcxx_check_insert(__position);
+       __glibcxx_check_insert(__position);
        const difference_type __offset = __position.base() - _Base::cbegin();
-       _Base_iterator __res = _Base::insert(__position.base(), __n, __x);
-       _M_invalidate_after_nth(__offset);
-       return { __res, this };
+       _Base_iterator __res;
+       {
+         _InvalidateAfterNthGuard __guard(*this, __offset);
+         __res = _Base::insert(__position.base(), __n, __x);
+       }
+       return iterator(__res, this);
       }
 
       template<__any_input_iterator _InputIterator>
@@ -449,14 +518,16 @@ namespace __debug
 
          const 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);
+         {
+           _InvalidateAfterNthGuard __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);
+         }
 
-         _M_invalidate_after_nth(__offset);
          return { __res, this };
        }
 
@@ -464,11 +535,12 @@ namespace __debug
        constexpr iterator
        insert_range(const_iterator __position, _Rg&& __rg)
        {
-         const auto __size = size();
          const difference_type __offset = __position.base() - _Base::cbegin();
-         auto __res = _Base::insert_range(__position.base(), __rg);
-         if (size() > __size)
-           this->_M_invalidate_after_nth(__offset);
+         _Base_iterator __res;
+         {
+           _InvalidateAfterNthGuard __guard(*this, __offset);
+           __res = _Base::insert_range(__position.base(), __rg);
+         }
 
          return iterator(__res, this);
        }
@@ -480,11 +552,13 @@ namespace __debug
          return iterator(_Base::insert(__position.base(), __il), this);
 
        __glibcxx_check_insert(__position);
-       const auto __size = size();
        difference_type __offset = __position.base() - _Base::begin();
-       _Base_iterator __res = _Base::insert(__position.base(), __il);
-       if (size() > __size)
-         this->_M_invalidate_after_nth(__offset);
+       _Base_iterator __res;
+       {
+         _InvalidateAfterNthGuard __guard(*this, __offset);
+         __res = _Base::insert(__position.base(), __il);
+       }
+
        return iterator(__res, this);
       }
 
@@ -496,8 +570,11 @@ namespace __debug
 
        __glibcxx_check_erase(__position);
        difference_type __offset = __position.base() - _Base::cbegin();
-       _Base_iterator __res = _Base::erase(__position.base());
-       this->_M_invalidate_after_nth(__offset);
+       _Base_iterator __res;
+       {
+         _InvalidateAfterNthGuard __guard(*this, __offset);
+         __res = _Base::erase(__position.base());
+       }
        return iterator(__res, this);
       }
 
@@ -509,13 +586,14 @@ namespace __debug
                          this);
 
        __glibcxx_check_erase_range(__first, __last);
-
        if (__first.base() != __last.base())
          {
            difference_type __offset = __first.base() - _Base::cbegin();
-           _Base_iterator __res = _Base::erase(__first.base(),
-                                               __last.base());
-           this->_M_invalidate_after_nth(__offset);
+           _Base_iterator __res;
+           {
+             _InvalidateAfterNthGuard __guard(*this, __offset);
+             __res = _Base::erase(__first.base(), __last.base());
+           }
            return { __res, this };
          }
        else
diff --git 
a/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/initializer_list.cc 
b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/initializer_list.cc
new file mode 100644
index 00000000000..35d2916021e
--- /dev/null
+++ 
b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/initializer_list.cc
@@ -0,0 +1,26 @@
+// { dg-do run { target c++26 } }
+
+#include <debug/inplace_vector>
+#include <testsuite_hooks.h>
+
+void
+test01()
+{
+  __gnu_debug::inplace_vector<int, 6> 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/inplace_vector/debug/initializer_list_pedantic.cc
 
b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/initializer_list_pedantic.cc
new file mode 100644
index 00000000000..060161e36f2
--- /dev/null
+++ 
b/libstdc++-v3/testsuite/23_containers/inplace_vector/debug/initializer_list_pedantic.cc
@@ -0,0 +1,27 @@
+// { dg-do run { target c++26 } }
+// { dg-options "-D_GLIBCXX_DEBUG_PEDANTIC" }
+
+#include <debug/inplace_vector>
+#include <testsuite_hooks.h>
+
+void
+test01()
+{
+  __gnu_debug::inplace_vector<int, 6> 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();
+}

Reply via email to