Currently std::list uses raw pointers to connect its nodes, which is
non-conforming. We should use the allocator's pointer type everywhere
that a "pointer" is needed.

Because the existing types like _List_node<T> are part of the ABI now,
we can't change them. To support nodes that are connected by fancy
pointers we need a parallel hierarchy of node types. This change
introduces new class templates parameterized on the allocator's
void_pointer type, __list::_Node_base and __list::_Node_header, and new
class templates parameterized on the allocator's pointer type,
__list::Node, __list::_Iterator. The iterator class template is used for
both iterator and const_iterator. Whether std::list<T, A> should use the
old _List_node<T> or new _list::_Node<A::pointer> type family internally
is controlled by a new __list::_Node_traits traits template.

Because std::pointer_traits and std::__to_address are not defined for
C++98, there is no way to support fancy pointers in C++98. For C++98 the
_Node_traits traits always choose the old _List_node family.

In case anybody is currently using std::list with an allocator that has
a fancy pointer, this change would be an ABI break, because their
std::list instantiations would start to (correctly) use the fancy
pointer type. If the fancy pointer just contains a single pointer and so
has the same size, layout, and object represenation as a raw pointer,
the code might still work (despite being an ODR violation). But if their
fancy pointer has a different representation, they would need to
recompile all their code using that allocator with std::list. Because
std::list will never use fancy pointers in C++98 mode, recompiling
everything to use fancy pointers isn't even possible if mixing C++98 and
C++11 code that uses std::list. To alleviate this problem, compiling
with -D_GLIBCXX_LIST_USE_ALLOC_PTR=0 will force std::list to have the
old, non-conforming behaviour and use raw pointers internally. This
macro is currently undocumented, which needs to be fixed.

Pretty printers for std::list need to be updated to handle the new node
types.

libstdc++-v3/ChangeLog:

        PR libstdc++/57272
        PR libstdc++/110952
        * include/bits/list.tcc (_List_base::_M_clear()): Replace uses
        of raw pointers.
        (list::emplace, list::insert): Likewise.
        (list::sort): Adjust check for 0 or 1 wsize. Use template
        argument list for _Scratch_list.
        * include/bits/stl_list.h (_GLIBCXX_LIST_USE_ALLOC_PTR): Define.
        (_List_node_base::_Base_ptr): New typedef.
        (_List_node_base::_M_base): New member functions.
        (_List_node_header::_M_base): Make public and add
        using-declaration for base class overload.
        (__list::_Node_traits, __list::_Node_base)
        (__list::_Node_header, __list::_Node, __list::_Iterator): New
        class templates.
        (_Scratch_list): Turn class into class template. Use _Base_ptr
        typedef instead of _List_node_base*.
        (_List_node::_Node_ptr): New typedef.
        (_List_node::_M_node_ptr): New member function.
        (_List_base, _List_impl): Use _Node_traits to get node types.
        (_List_base::_M_get_node, _List_base::_M_put_node): Convert
        to/from _List_node<T>* if required.
        (_List_base(_List_base&&, _Node_alloc_type&&)): Use if constexpr
        to make function a no-op for fancy pointers.
        (_List_base::_S_distance, _List_base::_M_distance)
        (_List_base::_M_node_count): Likewise.
        (list): Use _Node_traits to get iterator, node and pointer
        types.
        (list::_M_create_node): Use _Node_ptr typedef instead of _Node*.
        (list::end, list::cend, list::empty): Use node header's
        _M_base() function instead of taking its address.
        (list::swap): Use _Node_traits to get node base type.
        (list::_M_create_node, list::_M_insert, list::_M_erase): Use
        _Node_ptr typedef instead of _Node*.
        (__distance): Overload for __list::_Iterator.
        (_Node_base::swap, _Node_base::_M_transfer): Define non-inline
        member functions of class templates.
        (_Node_header::_M_reverse): Likewise.
        * 
testsuite/23_containers/list/requirements/explicit_instantiation/alloc_ptr.cc:
        New test.
        * 
testsuite/23_containers/list/requirements/explicit_instantiation/alloc_ptr_ignored.cc:
        New test.
---

Tested x86_64-linux.

Pull request at https://forge.sourceware.org/gcc/gcc-TEST/pulls/25

 libstdc++-v3/include/bits/list.tcc            |  42 +-
 libstdc++-v3/include/bits/stl_list.h          | 552 ++++++++++++++++--
 .../explicit_instantiation/alloc_ptr.cc       |  76 +++
 .../alloc_ptr_ignored.cc                      |   4 +
 4 files changed, 608 insertions(+), 66 deletions(-)
 create mode 100644 
libstdc++-v3/testsuite/23_containers/list/requirements/explicit_instantiation/alloc_ptr.cc
 create mode 100644 
libstdc++-v3/testsuite/23_containers/list/requirements/explicit_instantiation/alloc_ptr_ignored.cc

