public inbox for libstdc++-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r15-5910] libstdc++: Add fancy pointer support to std::forward_list [PR57272]
@ 2024-12-03 21:36 Jonathan Wakely
  0 siblings, 0 replies; only message in thread
From: Jonathan Wakely @ 2024-12-03 21:36 UTC (permalink / raw)
  To: gcc-cvs, libstdc++-cvs

https://gcc.gnu.org/g:84b40a1c1b2c9e3feb546838fa988d653eed0755

commit r15-5910-g84b40a1c1b2c9e3feb546838fa988d653eed0755
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Fri Nov 15 21:45:16 2024 +0000

    libstdc++: Add fancy pointer support to std::forward_list [PR57272]
    
    This takes a very similar approach to the changes for std::list.
    
    libstdc++-v3/ChangeLog:
    
            PR libstdc++/57272
            * include/bits/forward_list.h (_GLIBCXX_USE_ALLOC_PTR_FOR_LIST):
            Define.
            (_Fwd_list_node_base::_M_base_ptr): New member functions.
            (_Fwd_list_node::_M_node_ptr): New member function.
            (_Fwd_list_iterator, _Fwd_list_const_iterator): Make internal
            member functions and data member private. Declare forward_list
            and _Fwd_list_base as friends.
            (__fwdlist::_Node_base, __fwdlist::_Node, __fwdlist::_Iterator):
            New class templates.
            (__fwdlist::_Node_traits): New class template.
            (_Fwd_list_base): Use _Node_traits to get types. Use _Base_ptr
            instad of _Fwd_list_node_base*. Use _M_base_ptr() instead of
            taking address of head node.
            (forward_list): Likewise.
            (_Fwd_list_base::_M_get_node): Do not define for versioned
            namespace.
            (_Fwd_list_base::_M_put_node): Only convert pointer if needed.
            (_Fwd_list_base::_M_create_node): Use __allocate_guarded_obj.
            (_Fwd_list_base::_M_destroy_node): New member function.
            * include/bits/forward_list.tcc (_Fwd_list_base::_M_insert_after)
            (forward_list::_M_splice_after, forward_list::insert_after): Use
            const_iterator::_M_const_cast() instead of casting pointers.
            (_Fwd_list_base::_M_erase_after): Use _M_destroy_node.
            (forward_list::remove, forward_list::remove_if): Only do
            downcasts when accessing the value.
            (forward_list::sort): Likewise.
            * testsuite/23_containers/forward_list/capacity/1.cc: Check
            max_size for new node type.
            * testsuite/23_containers/forward_list/capacity/node_sizes.cc:
            New test.
            * testsuite/23_containers/forward_list/requirements/explicit_instantiation/alloc_ptr.cc:
            New test.
            * testsuite/23_containers/forward_list/requirements/explicit_instantiation/alloc_ptr_ignored.cc:
            New test.

Diff:
---
 libstdc++-v3/include/bits/forward_list.h           | 508 +++++++++++++++++----
 libstdc++-v3/include/bits/forward_list.tcc         | 124 ++---
 .../23_containers/forward_list/capacity/1.cc       |  11 +-
 .../forward_list/capacity/node_sizes.cc            |  24 +
 .../forward_list/requirements/completeness.cc      |  19 +
 .../explicit_instantiation/alloc_ptr.cc            |  88 ++++
 .../explicit_instantiation/alloc_ptr_ignored.cc    |   4 +
 7 files changed, 635 insertions(+), 143 deletions(-)

diff --git a/libstdc++-v3/include/bits/forward_list.h b/libstdc++-v3/include/bits/forward_list.h
index f6d4e6bd3d26..f4a53bb4d7ef 100644
--- a/libstdc++-v3/include/bits/forward_list.h
+++ b/libstdc++-v3/include/bits/forward_list.h
@@ -40,6 +40,9 @@
 #include <bits/stl_algobase.h>
 #include <bits/stl_function.h>
 #include <bits/allocator.h>
+#include <bits/allocated_ptr.h>
+#include <bits/ptr_traits.h>
+#include <debug/assertions.h>
 #include <ext/alloc_traits.h>
 #include <ext/aligned_buffer.h>
 #include <debug/assertions.h>
@@ -48,6 +51,10 @@
 # include <bits/ranges_util.h> // ranges::subrange
 #endif
 
+#if ! defined _GLIBCXX_USE_ALLOC_PTR_FOR_FWD_LIST
+# define _GLIBCXX_USE_ALLOC_PTR_FOR_FWD_LIST 1
+#endif
+
 namespace std _GLIBCXX_VISIBILITY(default)
 {
 _GLIBCXX_BEGIN_NAMESPACE_VERSION
@@ -55,11 +62,14 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 
   /**
    *  @brief  A helper basic node class for %forward_list.
+   *
    *          This is just a linked list with nothing inside it.
    *          There are purely list shuffling utility methods here.
    */
   struct _Fwd_list_node_base
   {
+    using _Base_ptr = _Fwd_list_node_base*;
+
     _Fwd_list_node_base() = default;
     _Fwd_list_node_base(_Fwd_list_node_base&& __x) noexcept
       : _M_next(__x._M_next)
@@ -108,6 +118,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 	  _M_next->_M_next = __keep;
 	}
     }
