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. --- Tested x86_64-linux. libstdc++-v3/include/bits/forward_list.h | 432 +++++++++++++++--- 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 | 86 ++++ .../alloc_ptr_ignored.cc | 4 + 7 files changed, 581 insertions(+), 119 deletions(-) create mode 100644 libstdc++-v3/testsuite/23_containers/forward_list/capacity/node_sizes.cc create mode 100644 libstdc++-v3/testsuite/23_containers/forward_list/requirements/completeness.cc create mode 100644 libstdc++-v3/testsuite/23_containers/forward_list/requirements/explicit_instantiation/alloc_ptr.cc create mode 100644 libstdc++-v3/testsuite/23_containers/forward_list/requirements/explicit_instantiation/alloc_ptr_ignored.cc diff --git a/libstdc++-v3/include/bits/forward_list.h b/libstdc++-v3/include/bits/forward_list.h index c9238cef96f..e4d24c4b9ca 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> #if __glibcxx_ranges_to_container // C++ >= 23 @@ -47,6 +50,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 @@ -54,11 +61,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) @@ -107,6 +117,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; } }; /** @@ -119,6 +132,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; @@ -130,8 +145,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. * @@ -199,6 +220,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 { @@ -282,6 +310,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 { @@ -291,23 +325,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) @@ -328,9 +634,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 @@ -357,54 +662,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); } - - template<typename... _Args> - _Node* - _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; - } - - template<typename... _Args> - _Fwd_list_node_base* - _M_insert_after(const_iterator __pos, _Args&&... __args); +#endif void - _M_put_node(_Node* __p) + _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 } - _Fwd_list_node_base* - _M_erase_after(_Fwd_list_node_base* __pos); + template<typename... _Args> + _Node_ptr + _M_create_node(_Args&&... __args) + { + 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 + } - _Fwd_list_node_base* - _M_erase_after(_Fwd_list_node_base* __pos, - _Fwd_list_node_base* __last); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wc++17-extensions" // if constexpr + void + _M_destroy_node(_Node_ptr __p) + { + 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); + + _Base_ptr + _M_erase_after(_Base_ptr __pos); + + _Base_ptr + _M_erase_after(_Base_ptr __pos, _Base_ptr __last); }; /** @@ -581,12 +903,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; } } @@ -776,7 +1098,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 @@ -786,7 +1108,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 @@ -845,7 +1167,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 @@ -884,8 +1206,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER reference front() { - _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(); } /** @@ -896,8 +1218,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER const_reference front() const { - _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: @@ -990,7 +1312,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 @@ -1137,8 +1462,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. @@ -1160,10 +1484,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. @@ -1225,7 +1549,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: diff --git a/libstdc++-v3/include/bits/forward_list.tcc b/libstdc++-v3/include/bits/forward_list.tcc index 9750c7c0502..140e9955714 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 119bf6ca027..590bba5bd25 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 00000000000..a709031783b --- /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 00000000000..abc9df504e2 --- /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 00000000000..2f382649d92 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/forward_list/requirements/explicit_instantiation/alloc_ptr.cc @@ -0,0 +1,86 @@ +// { 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>>; + +// 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 +} + 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 00000000000..6205a2ff3bf --- /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" -- 2.47.0