diff --git a/libstdc++-v3/include/bits/list.tcc 
b/libstdc++-v3/include/bits/list.tcc
index 65835c1379f..58892f78c8f 100644
--- a/libstdc++-v3/include/bits/list.tcc
+++ b/libstdc++-v3/include/bits/list.tcc
@@ -66,11 +66,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
     _List_base<_Tp, _Alloc>::
     _M_clear() _GLIBCXX_NOEXCEPT
     {
-      typedef _List_node<_Tp>  _Node;
-      __detail::_List_node_base* __cur = _M_impl._M_node._M_next;
-      while (__cur != &_M_impl._M_node)
+      typedef typename _Node_traits::_Node  _Node;
+      typedef typename _Node::_Node_ptr  _Node_ptr;
+      typename _Node::_Base_ptr __cur = _M_impl._M_node._M_next;
+      while (__cur != _M_impl._M_node._M_base())
        {
-         _Node* __tmp = static_cast<_Node*>(__cur);
+         _Node_ptr __tmp = static_cast<_Node&>(*__cur)._M_node_ptr();
          __cur = __tmp->_M_next;
          _Tp* __val = __tmp->_M_valptr();
 #if __cplusplus >= 201103L
@@ -89,10 +90,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       list<_Tp, _Alloc>::
       emplace(const_iterator __position, _Args&&... __args)
       {
-       _Node* __tmp = _M_create_node(std::forward<_Args>(__args)...);
+       _Node_ptr __tmp = _M_create_node(std::forward<_Args>(__args)...);
        __tmp->_M_hook(__position._M_const_cast()._M_node);
        this->_M_inc_size(1);
-       return iterator(__tmp);
+       return iterator(__tmp->_M_base());
       }
 #endif
 
@@ -105,10 +106,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
     insert(iterator __position, const value_type& __x)
 #endif
     {
-      _Node* __tmp = _M_create_node(__x);
+      _Node_ptr __tmp = _M_create_node(__x);
       __tmp->_M_hook(__position._M_const_cast()._M_node);
       this->_M_inc_size(1);
-      return iterator(__tmp);
+      return iterator(__tmp->_M_base());
     }
 
 #if __cplusplus >= 201103L
@@ -482,10 +483,13 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
     sort()
     {
       // Do nothing if the list has length 0 or 1.
-      if (this->_M_impl._M_node._M_next != &this->_M_impl._M_node
-         && this->_M_impl._M_node._M_next->_M_next != &this->_M_impl._M_node)
+      if (empty() || ++begin() == end())
+       return;
+
       {
-       using __detail::_Scratch_list;
+       typedef __list::_Scratch_list<typename _Node_traits::_Node_base>
+         _Scratch_list;
+
        // The algorithm used here is largely unchanged from the SGI STL
        // and is described in The C++ Standard Template Library by Plauger,
        // Stepanov, Lee, Musser.
@@ -499,7 +503,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
        _Scratch_list* __fill = __tmp;
        _Scratch_list* __counter;
 
-       _Scratch_list::_Ptr_cmp<iterator, void> __ptr_comp;
+       typename _Scratch_list::template _Ptr_cmp<iterator, void> __ptr_comp;
 
        __try
          {
@@ -614,17 +618,21 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       sort(_StrictWeakOrdering __comp)
       {
        // Do nothing if the list has length 0 or 1.
-       if (this->_M_impl._M_node._M_next != &this->_M_impl._M_node
-           && this->_M_impl._M_node._M_next->_M_next != &this->_M_impl._M_node)
+       if (empty() || ++begin() == end())
+         return;
+
        {
-         using __detail::_Scratch_list;
+         typedef __list::_Scratch_list<typename _Node_traits::_Node_base>
+           _Scratch_list;
+
          _Scratch_list __carry;
          _Scratch_list __tmp[64];
          _Scratch_list* __fill = __tmp;
          _Scratch_list* __counter;
 
-       _Scratch_list::_Ptr_cmp<iterator, _StrictWeakOrdering> __ptr_comp
-         = { __comp };
+         typename _Scratch_list::
+           template _Ptr_cmp<iterator, _StrictWeakOrdering> __ptr_comp
+           = { __comp };
 
          __try
            {
diff --git a/libstdc++-v3/include/bits/stl_list.h 
b/libstdc++-v3/include/bits/stl_list.h
index b1ab335ba4c..4628c782db6 100644
--- a/libstdc++-v3/include/bits/stl_list.h
+++ b/libstdc++-v3/include/bits/stl_list.h
@@ -62,6 +62,7 @@
 #if __cplusplus >= 201103L
 #include <initializer_list>
 #include <bits/allocated_ptr.h>
+#include <bits/ptr_traits.h>
 #include <ext/aligned_buffer.h>
 #endif
 #if __glibcxx_ranges_to_container // C++ >= 23
@@ -69,6 +70,13 @@
 # include <bits/ranges_util.h> // ranges::subrange
 #endif
 
+#if __cplusplus < 201103L
+#undef _GLIBCXX_LIST_USE_ALLOC_PTR
+#define _GLIBCXX_LIST_USE_ALLOC_PTR 0
+#elif ! defined _GLIBCXX_LIST_USE_ALLOC_PTR
+#define _GLIBCXX_LIST_USE_ALLOC_PTR 1
+#endif
+
 namespace std _GLIBCXX_VISIBILITY(default)
 {
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
@@ -84,6 +92,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     /// Common part of a node in the %list.
     struct _List_node_base
     {
+      typedef _List_node_base* _Base_ptr;
+
       _List_node_base* _M_next;
       _List_node_base* _M_prev;
 
@@ -102,6 +112,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       void
       _M_unhook() _GLIBCXX_USE_NOEXCEPT;
+
+      _List_node_base* _M_base() { return this; }
+      const _List_node_base* _M_base() const { return this; }
     };
 
     struct _List_size
@@ -112,7 +125,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
     };
 
-
     /// The %list node header.
     struct _List_node_header : public _List_node_base, _List_size
     {
@@ -157,18 +169,302 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        _List_size::operator=(_List_size());
       }
 
-    private:
-      _List_node_base* _M_base() { return this; }
+      using _List_node_base::_M_base;
+#if ! _GLIBCXX_INLINE_VERSION
+      _List_node_base* _M_base() { return this; } // XXX GLIBCXX_ABI Deprecated
+#endif
     };
 
-    // Used by list::sort to hold nodes being sorted.
-    struct _Scratch_list : _List_node_base
+  } // namespace detail
+
+_GLIBCXX_BEGIN_NAMESPACE_CONTAINER
+  template<typename _Tp> struct _List_node;
+  template<typename _Tp> struct _List_iterator;
+  template<typename _Tp> struct _List_const_iterator;
+_GLIBCXX_BEGIN_NAMESPACE_CXX11
+  template<typename _Tp, typename _Allocator> class list;
+_GLIBCXX_END_NAMESPACE_CXX11
+_GLIBCXX_END_NAMESPACE_CONTAINER
+
+namespace __list
+{
+  // Determine the node and iterator types used by std::list.
+  template<typename _Tp, typename _Ptr>
+    struct _Node_traits;
+
+  // Specialization for the simple case where the allocator's pointer type
+  // is the same type as value_type*.
+  // For ABI compatibility we can't change the types used for this case.
+  template<typename _Tp>
+    struct _Node_traits<_Tp, _Tp*>
     {
-      _Scratch_list() { _M_next = _M_prev = this; }
+      typedef __detail::_List_node_base                        _Node_base;
+      typedef __detail::_List_node_header              _Node_header;
+      typedef _GLIBCXX_STD_C::_List_node<_Tp>          _Node;
+      typedef _GLIBCXX_STD_C::_List_iterator<_Tp>      _Iterator;
+      typedef _GLIBCXX_STD_C::_List_const_iterator<_Tp>        _Const_iterator;
+    };
 
-      bool empty() const { return _M_next == this; }
+#if ! _GLIBCXX_LIST_USE_ALLOC_PTR
+  // Always use the T* specialization.
+  template<typename _Tp, typename _Ptr>
+    struct _Node_traits
+    : _Node_traits<_Tp, _Tp*>
+    { };
+#else
 
-      void swap(_List_node_base& __l) { _List_node_base::swap(*this, __l); }
+  // The base class for a list node.  Contains the pointers connecting nodes.
+  template<typename _VoidPtr>
+    struct _Node_base
+    {
+      using _Base_ptr = __ptr_rebind<_VoidPtr, _Node_base>;
+      _Base_ptr _M_next;
+      _Base_ptr _M_prev;
+
+      static void
+      swap(_Node_base& __x, _Node_base& __y) noexcept;
+
+      void
+      _M_transfer(_Base_ptr const __first, _Base_ptr const __last);
+
+      void
+      _M_hook(_Base_ptr const __position) noexcept
+      {
+       auto __self = pointer_traits<_Base_ptr>::pointer_to(*this);
+       this->_M_next = __position;
+       this->_M_prev = __position->_M_prev;
+       __position->_M_prev->_M_next = __self;
+       __position->_M_prev = __self;
+      }
+
+      void
+      _M_unhook() noexcept
+      {
+       auto const __next_node = this->_M_next;
+       auto const __prev_node = this->_M_prev;
+       __prev_node->_M_next = __next_node;
+       __next_node->_M_prev = __prev_node;
+      }
+
+      // This is not const-correct, but it's only used in a const access path
+      // by std::list::empty(), where it doesn't escape, and by
+      // std::list::end() const, where the pointer is used to initialize a
+      // const_iterator and so constness is restored.
+      _Base_ptr
+      _M_base() const noexcept
+      {
+       return pointer_traits<_Base_ptr>::
+                pointer_to(const_cast<_Node_base&>(*this));
+      }
+    };
+
+  using ::std::__detail::_List_size;
+
+  // The special sentinel node contained by a std::list.
+  // begin()->_M_node->_M_prev and end()->_M_node point to this header.
+  // This is not a complete node, as it doesn't contain a value.
+  template<typename _VoidPtr>
+    struct _Node_header
+    : public _Node_base<_VoidPtr>, _List_size
+    {
+      _Node_header() noexcept
+      { _M_init(); }
+
+      _Node_header(_Node_header&& __x) noexcept
+      : _Node_base<_VoidPtr>(__x), _List_size(__x)
+      {
+       if (__x._M_base()->_M_next == __x._M_base())
+         this->_M_next = this->_M_prev = this->_M_base();
+       else
+         {
+           this->_M_next->_M_prev = this->_M_prev->_M_next = this->_M_base();
+           __x._M_init();
+         }
+      }
+
+      void
+      _M_move_nodes(_Node_header&& __x) noexcept
+      {
+       auto const __xnode = __x._M_base();
+       if (__xnode->_M_next == __xnode)
+         _M_init();
+       else
+         {
+           auto const __node = this->_M_base();
+           __node->_M_next = __xnode->_M_next;
+           __node->_M_prev = __xnode->_M_prev;
+           __node->_M_next->_M_prev = __node->_M_prev->_M_next = __node;
+           _List_size::operator=(__x);
+           __x._M_init();
+         }
+      }
+
+      void
+      _M_init() noexcept
+      {
+       this->_M_next = this->_M_prev = this->_M_base();
+       _List_size::operator=(_List_size());
+      }
+
+      void _M_reverse() noexcept;
+    };
+
+  // The node type used for allocators with fancy pointers.
+  template<typename _ValPtr>
+    struct _Node : public __list::_Node_base<__ptr_rebind<_ValPtr, void>>
+    {
+      using value_type = typename pointer_traits<_ValPtr>::element_type;
+      using _Node_ptr = __ptr_rebind<_ValPtr, _Node>;
+
+      union {
+       value_type _M_data;
+      };
+
+      _Node() { }
+      ~_Node() { }
+      _Node(_Node&&) = delete;
+
+      value_type*       _M_valptr()       { return std::__addressof(_M_data); }
+      value_type const* _M_valptr() const { return std::__addressof(_M_data); }
+
+      _Node_ptr
+      _M_node_ptr()
+      { return pointer_traits<_Node_ptr>::pointer_to(*this); }
+    };
+
+  template<bool _Const, typename _Ptr> class _Iterator;
+
+  // Primary template used when the allocator uses fancy pointers.
+  template<typename _Tp, typename _Ptr>
+    struct _Node_traits
+    {
+    private:
+      using _VoidPtr = __ptr_rebind<_Ptr, void>;
+      using _ValPtr = __ptr_rebind<_Ptr, _Tp>;
+
+    public:
+      using _Node_base      = __list::_Node_base<_VoidPtr>;
+      using _Node_header    = __list::_Node_header<_VoidPtr>;
+      using _Node           = __list::_Node<_ValPtr>;
+      using _Iterator       = __list::_Iterator<false, _ValPtr>;
+      using _Const_iterator = __list::_Iterator<true, _ValPtr>;
+    };
+
+  template<bool _Const, typename _Ptr>
+    class _Iterator
+    {
+      using _Node     = __list::_Node<_Ptr>;
+      using _Base_ptr = typename _Node::_Base_ptr;
+
+      template<typename _Tp>
+       using __maybe_const = __conditional_t<_Const, const _Tp, _Tp>;
+
+    public:
+      using value_type        = typename pointer_traits<_Ptr>::element_type;
+      using difference_type   = ptrdiff_t;
+      using iterator_category = bidirectional_iterator_tag;
+      using pointer           = __maybe_const<value_type>*;
+      using reference         = __maybe_const<value_type>&;
+
+      constexpr _Iterator() noexcept : _M_node() { }
+
+      _Iterator(const _Iterator&) = default;
+      _Iterator& operator=(const _Iterator&) = default;
+
+#ifdef __glibcxx_concepts
+      constexpr
+      _Iterator(const _Iterator<false, _Ptr>& __i) requires _Const
+#else
+      template<bool _OtherConst,
+              typename = __enable_if_t<_Const && !_OtherConst>>
+       _Iterator(const _Iterator<_OtherConst, _Ptr>& __i)
+#endif
+       : _M_node(__i._M_node) { }
+
+      constexpr explicit
+      _Iterator(_Base_ptr __x) noexcept
+      : _M_node(__x) { }
+
+      // Must downcast from _Node_base to _Node to get to value.
+      [[__nodiscard__]]
+      constexpr reference
+      operator*() const noexcept
+      { return static_cast<_Node&>(*_M_node)._M_data; }
+
+      [[__nodiscard__]]
+      constexpr pointer
+      operator->() const noexcept
+      { return std::__addressof(operator*()); }
+
+      _GLIBCXX14_CONSTEXPR _Iterator&
+      operator++() noexcept
+      {
+       _M_node = _M_node->_M_next;
+       return *this;
+      }
+
+      _GLIBCXX14_CONSTEXPR _Iterator
+      operator++(int) noexcept
+      {
+       auto __tmp = *this;
+       _M_node = _M_node->_M_next;
+       return __tmp;
+      }
+
+      _GLIBCXX14_CONSTEXPR _Iterator&
+      operator--() noexcept
+      {
+       _M_node = _M_node->_M_prev;
+       return *this;
+      }
+
+      _GLIBCXX14_CONSTEXPR _Iterator
+      operator--(int) noexcept
+      {
+       auto __tmp = *this;
+       _M_node = _M_node->_M_prev;
+       return __tmp;
+      }
+
+      [[__nodiscard__]]
+      friend constexpr bool
+      operator==(const _Iterator& __x, const _Iterator& __y) noexcept
+      { return __x._M_node == __y._M_node; }
+
+#if __cpp_impl_three_way_comparison < 201907L
+      [[__nodiscard__]]
+      friend constexpr bool
+      operator!=(const _Iterator& __x, const _Iterator& __y) noexcept
+      { return __x._M_node != __y._M_node; }
+#endif
+
+    private:
+      template<typename _Tp, typename _Allocator>
+       friend class _GLIBCXX_STD_C::list;
+
+      constexpr _Iterator<false, _Ptr>
+      _M_const_cast() const noexcept
+      { return _Iterator<false, _Ptr>(_M_node); }
+
+      friend _Iterator<!_Const, _Ptr>;
+
+      _Base_ptr _M_node;
+    };
+#endif // LIST_USE_ALLOC_PTR
+
+  // Used by std::list::sort to hold nodes being sorted.
+  template<typename _NodeBaseT>
+    struct _Scratch_list
+    : _NodeBaseT
+    {
+      typedef _NodeBaseT _Base;
+      typedef typename _Base::_Base_ptr _Base_ptr;
+
+      _Scratch_list() { this->_M_next = this->_M_prev = this->_M_base(); }
+
+      bool empty() const { return this->_M_next == this->_M_base(); }
+
+      void swap(_Base& __l) { _Base::swap(*this, __l); }
 
       template<typename _Iter, typename _Cmp>
        struct _Ptr_cmp
@@ -176,8 +472,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
          _Cmp _M_cmp;
 
          bool
-         operator()(__detail::_List_node_base* __lhs,
-                    __detail::_List_node_base* __rhs) /* not const */
+         operator()(_Base_ptr __lhs, _Base_ptr __rhs) /* not const */
          { return _M_cmp(*_Iter(__lhs), *_Iter(__rhs)); }
        };
 
@@ -185,26 +480,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        struct _Ptr_cmp<_Iter, void>
        {
          bool
-         operator()(__detail::_List_node_base* __lhs,
-                    __detail::_List_node_base* __rhs) const
+         operator()(_Base_ptr __lhs, _Base_ptr __rhs) const
          { return *_Iter(__lhs) < *_Iter(__rhs); }
        };
 
       // Merge nodes from __x into *this. Both lists must be sorted wrt _Cmp.
       template<typename _Cmp>
        void
-       merge(_List_node_base& __x, _Cmp __comp)
+       merge(_Base& __x, _Cmp __comp)
        {
-         _List_node_base* __first1 = _M_next;
-         _List_node_base* const __last1 = this;
-         _List_node_base* __first2 = __x._M_next;
-         _List_node_base* const __last2 = std::__addressof(__x);
+         _Base_ptr __first1 = this->_M_next;
+         _Base_ptr const __last1 = this->_M_base();
+         _Base_ptr __first2 = __x._M_next;
+         _Base_ptr const __last2 = __x._M_base();
 
          while (__first1 != __last1 && __first2 != __last2)
            {
              if (__comp(__first2, __first1))
                {
-                 _List_node_base* __next = __first2->_M_next;
+                 _Base_ptr __next = __first2->_M_next;
                  __first1->_M_transfer(__first2, __next);
                  __first2 = __next;
                }
@@ -216,18 +510,19 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        }
 
       // Splice the node at __i into *this.
-      void _M_take_one(_List_node_base* __i)
+      void _M_take_one(_Base_ptr __i)
       { this->_M_transfer(__i, __i->_M_next); }
 
       // Splice all nodes from *this after __i.
-      void _M_put_all(_List_node_base* __i)
+      void _M_put_all(_Base_ptr __i)
       {
        if (!empty())
-         __i->_M_transfer(_M_next, this);
+         __i->_M_transfer(this->_M_next, this->_M_base());
       }
     };
 
-  } // namespace detail
+} // namespace __list
+
 
 _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 
@@ -235,6 +530,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
   template<typename _Tp>
     struct _List_node : public __detail::_List_node_base
     {
+      typedef _List_node* _Node_ptr;
+
 #if __cplusplus >= 201103L
       __gnu_cxx::__aligned_membuf<_Tp> _M_storage;
       _Tp*       _M_valptr()       { return _M_storage._M_ptr(); }
@@ -244,6 +541,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       _Tp*       _M_valptr()       { return std::__addressof(_M_data); }
       _Tp const* _M_valptr() const { return std::__addressof(_M_data); }
 #endif
+
+      _Node_ptr _M_node_ptr() { return this; }
     };
 
   /**
@@ -432,14 +731,17 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
       typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template
        rebind<_Tp>::other                              _Tp_alloc_type;
       typedef __gnu_cxx::__alloc_traits<_Tp_alloc_type>        
_Tp_alloc_traits;
+
+      typedef __list::_Node_traits<_Tp, typename _Tp_alloc_traits::pointer>
+       _Node_traits;
       typedef typename _Tp_alloc_traits::template
-       rebind<_List_node<_Tp> >::other _Node_alloc_type;
+       rebind<typename _Node_traits::_Node>::other _Node_alloc_type;
       typedef __gnu_cxx::__alloc_traits<_Node_alloc_type> _Node_alloc_traits;
 
       struct _List_impl
       : public _Node_alloc_type
       {
-       __detail::_List_node_header _M_node;
+       typename _Node_traits::_Node_header _M_node;
 
        _List_impl() _GLIBCXX_NOEXCEPT_IF(
            is_nothrow_default_constructible<_Node_alloc_type>::value)
@@ -481,6 +783,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
       void _M_dec_size(size_t) { }
 #endif
 
+#if __cplusplus < 201103L || _GLIBCXX_LIST_USE_ALLOC_PTR
       typename _Node_alloc_traits::pointer
       _M_get_node()
       { return _Node_alloc_traits::allocate(_M_impl, 1); }
@@ -488,6 +791,22 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
       void
       _M_put_node(typename _Node_alloc_traits::pointer __p) _GLIBCXX_NOEXCEPT
       { _Node_alloc_traits::deallocate(_M_impl, __p, 1); }
+#else
+      // When not using the allocator's pointer type internally we must
+      // convert between _Node_ptr (i.e. _List_node*) and the allocator's
+      // pointer type. We do that here.
+      _List_node<_Tp>*
+      _M_get_node()
+      { return std::__to_address(_Node_alloc_traits::allocate(_M_impl, 1)); }
+
+      void
+      _M_put_node(_List_node<_Tp>* __p)
+      {
+       using __alloc_pointer = typename _Node_alloc_traits::pointer;
+       auto __ap = pointer_traits<__alloc_pointer>::pointer_to(*__p);
+       _Node_alloc_traits::deallocate(_M_impl, __ap, 1);
+      }
+#endif
 
   public:
       typedef _Alloc allocator_type;
@@ -547,13 +866,24 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
       // so that explicit instantiation declarations of std::list that were
       // compiled with old versions of GCC can still find these old symbols.
 
+      // Use 'if constexpr' so that the functions don't do anything for
+      // specializations using _Node_traits<T, fancy-pointer>, because any
+      // old code referencing these symbols wasn't using the fancy-pointer
+      // specializations.
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
+
 # if __cplusplus >= 201103L
       _List_base(_List_base&& __x, _Node_alloc_type&& __a)
       : _M_impl(std::move(__a))
       {
-       if (__x._M_get_Node_allocator() == _M_get_Node_allocator())
-         _M_move_nodes(std::move(__x));
-       // else caller must move individual elements.
+#if _GLIBCXX_LIST_USE_ALLOC_PTR
+       if constexpr (is_same<typename _Tp_alloc_traits::pointer, _Tp*>::value)
+#endif
+         if (__x._M_get_Node_allocator() == _M_get_Node_allocator())
+           _M_move_nodes(std::move(__x));
+         // else caller must move individual elements.
       }
 # endif
 
@@ -561,13 +891,20 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
       _S_distance(const __detail::_List_node_base* __first,
                  const __detail::_List_node_base* __last)
       {
-       size_t __n = 0;
-       while (__first != __last)
+#if _GLIBCXX_LIST_USE_ALLOC_PTR
+       if constexpr (!is_same<typename _Tp_alloc_traits::pointer, _Tp*>::value)
+         return 0;
+       else
+#endif
          {
-           __first = __first->_M_next;
-           ++__n;
+           size_t __n = 0;
+           while (__first != __last)
+             {
+               __first = __first->_M_next;
+               ++__n;
+             }
+           return __n;
          }
-       return __n;
       }
 
 #if _GLIBCXX_USE_CXX11_ABI
@@ -584,10 +921,16 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
       // count the number of nodes
       size_t _M_node_count() const
       {
-       return _S_distance(_M_impl._M_node._M_next,
-                          std::__addressof(_M_impl._M_node));
+#if _GLIBCXX_LIST_USE_ALLOC_PTR
+       if constexpr (!is_same<typename _Tp_alloc_traits::pointer, _Tp*>::value)
+         return 0;
+       else
+#endif
+         return _S_distance(_M_impl._M_node._M_next,
+                            _M_impl._M_node._M_base());
       }
 #endif
+#pragma GCC diagnostic pop
 #endif // ! INLINE_VERSION
     };
 
@@ -663,6 +1006,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
       typedef typename _Base::_Tp_alloc_traits         _Tp_alloc_traits;
       typedef typename _Base::_Node_alloc_type         _Node_alloc_type;
       typedef typename _Base::_Node_alloc_traits       _Node_alloc_traits;
+      typedef typename _Base::_Node_traits             _Node_traits;
 
     public:
       typedef _Tp                                       value_type;
@@ -670,8 +1014,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
       typedef typename _Tp_alloc_traits::const_pointer  const_pointer;
       typedef typename _Tp_alloc_traits::reference      reference;
       typedef typename _Tp_alloc_traits::const_reference const_reference;
-      typedef _List_iterator<_Tp>                       iterator;
-      typedef _List_const_iterator<_Tp>                         const_iterator;
+      typedef typename _Node_traits::_Iterator          iterator;
+      typedef typename _Node_traits::_Const_iterator    const_iterator;
       typedef std::reverse_iterator<const_iterator>     const_reverse_iterator;
       typedef std::reverse_iterator<iterator>           reverse_iterator;
       typedef size_t                                    size_type;
@@ -681,7 +1025,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
     protected:
       // Note that pointers-to-_Node's can be ctor-converted to
       // iterator types.
-      typedef _List_node<_Tp>                           _Node;
+      typedef typename _Node_traits::_Node              _Node;
+      typedef typename _Node::_Node_ptr                         _Node_ptr;
 
       using _Base::_M_impl;
       using _Base::_M_put_node;
@@ -695,10 +1040,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
        *  @a __args in it.
        */
 #if __cplusplus < 201103L
-      _Node*
+      _Node_ptr
       _M_create_node(const value_type& __x)
       {
-       _Node* __p = this->_M_get_node();
+       _Node_ptr __p = this->_M_get_node();
        __try
          {
            _Tp_alloc_type __alloc(_M_get_Node_allocator());
@@ -713,7 +1058,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
       }
 #else
       template<typename... _Args>
-       _Node*
+       _Node_ptr
        _M_create_node(_Args&&... __args)
        {
          auto __p = this->_M_get_node();
@@ -1070,7 +1415,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
       _GLIBCXX_NODISCARD
       iterator
       end() _GLIBCXX_NOEXCEPT
-      { return iterator(&this->_M_impl._M_node); }
+      { return iterator(this->_M_impl._M_node._M_base()); }
 
       /**
        *  Returns a read-only (constant) iterator that points one past
@@ -1080,7 +1425,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
       _GLIBCXX_NODISCARD
       const_iterator
       end() const _GLIBCXX_NOEXCEPT
-      { return const_iterator(&this->_M_impl._M_node); }
+      { return const_iterator(this->_M_impl._M_node._M_base()); }
 
       /**
        *  Returns a read/write reverse iterator that points to the last
@@ -1141,7 +1486,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
       [[__nodiscard__]]
       const_iterator
       cend() const noexcept
-      { return const_iterator(&this->_M_impl._M_node); }
+      { return const_iterator(this->_M_impl._M_node._M_base()); }
 
       /**
        *  Returns a read-only (constant) reverse iterator that points to
@@ -1171,7 +1516,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
        */
       _GLIBCXX_NODISCARD bool
       empty() const _GLIBCXX_NOEXCEPT
-      { return this->_M_impl._M_node._M_next == &this->_M_impl._M_node; }
+      {
+       return this->_M_impl._M_node._M_next == this->_M_impl._M_node._M_base();
+      }
 
       /**  Returns the number of elements in the %list.  */
       _GLIBCXX_NODISCARD
@@ -1682,8 +2029,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
       void
       swap(list& __x) _GLIBCXX_NOEXCEPT
       {
-       __detail::_List_node_base::swap(this->_M_impl._M_node,
-                                       __x._M_impl._M_node);
+       _Node_traits::_Node_base::swap(this->_M_impl._M_node,
+                                      __x._M_impl._M_node);
 
        size_t __xsize = __x._M_get_size();
        __x._M_set_size(this->_M_get_size());
@@ -2105,7 +2452,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
       void
       _M_insert(iterator __position, const value_type& __x)
       {
-       _Node* __tmp = _M_create_node(__x);
+       _Node_ptr __tmp = _M_create_node(__x);
        __tmp->_M_hook(__position._M_node);
        this->_M_inc_size(1);
       }
@@ -2114,7 +2461,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
        void
        _M_insert(iterator __position, _Args&&... __args)
        {
-        _Node* __tmp = _M_create_node(std::forward<_Args>(__args)...);
+        _Node_ptr __tmp = _M_create_node(std::forward<_Args>(__args)...);
         __tmp->_M_hook(__position._M_node);
         this->_M_inc_size(1);
        }
@@ -2126,7 +2473,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11
       {
        this->_M_dec_size(1);
        __position._M_node->_M_unhook();
-       _Node* __n = static_cast<_Node*>(__position._M_node);
+       _Node_ptr __n = static_cast<_Node&>(*__position._M_node)._M_node_ptr();
 #if __cplusplus >= 201103L
        _Node_alloc_traits::destroy(_M_get_Node_allocator(), __n->_M_valptr());
 #else
@@ -2397,6 +2744,113 @@ _GLIBCXX_END_NAMESPACE_CONTAINER
        }
       return __n;
     }
+
+#if _GLIBCXX_LIST_USE_ALLOC_PTR
+  template<bool _Const, typename _Ptr>
+    inline ptrdiff_t
+    __distance(__list::_Iterator<_Const, _Ptr> __first,
+              __list::_Iterator<_Const, _Ptr> __last,
+              input_iterator_tag __tag)
+    {
+      using _Tp = typename __list::_Iterator<_Const, _Ptr>::value_type;
+      using _Sentinel = typename __list::_Node_traits<_Tp, _Ptr>::_Node_header;
+      auto __beyond = __last;
+      ++__beyond;
+      const bool __whole = __first == __beyond;
+      if (__builtin_constant_p (__whole) && __whole)
+       return static_cast<const _Sentinel&>(*__last._M_node)._M_size;
+
+      ptrdiff_t __n = 0;
+      while (__first != __last)
+       {
+         ++__first;
+         ++__n;
+       }
+      return __n;
+    }
+#endif
+#endif
+
+#if _GLIBCXX_LIST_USE_ALLOC_PTR
+namespace __list
+{
+  template<typename _VoidPtr>
+    void
+    _Node_base<_VoidPtr>::swap(_Node_base& __x, _Node_base& __y) noexcept
+    {
+      auto __px = pointer_traits<_Base_ptr>::pointer_to(__x);
+      auto __py = pointer_traits<_Base_ptr>::pointer_to(__y);
+
+      if (__x._M_next != __px)
+       {
+         if (__y._M_next != __py)
+           {
+             using std::swap;
+             // Both __x and __y are not empty.
+             swap(__x._M_next,__y._M_next);
+             swap(__x._M_prev,__y._M_prev);
+             __x._M_next->_M_prev = __x._M_prev->_M_next = __px;
+             __y._M_next->_M_prev = __y._M_prev->_M_next = __py;
+           }
+         else
+           {
+             // __x is not empty, __y is empty.
+             __y._M_next = __x._M_next;
+             __y._M_prev = __x._M_prev;
+             __y._M_next->_M_prev = __y._M_prev->_M_next = __py;
+             __x._M_next = __x._M_prev = __px;
+           }
+       }
+      else if (__y._M_next != __py)
+       {
+         // __x is empty, __y is not empty.
+         __x._M_next = __y._M_next;
+         __x._M_prev = __y._M_prev;
+         __x._M_next->_M_prev = __x._M_prev->_M_next = __px;
+         __y._M_next = __y._M_prev = __py;
+       }
+    }
+
+  template<typename _VoidPtr>
+    void
+    _Node_base<_VoidPtr>::_M_transfer(_Base_ptr const __first,
+                                     _Base_ptr const __last)
+    {
+      __glibcxx_assert(__first != __last);
+
+      auto __self = pointer_traits<_Base_ptr>::pointer_to(*this);
+      if (__self != __last)
+       {
+         // Remove [first, last) from its old position.
+         __last->_M_prev->_M_next  = __self;
+         __first->_M_prev->_M_next = __last;
+         this->_M_prev->_M_next    = __first;
+
+         // Splice [first, last) into its new position.
+         auto const __tmp = this->_M_prev;
+         this->_M_prev    = __last->_M_prev;
+         __last->_M_prev  = __first->_M_prev;
+         __first->_M_prev = __tmp;
+       }
+    }
+
+  template<typename _VoidPtr>
+    void
+    _Node_header<_VoidPtr>::_M_reverse() noexcept
+    {
+      const auto __self = this->_M_base();
+      auto __tmp = __self;
+      do
+       {
+         using std::swap;
+         swap(__tmp->_M_next, __tmp->_M_prev);
+
+         // Old next node is now prev.
+         __tmp = __tmp->_M_prev;
+       }
+      while (__tmp != __self);
+    }
+} // namespace __list
 #endif
 
 _GLIBCXX_END_NAMESPACE_VERSION
diff --git 
a/libstdc++-v3/testsuite/23_containers/list/requirements/explicit_instantiation/alloc_ptr.cc
 
b/libstdc++-v3/testsuite/23_containers/list/requirements/explicit_instantiation/alloc_ptr.cc
new file mode 100644
index 00000000000..01715b599a9
--- /dev/null
+++ 
b/libstdc++-v3/testsuite/23_containers/list/requirements/explicit_instantiation/alloc_ptr.cc
@@ -0,0 +1,76 @@
+// { dg-do compile { target c++11 } }
+
+#include <list>
+#include <testsuite_allocator.h>
+
+// An allocator that uses __gnu_cxx::_Pointer_adapter as its pointer type.
+template class std::list<int, __gnu_test::CustomPointerAlloc<int>>;
+
+// Unlike __gnu_cxx::_Pointer_adapter, this fancy pointer supports neither
+// implicit nor explicit conversions from raw pointers. The constructor from
+// a raw pointer is explicit and requires a second parameter. The only way for
+// containers to construct one of these pointers is pointer_traits::pointer_to.
+template<typename T>
+struct Pointer : __gnu_test::PointerBase<Pointer<T>, T>
+{
+  using Base = __gnu_test::PointerBase<Pointer<T>, T>;
+
+  Pointer() = default;
+  Pointer(std::nullptr_t) : Base() { }
+  explicit Pointer(T* p, int) : Base(p) { }
+
+  // Allow conversions to const_pointer and to void_pointer
+  template<typename U, typename = typename std::enable_if<
+    (!std::is_const<U>::value && std::is_same<T, const U>::value)
+    || (std::is_void<T>::value && std::is_convertible<U*, T*>::value)
+    >::type>
+    Pointer(const Pointer<U>& p) : Base(p.operator->()) { }
+
+  template<typename U>
+    static typename std::enable_if<std::is_same<U, T>::value, Pointer>::type
+    pointer_to(U& t)
+    { return Pointer(std::addressof(t), 1); }
+};
+
+// A minimal allocator that uses Pointer as its pointer type.
+template<typename T>
+struct Allocator
+{
+  using value_type = T;
+  using pointer = Pointer<T>;
+
+  Allocator() = default;
+  template<typename U>
+    Allocator(const Allocator<U>&) { }
+
+  pointer allocate(std::size_t n)
+  { return pointer(std::allocator<T>().allocate(n), 1); }
+
+  void deallocate(pointer p, std::size_t n)
+  {
+    std::allocator<T>().deallocate(p.operator->(), n);
+  }
+
+  bool operator==(const Allocator&) const { return true; }
+  bool operator!=(const Allocator&) const { return false; }
+};
+
+template class std::list<int, Allocator<int>>;
+
+#include <testsuite_iterators.h>
+
+void
+test_template_members(__gnu_test::input_container<short>& c)
+{
+  // Use member functions that are not included in explicit instantiations.
+  std::list<int, Allocator<int>> l(c.begin(), c.end());
+  l.assign(c.begin(), c.end());
+  l.emplace_front(1);
+  l.emplace_back(1);
+  l.emplace(l.begin(), 1);
+  l.remove_if([](int) { return false; });
+  l.unique([](int, int) { return false; });
+  l.merge(l, [](int, int) { return false; });
+  l.merge(std::move(l), [](int, int) { return false; });
+  l.sort([](int, int) { return false; });
+}
diff --git 
a/libstdc++-v3/testsuite/23_containers/list/requirements/explicit_instantiation/alloc_ptr_ignored.cc
 
b/libstdc++-v3/testsuite/23_containers/list/requirements/explicit_instantiation/alloc_ptr_ignored.cc
new file mode 100644
index 00000000000..e6a499d2716
--- /dev/null
+++ 
b/libstdc++-v3/testsuite/23_containers/list/requirements/explicit_instantiation/alloc_ptr_ignored.cc
@@ -0,0 +1,4 @@
+// { dg-options "-D_GLIBCXX_LIST_USE_ALLOC_PTR=0" }
+// { dg-do compile { target c++11 } }
+
+#include "alloc_ptr.cc"
-- 
2.47.0

Reply via email to