+
+    _Fwd_list_node_base* _M_base_ptr() { return this; }
+    const _Fwd_list_node_base* _M_base_ptr() const { return this; }
   };
 
   /**
@@ -120,6 +133,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
     struct _Fwd_list_node
     : public _Fwd_list_node_base
     {
+      using _Node_ptr = _Fwd_list_node*;
+
       _Fwd_list_node() = default;
 
       __gnu_cxx::__aligned_buffer<_Tp> _M_storage;
@@ -131,8 +146,14 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       const _Tp*
       _M_valptr() const noexcept
       { return _M_storage._M_ptr(); }
+
+      _Node_ptr
+      _M_node_ptr()
+      { return this; }
     };
 
+  template<typename _Tp> struct _Fwd_list_const_iterator;
+
   /**
    *   @brief A forward_list::iterator.
    *
@@ -200,6 +221,13 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       { return __x._M_node != __y._M_node; }
 #endif
 
+    private:
+      template<typename, typename>
+	friend class forward_list;
+      template<typename, typename>
+	friend struct _Fwd_list_base;
+      friend struct _Fwd_list_const_iterator<_Tp>;
+
       _Self
       _M_next() const noexcept
       {
@@ -283,6 +311,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       { return __x._M_node != __y._M_node; }
 #endif
 
+    private:
+      template<typename, typename>
+	friend class forward_list;
+      template<typename, typename>
+	friend struct _Fwd_list_base;
+
       _Self
       _M_next() const noexcept
       {
@@ -292,23 +326,295 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 	  return _Fwd_list_const_iterator(nullptr);
       }
 
+      _Fwd_list_iterator<_Tp>
+      _M_const_cast() const noexcept
+      {
+	return _Fwd_list_iterator<_Tp>(
+		 const_cast<_Fwd_list_node_base*>(_M_node));
+      }
+
       const _Fwd_list_node_base* _M_node;
     };
 
+  template<typename _Tp, typename _Allocator> class forward_list;
+  template<typename _Tp, typename _Allocator> struct _Fwd_list_base;
+
+namespace __fwdlist
+{
+#if _GLIBCXX_USE_ALLOC_PTR_FOR_FWD_LIST
+  /// The node-base type for allocators that use fancy pointers.
+  template<typename _VoidPtr>
+    struct _Node_base
+    {
+      using _Base_ptr = __ptr_rebind<_VoidPtr, _Node_base>;
+
+      _Node_base() = default;
+
+      _Node_base(_Node_base&& __x) noexcept
+      : _M_next(__x._M_next)
+      { __x._M_next = nullptr; }
+
+      _Node_base(const _Node_base&) = delete;
+      _Node_base& operator=(const _Node_base&) = delete;
+
+      _Node_base&
+      operator=(_Node_base&& __x) noexcept
+      {
+	_M_next = __x._M_next;
+	__x._M_next = nullptr;
+	return *this;
+      }
+
+      _Base_ptr _M_next = nullptr;
+
+      // Splice (begin,end) before _M_next.
+      _Base_ptr
+      _M_transfer_after(_Base_ptr __begin, _Base_ptr __end) noexcept
+      {
+	_Base_ptr __keep = __begin->_M_next;
+	if (__end)
+	  {
+	    __begin->_M_next = __end->_M_next;
+	    __end->_M_next = _M_next;
+	  }
+	else
+	  __begin->_M_next = nullptr;
+	_M_next = __keep;
+	return __end;
+      }
+
+      void
+      _M_reverse_after() noexcept
+      {
+	_Base_ptr __tail = _M_next;
+	if (!__tail)
+	  return;
+	while (_Base_ptr __temp = __tail->_M_next)
+	  {
+	    _Base_ptr __keep = _M_next;
+	    _M_next = __temp;
+	    __tail->_M_next = __temp->_M_next;
+	    _M_next->_M_next = __keep;
+	  }
+      }
+
+      // This is not const-correct, but it's only used in a const access path
+      // by std::forward_list::empty(), where it doesn't escape, and by
+      // std::forward_list::before_begin() const, where the pointer is used
+      // to initialize a const_iterator and so constness is restored.
+      _Base_ptr
+      _M_base_ptr() const
+      {
+	return pointer_traits<_Base_ptr>::
+		 pointer_to(const_cast<_Node_base&>(*this));
+      }
+    };
+
+  /**
+   *  @brief  A helper node class for %forward_list.
+   */
+  template<typename _ValPtr>
+    struct _Node
+    : public _Node_base<__ptr_rebind<_ValPtr, void>>
+    {
+      using value_type = typename pointer_traits<_ValPtr>::element_type;
+      using _Node_ptr = __ptr_rebind<_ValPtr, _Node>;
+
+      _Node() { }
+      ~_Node() { }
+      _Node(_Node&&) = delete;
+
+      union {
+#if ! _GLIBCXX_INLINE_VERSION
+	// For ABI compatibility we need to overalign this member.
+	alignas(__alignof__(value_type)) // XXX GLIBCXX_ABI Deprecated
+#endif
+	value_type _M_data;
+      };
+
+      value_type*
+      _M_valptr() noexcept
+      { return std::__addressof(_M_data); }
+
+      const value_type*
+      _M_valptr() const noexcept
+      { return std::__addressof(_M_data); }
+
+      _Node_ptr
+      _M_node_ptr()
+      { return pointer_traits<_Node_ptr>::pointer_to(*this); }
+    };
+
+  /// A forward_list iterator when the allocator uses fancy pointers.
+  template<bool _Const, typename _Ptr>
+    class _Iterator
+    {
+      using _Node = __fwdlist::_Node<_Ptr>;
+      using _Base_ptr
+	= typename __fwdlist::_Node_base<__ptr_rebind<_Ptr, void>>::_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 = forward_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>>
+	constexpr
+	_Iterator(const _Iterator<_OtherConst, _Ptr>& __i)
+#endif
+	: _M_node(__i._M_node) { }
+
+      constexpr explicit
+      _Iterator(_Base_ptr __x) noexcept
+      : _M_node(__x) { }
+
+      [[__nodiscard__]]
+      constexpr reference
+      operator*() const noexcept
+      { return static_cast<_Node&>(*this->_M_node)._M_data; }
+
+      [[__nodiscard__]]
+      constexpr pointer
+      operator->() const noexcept
+      { return static_cast<_Node&>(*this->_M_node)._M_valptr(); }
+
+      _GLIBCXX14_CONSTEXPR _Iterator&
+      operator++() noexcept
+      {
+	_M_node = _M_node->_M_next;
+	return *this;
+      }
+
+      _GLIBCXX14_CONSTEXPR _Iterator
+      operator++(int) noexcept
+      {
+	_Iterator __tmp(*this);
+	_M_node = _M_node->_M_next;
+	return __tmp;
+      }
+
+      /**
+       *  @brief  Forward list iterator equality comparison.
+       */
+      [[__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
+      /**
+       *  @brief  Forward list iterator inequality comparison.
+       */
+      [[__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::forward_list;
+      template<typename _Tp, typename _Allocator>
+	friend struct _GLIBCXX_STD_C::_Fwd_list_base;
+
+      constexpr _Iterator<false, _Ptr>
+      _M_const_cast() const noexcept
+      { return _Iterator<false, _Ptr>(_M_node); }
+
+      friend _Iterator<!_Const, _Ptr>;
+
+      constexpr _Iterator
+      _M_next() const noexcept
+      { return _Iterator(_M_node ? _M_node->_M_next : nullptr); }
+
+      _Base_ptr _M_node;
+    };
+#endif // USE_ALLOC_PTR_FOR_FWD_LIST
+
+  // Determine the node and iterator types used by std::forward_list.
+  template<typename _Tp, typename _Ptr>
+    struct _Node_traits;
+
+#if _GLIBCXX_USE_ALLOC_PTR_FOR_LIST <= 9000
+  // 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*>
+    {
+      using _Node_base	    = _Fwd_list_node_base;
+      using _Node	    = _Fwd_list_node<_Tp>;
+      using _Iterator	    = _Fwd_list_iterator<_Tp>;
+      using _Const_iterator = _Fwd_list_const_iterator<_Tp>;
+    };
+#endif
+
+#if ! _GLIBCXX_USE_ALLOC_PTR_FOR_FWD_LIST
+  // Always use the T* specialization.
+  template<typename _Tp, typename _Ptr>
+    struct _Node_traits
+    : _Node_traits<_Tp, _Tp*>
+    { };
+#else
+  // 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      = __fwdlist::_Node_base<_VoidPtr>;
+      using _Node           = __fwdlist::_Node<_ValPtr>;
+      using _Iterator       = __fwdlist::_Iterator<false, _ValPtr>;
+      using _Const_iterator = __fwdlist::_Iterator<true, _ValPtr>;
+    };
+#endif // USE_ALLOC_PTR_FOR_FWD_LIST
+} // namespace __fwdlist
+
   /**
    *  @brief  Base class for %forward_list.
    */
   template<typename _Tp, typename _Alloc>
     struct _Fwd_list_base
     {
+#if __cplusplus > 201703L || defined __STRICT_ANSI__
+      // The static_assert in forward_list ensures _Alloc::value_type is _Tp.
+      using pointer = typename allocator_traits<_Alloc>::pointer;
+#else
+      using _Tp_alloc_traits
+	= typename allocator_traits<_Alloc>::template rebind_traits<_Tp>;
+      using pointer = typename _Tp_alloc_traits::pointer;
+#endif
+
     protected:
-      typedef __alloc_rebind<_Alloc, _Fwd_list_node<_Tp>> _Node_alloc_type;
-      typedef __gnu_cxx::__alloc_traits<_Node_alloc_type> _Node_alloc_traits;
+      using _Node_traits = __fwdlist::_Node_traits<_Tp, pointer>;
+      using _Node = typename _Node_traits::_Node;
+      using _Node_alloc_type = __alloc_rebind<_Alloc, _Node>;
+      using _Node_alloc_traits = __gnu_cxx::__alloc_traits<_Node_alloc_type>;
+      using _Node_ptr = typename _Node_alloc_traits::pointer;
+      using _Base_ptr = typename _Node_traits::_Node_base::_Base_ptr;
 
       struct _Fwd_list_impl
       : public _Node_alloc_type
       {
-	_Fwd_list_node_base _M_head;
+	typename _Node_traits::_Node_base _M_head;
 
 	_Fwd_list_impl()
 	  noexcept(is_nothrow_default_constructible<_Node_alloc_type>::value)
@@ -329,9 +635,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       _Fwd_list_impl _M_impl;
 
     public:
-      typedef _Fwd_list_iterator<_Tp>		iterator;
-      typedef _Fwd_list_const_iterator<_Tp>	const_iterator;
-      typedef _Fwd_list_node<_Tp>		_Node;
+      using iterator = typename _Node_traits::_Iterator;
+      using const_iterator = typename _Node_traits::_Const_iterator;
 
       _Node_alloc_type&
       _M_get_Node_allocator() noexcept
@@ -358,54 +663,71 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       _Fwd_list_base(_Fwd_list_base&&) = default;
 
       ~_Fwd_list_base()
-      { _M_erase_after(&_M_impl._M_head, nullptr); }
+      { _M_erase_after(_M_impl._M_head._M_base_ptr(), nullptr); }
 
     protected:
+#if ! _GLIBCXX_INLINE_VERSION
+      // XXX GLIBCXX_ABI Deprecated
       _Node*
       _M_get_node()
       {
 	auto __ptr = _Node_alloc_traits::allocate(_M_get_Node_allocator(), 1);
 	return std::__to_address(__ptr);
       }
+#endif
+
+      void
+      _M_put_node(_Node_ptr __p)
+      {
+#if _GLIBCXX_USE_ALLOC_PTR_FOR_FWD_LIST
+	_Node_alloc_traits::deallocate(_M_get_Node_allocator(), __p, 1);
+#else
+	typedef typename _Node_alloc_traits::pointer _Ptr;
+	auto __ptr = std::pointer_traits<_Ptr>::pointer_to(*__p);
+	_Node_alloc_traits::deallocate(_M_get_Node_allocator(), __ptr, 1);
+#endif
+      }
 
       template<typename... _Args>
-	_Node*
+	_Node_ptr
 	_M_create_node(_Args&&... __args)
 	{
-	  _Node* __node = this->_M_get_node();
-	  __try
-	    {
-	      ::new ((void*)__node) _Node;
-	      _Node_alloc_traits::construct(_M_get_Node_allocator(),
-					    __node->_M_valptr(),
-					    std::forward<_Args>(__args)...);
-	    }
-	  __catch(...)
-	    {
-	      this->_M_put_node(__node);
-	      __throw_exception_again;
-	    }
-	  return __node;
+	  auto& __alloc = _M_get_Node_allocator();
+	  auto __guard = std::__allocate_guarded_obj(__alloc);
+	  _Node_alloc_traits::construct(__alloc, __guard->_M_valptr(),
+					std::forward<_Args>(__args)...);
+	  auto __p = __guard.release();
+#if _GLIBCXX_USE_ALLOC_PTR_FOR_FWD_LIST
+	  return __p;
+#else
+	  return std::__to_address(__p);
+#endif
 	}
 
-      template<typename... _Args>
-	_Fwd_list_node_base*
-	_M_insert_after(const_iterator __pos, _Args&&... __args);
-
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
       void
-      _M_put_node(_Node* __p)
+      _M_destroy_node(_Node_ptr __p)
       {
-	typedef typename _Node_alloc_traits::pointer _Ptr;
-	auto __ptr = std::pointer_traits<_Ptr>::pointer_to(*__p);
-	_Node_alloc_traits::deallocate(_M_get_Node_allocator(), __ptr, 1);
+	auto& __alloc = _M_get_Node_allocator();
+	// Destroy the element
+	_Node_alloc_traits::destroy(__alloc, __p->_M_valptr());
+	// Only destroy the node if the pointers require it.
+	if constexpr (!is_trivially_destructible<_Base_ptr>::value)
+	  __p->~_Node();
+	_M_put_node(__p);
       }
+#pragma GCC diagnostic pop
+
+      template<typename... _Args>
+	_Base_ptr
+	_M_insert_after(const_iterator __pos, _Args&&... __args);
 
-      _Fwd_list_node_base*
-      _M_erase_after(_Fwd_list_node_base* __pos);
+      _Base_ptr
+      _M_erase_after(_Base_ptr __pos);
 
-      _Fwd_list_node_base*
-      _M_erase_after(_Fwd_list_node_base* __pos,
-		     _Fwd_list_node_base* __last);
+      _Base_ptr
+      _M_erase_after(_Base_ptr __pos, _Base_ptr __last);
     };
 
   /**
@@ -582,12 +904,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 	forward_list(from_range_t, _Rg&& __rg, const _Alloc& __a = _Alloc())
 	: _Base(_Node_alloc_type(__a))
 	{
-	  _Node_base* __to = &this->_M_impl._M_head;
+	  auto __to = this->_M_impl._M_head._M_base_ptr();
 	  auto __first = ranges::begin(__rg);
 	  const auto __last = ranges::end(__rg);
 	  for (; __first != __last; ++__first)
 	    {
-	      __to->_M_next = this->_M_create_node(*__first);
+	      __to->_M_next = this->_M_create_node(*__first)->_M_base_ptr();
 	      __to = __to->_M_next;
 	    }
 	}
@@ -843,7 +1165,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       [[__nodiscard__]]
       iterator
       before_begin() noexcept
-      { return iterator(&this->_M_impl._M_head); }
+      { return iterator(this->_M_impl._M_head._M_base_ptr()); }
 
       /**
        *  Returns a read-only (constant) iterator that points before the
@@ -853,7 +1175,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       [[__nodiscard__]]
       const_iterator
       before_begin() const noexcept
-      { return const_iterator(&this->_M_impl._M_head); }
+      { return const_iterator(this->_M_impl._M_head._M_base_ptr()); }
 
       /**
        *  Returns a read/write iterator that points to the first element
@@ -912,7 +1234,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       [[__nodiscard__]]
       const_iterator
       cbefore_begin() const noexcept
-      { return const_iterator(&this->_M_impl._M_head); }
+      { return const_iterator(this->_M_impl._M_head._M_base_ptr()); }
 
       /**
        *  Returns a read-only (constant) iterator that points one past
@@ -952,8 +1274,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       front()
       {
 	__glibcxx_requires_nonempty();
-	_Node* __front = static_cast<_Node*>(this->_M_impl._M_head._M_next);
-	return *__front->_M_valptr();
+	_Node& __front = static_cast<_Node&>(*this->_M_impl._M_head._M_next);
+	return *__front._M_valptr();
       }
 
       /**
@@ -965,8 +1287,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       front() const
       {
 	__glibcxx_requires_nonempty();
-	_Node* __front = static_cast<_Node*>(this->_M_impl._M_head._M_next);
-	return *__front->_M_valptr();
+	_Node& __front = static_cast<_Node&>(*this->_M_impl._M_head._M_next);
+	return *__front._M_valptr();
       }
 
       // 23.3.4.5 modifiers:
@@ -1059,7 +1381,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
        */
       void
       pop_front()
-      { this->_M_erase_after(&this->_M_impl._M_head); }
+      {
+	__glibcxx_requires_nonempty();
+	this->_M_erase_after(this->_M_impl._M_head._M_base_ptr());
+      }
 
       /**
        *  @brief  Constructs object in %forward_list after the specified
@@ -1206,8 +1531,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
        */
       iterator
       erase_after(const_iterator __pos)
-      { return iterator(this->_M_erase_after(const_cast<_Node_base*>
-					     (__pos._M_node))); }
+      { return iterator(this->_M_erase_after(__pos._M_const_cast()._M_node)); }
 
       /**
        *  @brief  Remove a range of elements.
@@ -1230,10 +1554,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
        */
       iterator
       erase_after(const_iterator __pos, const_iterator __last)
-      { return iterator(this->_M_erase_after(const_cast<_Node_base*>
-					     (__pos._M_node),
-					     const_cast<_Node_base*>
-					     (__last._M_node))); }
+      {
+	return iterator(this->_M_erase_after(__pos._M_const_cast()._M_node,
+					     __last._M_const_cast()._M_node));
+      }
 
       /**
        *  @brief  Swaps data with another %forward_list.
@@ -1295,7 +1619,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
        */
       void
       clear() noexcept
-      { this->_M_erase_after(&this->_M_impl._M_head, nullptr); }
+      { this->_M_erase_after(this->_M_impl._M_head._M_base_ptr(), nullptr); }
 
       // 23.3.4.6 forward_list operations:
 
@@ -1530,6 +1854,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       _M_default_insert_after(const_iterator __pos, size_type __n);
 
 #if ! _GLIBCXX_INLINE_VERSION
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr
       // XXX GLIBCXX_ABI Deprecated
       // These members are unused by std::forward_list now, but we keep them
       // here so that an explicit instantiation will define them.
@@ -1537,53 +1863,79 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       // so that explicit instantiation declarations of std::forward_list that
       // were compiled with old versions of GCC can still find these 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.
+
       void
       _M_move_assign(forward_list&& __list, true_type) noexcept
       {
-	clear();
-	this->_M_impl._M_head._M_next = __list._M_impl._M_head._M_next;
-	__list._M_impl._M_head._M_next = nullptr;
-	std::__alloc_on_move(this->_M_get_Node_allocator(),
-			     __list._M_get_Node_allocator());
+#if _GLIBCXX_USE_ALLOC_PTR_FOR_FWD_LIST
+       if constexpr (is_same<typename _Alloc_traits::pointer, _Tp*>::value)
+#endif
+	 {
+	   clear();
+	   this->_M_impl._M_head._M_next = __list._M_impl._M_head._M_next;
+	   __list._M_impl._M_head._M_next = nullptr;
+	   std::__alloc_on_move(this->_M_get_Node_allocator(),
+				__list._M_get_Node_allocator());
+	 }
       }
 
       void
       _M_move_assign(forward_list&& __list, false_type)
       {
-	if (__list._M_get_Node_allocator() == this->_M_get_Node_allocator())
-	  _M_move_assign(std::move(__list), true_type());
-	else
-	  // The rvalue's allocator cannot be moved, or is not equal,
-	  // so we need to individually move each element.
-	  this->assign(std::make_move_iterator(__list.begin()),
-		       std::make_move_iterator(__list.end()));
+#if _GLIBCXX_USE_ALLOC_PTR_FOR_FWD_LIST
+	if constexpr (is_same<typename _Alloc_traits::pointer, _Tp*>::value)
+#endif
+	  {
+	    if (__list._M_get_Node_allocator() == this->_M_get_Node_allocator())
+	      _M_move_assign(std::move(__list), true_type());
+	    else
+	      // The rvalue's allocator cannot be moved, or is not equal,
+	      // so we need to individually move each element.
+	      this->assign(std::make_move_iterator(__list.begin()),
+			   std::make_move_iterator(__list.end()));
+	  }
       }
 
       void
       _M_assign_n(size_type __n, const _Tp& __val, true_type)
       {
-	auto __prev = before_begin();
-	auto __curr = begin();
-	auto __end = end();
-	while (__curr != __end && __n > 0)
+#if _GLIBCXX_USE_ALLOC_PTR_FOR_FWD_LIST
+	if constexpr (is_same<typename _Alloc_traits::pointer, _Tp*>::value)
+#endif
 	  {
-	    *__curr = __val;
-	    ++__prev;
-	    ++__curr;
-	    --__n;
+	    auto __prev = before_begin();
+	    auto __curr = begin();
+	    auto __end = end();
+	    while (__curr != __end && __n > 0)
+	      {
+		*__curr = __val;
+		++__prev;
+		++__curr;
+		--__n;
+	      }
+	    if (__n > 0)
+	      insert_after(__prev, __n, __val);
+	    else if (__curr != __end)
+	      erase_after(__prev, __end);
 	  }
-	if (__n > 0)
-	  insert_after(__prev, __n, __val);
-	else if (__curr != __end)
-	  erase_after(__prev, __end);
       }
 
       void
       _M_assign_n(size_type __n, const _Tp& __val, false_type)
       {
-	clear();
-	insert_after(cbefore_begin(), __n, __val);
+#if _GLIBCXX_USE_ALLOC_PTR_FOR_FWD_LIST
+	if constexpr (is_same<typename _Alloc_traits::pointer, _Tp*>::value)
+#endif
+	  {
+	    clear();
+	    insert_after(cbefore_begin(), __n, __val);
+	  }
       }
+#pragma GCC diagnostic pop
 #endif // ! _GLIBCXX_INLINE_VERSION
     };
 
diff --git a/libstdc++-v3/include/bits/forward_list.tcc b/libstdc++-v3/include/bits/forward_list.tcc
index 9750c7c0502b..140e9955714e 100644
--- a/libstdc++-v3/include/bits/forward_list.tcc
+++ b/libstdc++-v3/include/bits/forward_list.tcc
@@ -46,47 +46,42 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 
   template<typename _Tp, typename _Alloc>
     template<typename... _Args>
-      _Fwd_list_node_base*
+      auto
       _Fwd_list_base<_Tp, _Alloc>::
       _M_insert_after(const_iterator __pos, _Args&&... __args)
+      -> _Base_ptr
       {
-	_Fwd_list_node_base* __to
-	  = const_cast<_Fwd_list_node_base*>(__pos._M_node);
-	_Node* __thing = _M_create_node(std::forward<_Args>(__args)...);
+	auto __to = __pos._M_const_cast()._M_node;
+	_Node_ptr __thing = _M_create_node(std::forward<_Args>(__args)...);
 	__thing->_M_next = __to->_M_next;
-	__to->_M_next = __thing;
+	__to->_M_next = __thing->_M_base_ptr();
 	return __to->_M_next;
       }
 
   template<typename _Tp, typename _Alloc>
-    _Fwd_list_node_base*
+    auto
     _Fwd_list_base<_Tp, _Alloc>::
-    _M_erase_after(_Fwd_list_node_base* __pos)
+    _M_erase_after(_Base_ptr __pos)
+    -> _Base_ptr
     {
-      _Node* __curr = static_cast<_Node*>(__pos->_M_next);
-      __pos->_M_next = __curr->_M_next;
-      _Node_alloc_traits::destroy(_M_get_Node_allocator(),
-				  __curr->_M_valptr());
-      __curr->~_Node();
-      _M_put_node(__curr);
+      auto& __curr = static_cast<_Node&>(*__pos->_M_next);
+      __pos->_M_next = __curr._M_next;
+      _M_destroy_node(__curr._M_node_ptr());
       return __pos->_M_next;
     }
 
   template<typename _Tp, typename _Alloc>
-    _Fwd_list_node_base*
+    auto
     _Fwd_list_base<_Tp, _Alloc>::
-    _M_erase_after(_Fwd_list_node_base* __pos,
-		   _Fwd_list_node_base* __last)
+    _M_erase_after(_Base_ptr __pos, _Base_ptr __last)
+    -> _Base_ptr
     {
-      _Node* __curr = static_cast<_Node*>(__pos->_M_next);
+      _Base_ptr __curr = __pos->_M_next;
       while (__curr != __last)
 	{
-	  _Node* __temp = __curr;
-	  __curr = static_cast<_Node*>(__curr->_M_next);
-	  _Node_alloc_traits::destroy(_M_get_Node_allocator(),
-				      __temp->_M_valptr());
-	  __temp->~_Node();
-	  _M_put_node(__temp);
+	  auto& __node = static_cast<_Node&>(*__curr);
+	  __curr = __curr->_M_next;
+	  _M_destroy_node(__node._M_node_ptr());
 	}
       __pos->_M_next = __last;
       return __last;
@@ -99,10 +94,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       forward_list<_Tp, _Alloc>::
       _M_range_initialize(_InputIterator __first, _InputIterator __last)
       {
-	_Node_base* __to = &this->_M_impl._M_head;
+	auto __to = this->_M_impl._M_head._M_base_ptr();
 	for (; __first != __last; ++__first)
 	  {
-	    __to->_M_next = this->_M_create_node(*__first);
+	    __to->_M_next = this->_M_create_node(*__first)->_M_base_ptr();
 	    __to = __to->_M_next;
 	  }
       }
@@ -113,10 +108,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
     forward_list<_Tp, _Alloc>::
     _M_fill_initialize(size_type __n, const value_type& __value)
     {
-      _Node_base* __to = &this->_M_impl._M_head;
+      auto __to = this->_M_impl._M_head._M_base_ptr();
       for (; __n; --__n)
 	{
-	  __to->_M_next = this->_M_create_node(__value);
+	  __to->_M_next = this->_M_create_node(__value)->_M_base_ptr();
 	  __to = __to->_M_next;
 	}
     }
@@ -126,10 +121,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
     forward_list<_Tp, _Alloc>::
     _M_default_initialize(size_type __n)
     {
-      _Node_base* __to = &this->_M_impl._M_head;
+      auto __to = this->_M_impl._M_head._M_base_ptr();
       for (; __n; --__n)
 	{
-	  __to->_M_next = this->_M_create_node();
+	  __to->_M_next = this->_M_create_node()->_M_base_ptr();
 	  __to = __to->_M_next;
 	}
     }
@@ -220,9 +215,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
     _M_splice_after(const_iterator __pos,
 		    const_iterator __before, const_iterator __last)
     {
-      _Node_base* __tmp = const_cast<_Node_base*>(__pos._M_node);
-      _Node_base* __b = const_cast<_Node_base*>(__before._M_node);
-      _Node_base* __end = __b;
+      auto __tmp = __pos._M_const_cast()._M_node;
+      auto __b = __before._M_const_cast()._M_node;
+      auto __end = __b;
 
       while (__end && __end->_M_next != __last._M_node)
 	__end = __end->_M_next;
@@ -245,9 +240,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       if (__pos == __i || __pos == __j)
 	return;
 
-      _Node_base* __tmp = const_cast<_Node_base*>(__pos._M_node);
-      __tmp->_M_transfer_after(const_cast<_Node_base*>(__i._M_node),
-			       const_cast<_Node_base*>(__j._M_node));
+      auto __tmp = __pos._M_const_cast()._M_node;
+      __tmp->_M_transfer_after(__i._M_const_cast()._M_node,
+			       __j._M_const_cast()._M_node);
     }
 
   template<typename _Tp, typename _Alloc>
@@ -261,7 +256,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 	  return _M_splice_after(__pos, __tmp.before_begin(), __tmp.end());
 	}
       else
-	return iterator(const_cast<_Node_base*>(__pos._M_node));
+	return __pos._M_const_cast();
     }
 
   template<typename _Tp, typename _Alloc>
@@ -275,7 +270,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 	if (!__tmp.empty())
 	  return _M_splice_after(__pos, __tmp.before_begin(), __tmp.end());
 	else
-	  return iterator(const_cast<_Node_base*>(__pos._M_node));
+	  return __pos._M_const_cast();
       }
 
 #if __cplusplus > 201703L
