[gcc r12-3200] libstdc++: Fix std::allocator<void> for versioned namespace

Jonathan Wakely redi@gcc.gnu.org
Sat Aug 28 13:37:42 GMT 2021


https://gcc.gnu.org/g:754fca77e82a59d85c735a5aff49ee2b1ec4c6df

commit r12-3200-g754fca77e82a59d85c735a5aff49ee2b1ec4c6df
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Sat Aug 28 11:05:58 2021 +0100

    libstdc++: Fix std::allocator<void> for versioned namespace
    
    Removing the allocator<void> specialization for the versioned namespace
    breaks _Extptr_allocator<void> because the allocator<void>
    specialization was still declared in <bits/memoryfwd.h>, making it an
    incomplete type.  It wrong to remove that specialization anyway, because
    it is still needed pre-C++20.
    
    This removes the #if ! _GLIBCXX_INLINE_VERSION check, so that
    allocator<void> is still explicitly specialized for the versioned
    namespace, consistent with the normal unversioned namespace mode.
    
    To make _Extptr_allocator<void> usable as a ProtoAllocator, this change
    adds a default constructor and converting constructor. That is
    consistent with std::allocator<void> since C++20 (and harmless to do for
    earlier standards).
    
    I'm also explicitly specializing allocator_traits<allocator<void>> so
    that it doesn't need to use allocator<void>::construct and destroy.
    Doing that allows those members to be removed, further simplifying
    allocator<void>.  That new explicit specialization can delete the
    allocate, deallocate and max_size members, which are always ill-formed
    for allocator<void>.
    
    Signed-off-by: Jonathan Wakely <jwakely@redhat.com>
    
    libstdc++-v3/ChangeLog:
    
            * include/bits/alloc_traits.h (allocator_traits): Add explicit
            specialization for allocator<void>. Improve doxygen comments.
            * include/bits/allocator.h (allocator<void>): Restore for the
            versioned namespace.
            (allocator<void>::construct, allocator<void>::destroy): Remove.
            * include/ext/extptr_allocator.h (_Extptr_allocator<void>):
            Add default constructor and converting constructor.

Diff:
---
 libstdc++-v3/include/bits/alloc_traits.h    | 104 ++++++++++++++++++++++++++++
 libstdc++-v3/include/bits/allocator.h       |  38 +++-------
 libstdc++-v3/include/ext/extptr_allocator.h |   5 ++
 3 files changed, 120 insertions(+), 27 deletions(-)

diff --git a/libstdc++-v3/include/bits/alloc_traits.h b/libstdc++-v3/include/bits/alloc_traits.h
index 34412583064..05b584f742f 100644
--- a/libstdc++-v3/include/bits/alloc_traits.h
+++ b/libstdc++-v3/include/bits/alloc_traits.h
@@ -559,6 +559,110 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       { return __rhs; }
     };
 
+  /// Explicit specialization for std::allocator<void>.
+  template<>
+    struct allocator_traits<allocator<void>>
+    {
+      /// The allocator type
+      using allocator_type = allocator<void>;
+
+      /// The allocated type
+      using value_type = void;
+
+      /// The allocator's pointer type.
+      using pointer = void*;
+
+      /// The allocator's const pointer type.
+      using const_pointer = const void*;
+
+      /// The allocator's void pointer type.
+      using void_pointer = void*;
+
+      /// The allocator's const void pointer type.
+      using const_void_pointer = const void*;
+
+      /// The allocator's difference type
+      using difference_type = std::ptrdiff_t;
+
+      /// The allocator's size type
+      using size_type = std::size_t;
+
+      /// How the allocator is propagated on copy assignment
+      using propagate_on_container_copy_assignment = false_type;
+
+      /// How the allocator is propagated on move assignment
+      using propagate_on_container_move_assignment = true_type;
+
+      /// How the allocator is propagated on swap
+      using propagate_on_container_swap = false_type;
+
+      /// Whether all instances of the allocator type compare equal.
+      using is_always_equal = true_type;
+
+      template<typename _Up>
+	using rebind_alloc = allocator<_Up>;
+
+      template<typename _Up>
+	using rebind_traits = allocator_traits<allocator<_Up>>;
+
+      /// allocate is ill-formed for allocator<void>
+      static void*
+      allocate(allocator_type&, size_type, const void* = nullptr) = delete;
+
+      /// deallocate is ill-formed for allocator<void>
+      static void
+      deallocate(allocator_type&, void*, size_type) = delete;
+
+      /**
+       *  @brief  Construct an object of type `_Up`
+       *  @param  __a  An allocator.
+       *  @param  __p  Pointer to memory of suitable size and alignment for
+       *	       an object of type `_Up`.
+       *  @param  __args Constructor arguments.
+       *
+       *  Calls `__a.construct(__p, std::forward<_Args>(__args)...)`
+       *  in C++11, C++14 and C++17. Changed in C++20 to call
+       *  `std::construct_at(__p, std::forward<_Args>(__args)...)` instead.
+      */
+      template<typename _Up, typename... _Args>
+	static _GLIBCXX20_CONSTEXPR void
+	construct(allocator_type&, _Up* __p, _Args&&... __args)
+	noexcept(std::is_nothrow_constructible<_Up, _Args...>::value)
+	{
+#if __cplusplus <= 201703L
+	  ::new((void *)__p) _Up(std::forward<_Args>(__args)...);
+#else
+	  std::construct_at(__p, std::forward<_Args>(__args)...);
+#endif
+	}
+
+      /**
+       *  @brief  Destroy an object of type `_Up`
+       *  @param  __a  An allocator.
+       *  @param  __p  Pointer to the object to destroy
+       *
+       *  Invokes the destructor for `*__p`.
+      */
+      template<typename _Up>
+	static _GLIBCXX20_CONSTEXPR void
+	destroy(allocator_type&, _Up* __p)
+	noexcept(is_nothrow_destructible<_Up>::value)
+	{ std::_Destroy(__p); }
+
+      /// max_size is ill-formed for allocator<void>
+      static size_type
+      max_size(const allocator_type&) = delete;
+
+      /**
+       *  @brief  Obtain an allocator to use when copying a container.
+       *  @param  __rhs  An allocator.
+       *  @return `__rhs`
+      */
+      static _GLIBCXX20_CONSTEXPR allocator_type
+      select_on_container_copy_construction(const allocator_type& __rhs)
+      { return __rhs; }
+    };
+
 #if __cplusplus < 201703L
   template<typename _Alloc>
     inline void
