While working on fancy pointer support for the linked lists I noticed
they didn't have any debug assertions. This adds the obvious non-empty
assertions to front(), back(), pop_front() and pop_back().

For the pop members, adding an assertion to the underlying function that
erases a member means it also check erase(end()), which is always
invalid, and erase(begin()) on an empty list. For those erase members we
can also add a check so that we return without doing anything if the
assertion is disabled, but would have failed had it been enabled.

libstdc++-v3/ChangeLog:

        * include/bits/forward_list.h (forward_list::front): Add
        non-empty assertions.
        * include/bits/forward_list.tcc (_Fwd_list_base::_M_erase_after):
        Likewise. Return immediately if argument is invalid.
        * include/bits/stl_list.h (list::front, list::back): Add
        non-empty assertions.
        (list::_M_erase): Likewise. Return immediately if argument is
        invalid.
---

Tested x86_64-linux.

As pull request: https://forge.sourceware.org/gcc/gcc-TEST/pulls/26

 libstdc++-v3/include/bits/forward_list.h   |  3 +++
 libstdc++-v3/include/bits/forward_list.tcc |  6 ++++++
 libstdc++-v3/include/bits/stl_list.h       | 19 +++++++++++++++++--
 3 files changed, 26 insertions(+), 2 deletions(-)

diff --git a/libstdc++-v3/include/bits/forward_list.h 
b/libstdc++-v3/include/bits/forward_list.h
index c9238cef96f..3fac657518c 100644
--- a/libstdc++-v3/include/bits/forward_list.h
+++ b/libstdc++-v3/include/bits/forward_list.h
@@ -42,6 +42,7 @@
 #include <bits/allocator.h>
 #include <ext/alloc_traits.h>
 #include <ext/aligned_buffer.h>
+#include <debug/assertions.h>
 #if __glibcxx_ranges_to_container // C++ >= 23
 # include <bits/ranges_base.h> // ranges::begin, ranges::distance etc.
 # include <bits/ranges_util.h> // ranges::subrange
@@ -884,6 +885,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       reference
       front()
       {
+       __glibcxx_requires_nonempty();
        _Node* __front = static_cast<_Node*>(this->_M_impl._M_head._M_next);
        return *__front->_M_valptr();
       }
@@ -896,6 +898,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       const_reference
       front() const
       {
+       __glibcxx_requires_nonempty();
        _Node* __front = static_cast<_Node*>(this->_M_impl._M_head._M_next);
        return *__front->_M_valptr();
       }
diff --git a/libstdc++-v3/include/bits/forward_list.tcc 
b/libstdc++-v3/include/bits/forward_list.tcc
index 9750c7c0502..50acdb9f26b 100644
--- a/libstdc++-v3/include/bits/forward_list.tcc
+++ b/libstdc++-v3/include/bits/forward_list.tcc
@@ -63,6 +63,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
     _Fwd_list_base<_Tp, _Alloc>::
     _M_erase_after(_Fwd_list_node_base* __pos)
     {
+      if (__pos == nullptr || __pos->_M_next == nullptr) [[__unlikely__]]
+       {
+         __glibcxx_assert(__pos != nullptr && __pos->_M_next != nullptr);
+         return nullptr;
+       }
+
       _Node* __curr = static_cast<_Node*>(__pos->_M_next);
       __pos->_M_next = __curr->_M_next;
       _Node_alloc_traits::destroy(_M_get_Node_allocator(),
diff --git a/libstdc++-v3/include/bits/stl_list.h 
b/libstdc++-v3/include/bits/stl_list.h
index 7deb04b4bfe..d70ba90b8fa 100644
--- a/libstdc++-v3/include/bits/stl_list.h
+++ b/libstdc++-v3/include/bits/stl_list.h
@@ -59,6 +59,7 @@
 
 #include <bits/concept_check.h>
 #include <ext/alloc_traits.h>
+#include <debug/assertions.h>
 #if __cplusplus >= 201103L
 #include <initializer_list>
 #include <bits/allocated_ptr.h>
@@ -1249,7 +1250,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
       _GLIBCXX_NODISCARD
       reference
       front() _GLIBCXX_NOEXCEPT
-      { return *begin(); }
+      {
+       __glibcxx_requires_nonempty();
+       return *begin();
+      }
 
       /**
        *  Returns a read-only (constant) reference to the data at the first
@@ -1258,7 +1262,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
       _GLIBCXX_NODISCARD
       const_reference
       front() const _GLIBCXX_NOEXCEPT
-      { return *begin(); }
+      {
+       __glibcxx_requires_nonempty();
+       return *begin();
+      }
 
       /**
        *  Returns a read/write reference to the data at the last element
@@ -1268,6 +1275,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
       reference
       back() _GLIBCXX_NOEXCEPT
       {
+       __glibcxx_requires_nonempty();
        iterator __tmp = end();
        --__tmp;
        return *__tmp;
@@ -1281,6 +1289,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
       const_reference
       back() const _GLIBCXX_NOEXCEPT
       {
+       __glibcxx_requires_nonempty();
        const_iterator __tmp = end();
        --__tmp;
        return *__tmp;
@@ -2132,6 +2141,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
       void
       _M_erase(iterator __position) _GLIBCXX_NOEXCEPT
       {
+       if (__builtin_expect(empty(), 0))
+         {
+           __glibcxx_requires_nonempty();
+           return;
+         }
+
        this->_M_dec_size(1);
        __position._M_node->_M_unhook();
        _Node* __n = static_cast<_Node*>(__position._M_node);
-- 
2.47.0

Reply via email to