@@ -293,8 +288,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       forward_list __to_destroy(get_allocator());
 
       auto __prev_it = cbefore_begin();
-      while (_Node* __tmp = static_cast<_Node*>(__prev_it._M_node->_M_next))
-	if (*__tmp->_M_valptr() == __val)
+      while (auto __tmp = __prev_it._M_node->_M_next)
+	if (*static_cast<_Node&>(*__tmp)._M_valptr() == __val)
 	  {
 	    __to_destroy.splice_after(__to_destroy.cbefore_begin(),
 				      *this, __prev_it);
@@ -316,8 +311,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 	forward_list __to_destroy(get_allocator());
 
 	auto __prev_it = cbefore_begin();
-	while (_Node* __tmp = static_cast<_Node*>(__prev_it._M_node->_M_next))
-	  if (__pred(*__tmp->_M_valptr()))
+	while (auto __tmp = __prev_it._M_node->_M_next)
+	  if (__pred(*static_cast<_Node&>(*__tmp)._M_valptr()))
 	    {
 	      __to_destroy.splice_after(__to_destroy.cbefore_begin(),
 					*this, __prev_it);
@@ -372,15 +367,16 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 	if (std::__addressof(__list) == this)
 	  return;
 
-	_Node_base* __node = &this->_M_impl._M_head;
-	while (__node->_M_next && __list._M_impl._M_head._M_next)
+	using _Base_ptr = typename _Node::_Base_ptr;
+
+	_Base_ptr __node = this->_M_impl._M_head._M_base_ptr();
+	_Base_ptr __other = __list._M_impl._M_head._M_base_ptr();
+	while (__node->_M_next && __other->_M_next)
 	  {
-	    if (__comp(*static_cast<_Node*>
-		       (__list._M_impl._M_head._M_next)->_M_valptr(),
-		       *static_cast<_Node*>
-		       (__node->_M_next)->_M_valptr()))
-	      __node->_M_transfer_after(&__list._M_impl._M_head,
-					__list._M_impl._M_head._M_next);
+	    auto& __l = static_cast<_Node&>(*__other->_M_next);
+	    auto& __r = static_cast<_Node&>(*__node->_M_next);
+	    if (__comp(*__l._M_valptr(), *__r._M_valptr()))
+	      __node->_M_transfer_after(__other, __other->_M_next);
 	    __node = __node->_M_next;
 	  }
 
@@ -416,18 +412,21 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
       forward_list<_Tp, _Alloc>::
       sort(_Comp __comp)
       {
-	// If `next' is nullptr, return immediately.
-	_Node* __list = static_cast<_Node*>(this->_M_impl._M_head._M_next);
-	if (!__list)
+	if (empty())
 	  return;
 
+	using _Base_ptr = typename _Node::_Base_ptr;
+
+	// If `next' is nullptr, return immediately.
+	_Base_ptr __list = this->_M_impl._M_head._M_next;
+
 	unsigned long __insize = 1;
 
 	while (1)
 	  {
-	    _Node* __p = __list;
+	    _Base_ptr __p = __list;
 	    __list = nullptr;
-	    _Node* __tail = nullptr;
+	    _Base_ptr __tail = nullptr;
 
 	    // Count number of merges we do in this pass.
 	    unsigned long __nmerges = 0;
@@ -437,12 +436,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 		++__nmerges;
 		// There exists a merge to be done.
 		// Step `insize' places along from p.
-		_Node* __q = __p;
+		_Base_ptr __q = __p;
 		unsigned long __psize = 0;
 		for (unsigned long __i = 0; __i < __insize; ++__i)
 		  {
 		    ++__psize;
-		    __q = static_cast<_Node*>(__q->_M_next);
+		    __q = __q->_M_next;
 		    if (!__q)
 		      break;
 		  }
@@ -454,33 +453,34 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
 		while (__psize > 0 || (__qsize > 0 && __q))
 		  {
 		    // Decide whether next node of merge comes from p or q.
-		    _Node* __e;
+		    _Base_ptr __e;
 		    if (__psize == 0)
 		      {
 			// p is empty; e must come from q.
 			__e = __q;
-			__q = static_cast<_Node*>(__q->_M_next);
+			__q = __q->_M_next;
 			--__qsize;
 		      }
 		    else if (__qsize == 0 || !__q)
 		      {
 			// q is empty; e must come from p.
 			__e = __p;
-			__p = static_cast<_Node*>(__p->_M_next);
+			__p = __p->_M_next;
 			--__psize;
 		      }
-		    else if (!__comp(*__q->_M_valptr(), *__p->_M_valptr()))
+		    else if (!__comp(*static_cast<_Node&>(*__q)._M_valptr(),
+				     *static_cast<_Node&>(*__p)._M_valptr()))
 		      {
 			// First node of q is not lower; e must come from p.
 			__e = __p;
-			__p = static_cast<_Node*>(__p->_M_next);
+			__p = __p->_M_next;
 			--__psize;
 		      }
 		    else
 		      {
 			// First node of q is lower; e must come from q.
 			__e = __q;
-			__q = static_cast<_Node*>(__q->_M_next);
+			__q = __q->_M_next;
 			--__qsize;
 		      }
 
diff --git a/libstdc++-v3/testsuite/23_containers/forward_list/capacity/1.cc b/libstdc++-v3/testsuite/23_containers/forward_list/capacity/1.cc
index 119bf6ca0274..590bba5bd254 100644
--- a/libstdc++-v3/testsuite/23_containers/forward_list/capacity/1.cc
+++ b/libstdc++-v3/testsuite/23_containers/forward_list/capacity/1.cc
@@ -36,13 +36,18 @@ test01()
   VERIFY(fld.empty() == true);
 
 #ifdef _GLIBCXX_DEBUG
-  using std::_GLIBCXX_STD_C::_Fwd_list_node;
+  namespace C = std::_GLIBCXX_STD_C;
 #else
-  using std::_Fwd_list_node;
+  namespace C = std;
 #endif
 
-  std::allocator<_Fwd_list_node<double> > a;
+  std::allocator<C::_Fwd_list_node<double>> a;
   VERIFY( fld.max_size() == __gnu_test::max_size(a) );
+
+#if _GLIBCXX_FWDLIST_USE_ALLOC_PTR
+  std::allocator<C::__fwdlist::_Node<double*>> b;
+  VERIFY( __gnu_test::max_size(b) == __gnu_test::max_size(a) );
+#endif
 }
 
 int
diff --git a/libstdc++-v3/testsuite/23_containers/forward_list/capacity/node_sizes.cc b/libstdc++-v3/testsuite/23_containers/forward_list/capacity/node_sizes.cc
new file mode 100644
index 000000000000..a709031783b6
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/forward_list/capacity/node_sizes.cc
@@ -0,0 +1,24 @@
+// { dg-do compile { target c++11 } }
+
+#include <forward_list>
+
+#if _GLIBCXX_FWDLIST_USE_ALLOC_PTR
+
+#ifdef _GLIBCXX_DEBUG
+namespace C = std::_GLIBCXX_STD_C;
+#else
+namespace C = std;
+#endif
+
+// We use double here because for ADJUST_FIELD_ALIGN targets (like i386)
+// its alignment differs when used as a data member or as a complete object.
+static_assert(sizeof(C::_Fwd_list_node<double>)
+	      == sizeof(C::__fwdlist::_Node<double*>),
+	      "node types have same size");
+static_assert(alignof(C::_Fwd_list_node<double>)
+	      == alignof(C::__fwdlist::_Node<double*>),
+	      "node types have same alignment");
+static_assert(__alignof(C::_Fwd_list_node<double>)
+	      == __alignof(C::__fwdlist::_Node<double*>),
+	      "node types have same preferred alignment");
+#endif
diff --git a/libstdc++-v3/testsuite/23_containers/forward_list/requirements/completeness.cc b/libstdc++-v3/testsuite/23_containers/forward_list/requirements/completeness.cc
new file mode 100644
index 000000000000..abc9df504e23
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/forward_list/requirements/completeness.cc
@@ -0,0 +1,19 @@
+// { dg-do compile {target c++11 } }
+
+// C++17 [forwardlist.overview]
+// An incomplete type T may be used when instantiating forward_list if the
+// allocator satisfies the allocator completeness requirements (20.5.3.5.1).
+// T shall be complete before any member of the resulting specialization
+// of forward_list is referenced.
+
+#include <forward_list>
+
+struct Incomplete;
+
+// This instantiates std::forward_list, but none of its members.
+const int sz = sizeof(std::forward_list<Incomplete>);
+
+// Technically the following references a member of std::forward_list,
+// but because our iterators are SCARY it doesn't instantiate any members
+// of std::forward_list.
+std::forward_list<Incomplete>::iterator i{};
diff --git a/libstdc++-v3/testsuite/23_containers/forward_list/requirements/explicit_instantiation/alloc_ptr.cc b/libstdc++-v3/testsuite/23_containers/forward_list/requirements/explicit_instantiation/alloc_ptr.cc
new file mode 100644
index 000000000000..621ff4c652cb
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/forward_list/requirements/explicit_instantiation/alloc_ptr.cc
@@ -0,0 +1,88 @@
+// { dg-do compile { target c++11 } }
+
+#include <forward_list>
+#include <testsuite_allocator.h>
+
+// An allocator that uses __gnu_cxx::_Pointer_adapter as its pointer type.
+template class std::forward_list<int, __gnu_test::CustomPointerAlloc<int>>;
+
+#if _GLIBCXX_USE_ALLOC_PTR_FOR_FWD_LIST != 0
+// This fancy pointer is incompatible with _GLIBCXX_USE_ALLOC_PTR_FOR_FWD_LIST.
+// 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::forward_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::forward_list<int, Allocator<int>> l(c.begin(), c.end());
+  l.assign(c.begin(), c.end());
+  l.insert_after(l.before_begin(), c.begin(), c.end());
+  l.emplace_front(1);
+  l.emplace_after(l.before_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; });
+
+#ifdef __glibcxx_ranges_to_container
+  short arr[2];
+  __gnu_test::test_input_range<short> r(arr);
+  std::forward_list<int, Allocator<int>> l2(std::from_range, r);
+  l2.assign_range(r);
+  l2.prepend_range(r);
+  l2.insert_range_after(l2.begin(), r);
+#endif
+}
+#endif
diff --git a/libstdc++-v3/testsuite/23_containers/forward_list/requirements/explicit_instantiation/alloc_ptr_ignored.cc b/libstdc++-v3/testsuite/23_containers/forward_list/requirements/explicit_instantiation/alloc_ptr_ignored.cc
new file mode 100644
index 000000000000..6205a2ff3bf2
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/forward_list/requirements/explicit_instantiation/alloc_ptr_ignored.cc
@@ -0,0 +1,4 @@
+// { dg-options "-D_GLIBCXX_FWDLIST_USE_ALLOC_PTR=0" }
+// { dg-do compile { target c++11 } }
+
+#include "alloc_ptr.cc"

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2024-12-03 21:36 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-12-03 21:36 [gcc r15-5910] libstdc++: Add fancy pointer support to std::forward_list [PR57272] Jonathan Wakely

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).