diff --git a/libstdc++-v3/include/bits/allocator.h b/libstdc++-v3/include/bits/allocator.h
index 396872ffae5..a6883c6758a 100644
--- a/libstdc++-v3/include/bits/allocator.h
+++ b/libstdc++-v3/include/bits/allocator.h
@@ -61,12 +61,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
    */
 
   // Since C++20 the primary template should be used for allocator<void>,
-  // but then it would have a non-trivial default ctor and dtor, which
-  // would be an ABI change. So C++20 still uses the allocator<void> explicit
-  // specialization, with the historical ABI properties, but with the same
-  // members that are present in the primary template.
+  // but then it would have a non-trivial default ctor and dtor for C++20,
+  // but trivial for C++98-17, which would be an ABI incompatibiliy between
+  // different standard dialects. So C++20 still uses the allocator<void>
+  // explicit specialization, with the historical ABI properties, but with
+  // the same members that are present in the primary template.
 
-#if ! _GLIBCXX_INLINE_VERSION
   /// allocator<void> specialization.
   template<>
     class allocator<void>
@@ -77,7 +77,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       typedef ptrdiff_t   difference_type;
 
 #if __cplusplus <= 201703L
-      // These were removed for C++20.
+      // These were removed for C++20, allocator_traits does the right thing.
       typedef void*       pointer;
       typedef const void* const_pointer;
 
@@ -96,7 +96,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	= true_type;
 
 #if __cplusplus >= 202002L
+      // As noted above, these members are present for C++20 to provide the
+      // same API as the primary template, but still trivial as in pre-C++20.
       allocator() = default;
+      ~allocator() = default;
 
       template<typename _Up>
 	constexpr
@@ -105,28 +108,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // No allocate member because it's ill-formed by LWG 3307.
       // No deallocate member because it would be undefined to call it
       // with any pointer which wasn't obtained from allocate.
-
-#else // ! C++20
-    private:
-      // This uses construct and destroy in C++11/14/17 modes.
-      friend allocator_traits<allocator<void>>;
-
-      template<typename _Up, typename... _Args>
-	void
-	construct(_Up* __p, _Args&&... __args)
-	noexcept(std::is_nothrow_constructible<_Up, _Args...>::value)
-	{ ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
-
-      template<typename _Up>
-	void
-	destroy(_Up* __p)
-	noexcept(std::is_nothrow_destructible<_Up>::value)
-	{ __p->~_Up(); }
-#endif // C++17
+#endif // C++20
 #endif // C++11
-
     };
-#endif // ! _GLIBCXX_INLINE_VERSION
 
   /**
    * @brief  The @a standard allocator, as per C++03 [20.4.1].
@@ -212,7 +196,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	    return;
 	  }
 #endif
-	  __allocator_base<_Tp>::deallocate(__p, __n);
+	__allocator_base<_Tp>::deallocate(__p, __n);
       }
 #endif // C++20
 
diff --git a/libstdc++-v3/include/ext/extptr_allocator.h b/libstdc++-v3/include/ext/extptr_allocator.h
index 7d8aaac7cee..2dfc73ec605 100644
--- a/libstdc++-v3/include/ext/extptr_allocator.h
+++ b/libstdc++-v3/include/ext/extptr_allocator.h
@@ -176,6 +176,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       typedef _Pointer_adapter<_Relative_pointer_impl<const void> >
                                                               const_pointer;
 
+      _ExtPtr_allocator() { }
+
+      template<typename _Up>
+	_ExtPtr_allocator(const _ExtPtr_allocator<_Up>&) { }
+
       template<typename _Up>
         struct rebind
         { typedef _ExtPtr_allocator<_Up> other; };


More information about the Libstdc++-cvs mailing list