public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [ PATCH ] [ C++ ] [ libstdc++ ] P0674r1 - Extend support for arrays in make/allocate_shared
@ 2020-01-02 22:16 JeanHeyd Meneide
  2020-01-03 15:05 ` Jonathan Wakely
  2020-07-17 13:41 ` Jonathan Wakely
  0 siblings, 2 replies; 3+ messages in thread
From: JeanHeyd Meneide @ 2020-01-02 22:16 UTC (permalink / raw)
  To: gcc-patches, libstdc++, Glen Fernandes

[-- Attachment #1: Type: text/plain, Size: 1842 bytes --]

Glen Fernandes is already working on a patch, so I decided to finish
off my changes and throw them up on the Patch List so that Glen can
access the tests parts that I had written (they are not incredibly
extensive but do pass).

    For a small implementation description: the code I use does the
same in-place, fused-allocation technique used by other internal
classes of shared_ptr, and also creates some helpers in a new
bits/multi_dim.h header to simplify accessing multiple dimensions in a
proper order (useful for destruction, but construction is easy
enough). This might also come in handy down the road when mdspan
(finally) gets standardized.

     This implementation does not update the internal
__allocate_shared and __make_shared functions (I don't know why there
seems to be a duplicate front-end for those functions: it seems a
little weird to use both? Maybe it's for legacy reasons, albeit if
that is the case then I don't need to update the internal versions and
people should move to the non-internal version, yes?).

2020-01-02  JeanHeyd "ThePhD" Meneide  <phdofthehouse@gmail.com>

        * include/bits/multi_dim.h (new): New helpers for multi
          dimensional access.
        * include/bits/alloc_traits.h: New helpers for multi dimensional
          array construction and destruction.
        * include/bits/allocated_ptr.h: New guard type for sized
          allocation.
        * include/bits/memoryfwd.h: std::align forward declaration.
        * include/bits/shared_ptr.h: Changes to support multi
          dimensional array access with optimized size/extent storage.
        * include/bits/stl_construct.h: Formatting.
        * testsuite/20_util/shared_ptr/creation.array_support.allocate.cc:
          New test.
        * testsuite/20_util/shared_ptr/creation.array_support.make.cc:
          New test.

[-- Attachment #2: p0674r1.patch.txt --]
[-- Type: text/plain, Size: 104690 bytes --]

diff --git a/libstdc++-v3/include/bits/alloc_traits.h b/libstdc++-v3/include/bits/alloc_traits.h
index 061d353e3f0..39dcb639143 100644
--- a/libstdc++-v3/include/bits/alloc_traits.h
+++ b/libstdc++-v3/include/bits/alloc_traits.h
@@ -37,6 +37,9 @@
 # include <bits/ptr_traits.h>
 # include <ext/numeric_traits.h>
 #endif
+#if __cplusplus > 201703L
+#include <bits/multi_dim.h>
+#endif // C++20 and above multi dimensional tools
 
 namespace std _GLIBCXX_VISIBILITY(default)
 {
@@ -703,6 +706,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Alloc>
     using _RequireNotAllocator
       = typename enable_if<!__is_allocator<_Alloc>::value, _Alloc>::type;
+
+  template <typename _Alloc>
+    using __alloc_size
+      = typename allocator_traits<_Alloc>::size_type;
 #endif // C++11
 
   /**
@@ -733,6 +740,189 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Destroy(__first, __last);
     }
 
+
+
+#if __cplusplus > 201703L
+  /// Helpers for getting multi-dimensional construction an destruction
+  /// done properly. Obeys C++ Standard rules for array acccess: works
+  /// in constexpr.
+
+  template<typename _ElemAlloc, typename _Ty, typename _Init>
+    constexpr void
+    __recursive_element_construct_1 (_ElemAlloc& __elem_alloc,
+				     _Ty* __ptr,
+				     __alloc_size<_ElemAlloc>& __constructed,
+				     __alloc_size<_ElemAlloc> __extent,
+				     const _Init& __value)
+    {
+      using _SizeType = __alloc_size<_ElemAlloc>;
+      if constexpr (rank_v<_Ty> == 0)
+	{
+	  for (_SizeType __idx = 0; __idx < __extent; ++__idx)
+	    {
+	      _Ty* __element_ptr = __ptr + __idx; 
+	      allocator_traits<_ElemAlloc>::construct(__elem_alloc, 
+				       __element_ptr, 
+				       __value[__idx]);
+	      ++__constructed;
+	    }
+	}
+      else
+	{
+	  using _Tl = std::remove_extent_t<_Ty>;
+	  for (_SizeType __idx = 0; __idx < __extent; ++__idx)
+	    {
+	      _Tl* __elem_ptr = __ptr[__idx];
+	      __recursive_element_construct_1(__elem_alloc,
+			      __elem_ptr, 
+			      __constructed, 
+			      extent_v<_Ty>,
+			      __value[__idx]);
+	    }
+	}
+    }
+
+  template<typename _Ty, typename _ElemAlloc, typename _Init>
+    constexpr void
+    __recursive_element_construct (_ElemAlloc& __elem_alloc, _Ty* __ptr,
+				   __alloc_size<_ElemAlloc>& __constructed,
+				   __alloc_size<_ElemAlloc> __extent,
+				   const _Init& __value)
+    {
+      using _SizeType = __alloc_size<_ElemAlloc>;
+      if constexpr (rank_v<_Ty> == 0)
+	{
+	  for (_SizeType __idx = 0; __idx < __extent; ++__idx)
+	    {
+	      _Ty* __elem_ptr = __ptr + __idx;
+	      allocator_traits<_ElemAlloc>::construct(__elem_alloc,
+						      __elem_ptr,
+						      __value);
+	      ++__constructed;
+	    }
+	}
+      else
+	{
+	  using _Tl = std::remove_extent_t<_Ty>;
+	  for (_SizeType __idx = 0; __idx < __extent; ++__idx)
+	    {
+	      _Tl* __elem_ptr = __ptr[__idx];
+	      __recursive_element_construct_1(__elem_alloc,
+			    __elem_ptr, 
+			    __constructed, 
+			    extent_v<_Ty>,
+			    __value);
+	    }
+	}
+    }
+
+  template<typename _Ty, typename _Alloc, typename... _Args>
+    constexpr void
+    __unbounded_array_construct (_Alloc& __alloc, _Ty* __ptr,
+		    __alloc_size<_Alloc> __extent,
+		    __alloc_size<_Alloc>& __constructed,
+		    _Args&&... __args)
+    {
+      static_assert((sizeof...(_Args) == 0) || (sizeof...(_Args) == 1), 
+	"must either be value-initialized or copy-initialized");
+      if constexpr (sizeof...(_Args) == 0)
+	{
+	  for (__alloc_size<_Alloc> __idx = 0; __idx < __extent; ++__idx)
+	    {
+	      _Ty* __elem_ptr = __ptr + __idx;
+	      allocator_traits<_Alloc>::construct(__alloc, __elem_ptr);
+	      __constructed += sizeof(_Ty) / sizeof(remove_all_extents_t<_Ty>);
+	    }
+	}
+      else 
+	{
+	  using _ElemAlloc = __alloc_rebind<_Alloc, remove_all_extents_t<_Ty>>;
+	  _ElemAlloc __elem_alloc{__alloc};
+	  const _Ty& __value{forward<_Args>(__args)...};
+	  __recursive_element_construct(__elem_alloc, __ptr, __constructed,
+			  __extent,
+			  __value);
+	}
+    }
+
+  template<typename _Ty, typename _Alloc, typename... _Args>
+    constexpr void
+    __array_construct (_Alloc& __alloc, _Ty& __arr,
+		    __alloc_size<_Alloc>& __constructed,
+		    _Args&&... __args)
+    {
+      __unbounded_array_construct(__alloc, __arr, extent_v<_Ty>, __constructed, forward<_Args>(__args)...);
+    }
+
+  template<typename _ElemAlloc, typename _Ty, typename _Access>
+    constexpr void __array_destroy_element_at (_ElemAlloc& __elem_alloc, _Ty* __ptr,
+			   const _Access& __access,
+			   __alloc_size<_ElemAlloc> __access_idx) noexcept
+    {
+      constexpr size_t _Rank = rank_v<_Ty>;
+      if constexpr (_Rank == 0)
+	{
+	  _Ty* __elem_ptr = __ptr + __access[__access_idx];
+	  allocator_traits<_ElemAlloc>::destroy(__elem_alloc, __elem_ptr);
+	}
+      else 
+	{
+	  using _Tl = remove_extent_t<_Ty>;
+	  _Tl* __elem_ptr = __ptr[__access[__access_idx]];
+	  __array_destroy_element_at(__elem_alloc, 
+		        __elem_ptr,
+		        __access,
+		        __access_idx + 1);
+	}
+    }
+
+  template<typename _Ty, typename _Alloc>
+    constexpr void __unbounded_array_destroy (_Alloc& __alloc, _Ty* __arr,
+		        __alloc_size<_Alloc> __extent,
+		        __alloc_size<_Alloc> __count) noexcept
+    {
+      if (__count == 0)
+	return;
+      
+      using _SizeType = __alloc_size<_Alloc>;
+      constexpr size_t __total_sub_extents = __total_extent_v<_Ty>;
+      _SizeType __max_elements = (__total_sub_extents == 0
+        ? 1 
+	: __total_sub_extents)
+	* __extent;
+      if (__max_elements == __count)
+	{
+	  // if possible, shortcut the shenanigans
+	  for (_SizeType __idx = __extent; __idx-- > 0; --__idx)
+	    {
+	      _Ty* __elem_ptr = __arr + __idx;
+	      allocator_traits<_Alloc>::destroy(__alloc, __elem_ptr);
+	    }
+
+	  return;
+	}
+      
+      using _ElemAlloc = __alloc_rebind<_Alloc, remove_all_extents_t<_Ty>>;
+      constexpr size_t _Rank = 1 + rank_v<_Ty>;
+      const auto __extents = __extents_of<_Ty>(__extent);
+      const auto __strides = __strides_of<_Rank>(__extents);
+      _ElemAlloc __elem_alloc{__alloc};
+      for (_SizeType __idx = __count; __idx-- > 0 ;)
+	{
+	  auto __multi_idx = __index_to_multi_index<_Rank>(__idx, __strides);
+	  __array_destroy_element_at(__elem_alloc, __arr, __multi_idx, 0);
+	}
+    }
+
+  template<typename _Ty, typename _Alloc>
+    constexpr void __array_destroy (_Alloc& __alloc, _Ty& __arr,
+				    __alloc_size<_Alloc> __count) noexcept
+    {
+      return __unbounded_array_destroy(__alloc, __arr, 
+				       extent_v<_Ty>, __count);
+    }
+#endif // C++20 array allocator construction helpers
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace std
 #endif // _ALLOC_TRAITS_H
diff --git a/libstdc++-v3/include/bits/allocated_ptr.h b/libstdc++-v3/include/bits/allocated_ptr.h
index 5058ab08466..84857185924 100644
--- a/libstdc++-v3/include/bits/allocated_ptr.h
+++ b/libstdc++-v3/include/bits/allocated_ptr.h
@@ -97,6 +97,77 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       return { __a, std::allocator_traits<_Alloc>::allocate(__a, 1) };
     }
 
+#if __cplusplus > 201703L
+
+  template <typename _Alloc>
+    using __alloc_size_t =
+      typename ::std::allocator_traits<_Alloc>::size_type;
+
+  /// Non-standard RAII type for managing sized pointers
+  /// obtained from allocators with additional information.
+  template<typename _Alloc>
+    struct __sized_allocated_ptr
+    {
+      using pointer = typename allocator_traits<_Alloc>::pointer;
+      using value_type = typename allocator_traits<_Alloc>::value_type;
+      using size_type = __alloc_size_t<_Alloc>;
+
+      /// Take ownership of __ptr
+      __sized_allocated_ptr(_Alloc& __a, size_type __ptr_size,
+		      pointer __ptr) noexcept
+      : _M_alloc(std::__addressof(__a)), _M_ptr(__ptr), _M_size(__ptr_size)
+      { }
+
+      /// Convert __ptr to allocator's pointer type and take ownership of it
+      template<typename _Ptr,
+	       typename _Req = _Require<is_same<_Ptr, value_type*>>>
+      __sized_allocated_ptr(_Alloc& __a, size_type __ptr_size,
+		      _Ptr __ptr) noexcept
+      : _M_alloc(std::__addressof(__a)),
+	_M_ptr(pointer_traits<pointer>::pointer_to(*__ptr)),
+	_M_size(__ptr_size)
+      { }
+
+      /// Transfer ownership of the owned pointer
+      __sized_allocated_ptr(__sized_allocated_ptr&& __gd) noexcept
+      : _M_alloc(__gd._M_alloc), _M_ptr(__gd._M_ptr), _M_size(__gd._M_size)
+      { __gd._M_ptr = nullptr; }
+
+      /// Deallocate the owned pointer
+      ~__sized_allocated_ptr()
+      {
+	if (_M_ptr != nullptr)
+	  std::allocator_traits<_Alloc>::deallocate(*_M_alloc, _M_ptr, _M_size);
+      }
+
+      /// Release ownership of the owned pointer
+      __sized_allocated_ptr&
+      operator=(std::nullptr_t) noexcept
+      {
+	_M_ptr = nullptr;
+	return *this;
+      }
+
+      /// Get the address that the owned pointer refers to.
+      value_type* get() { return std::__to_address(_M_ptr); }
+
+    private:
+      _Alloc* _M_alloc;
+      pointer _M_ptr;
+      size_type _M_size;
+    };
+
+  /// Allocate space for maybe multiple objects using __a
+  template<typename _Alloc>
+    __sized_allocated_ptr<_Alloc>
+    __allocate_guarded_size(_Alloc& __a,
+      typename ::std::allocator_traits<_Alloc>::size_type __ptr_size)
+    {
+      return { __a, __ptr_size,
+	      std::allocator_traits<_Alloc>::allocate(__a, __ptr_size) };
+    }
+#endif // C++20 and above unbounded array sized allocate support
+
 _GLIBCXX_END_NAMESPACE_VERSION
 } // namespace std
 
diff --git a/libstdc++-v3/include/bits/memoryfwd.h b/libstdc++-v3/include/bits/memoryfwd.h
index af1a1c69c64..4d246fd6123 100644
--- a/libstdc++-v3/include/bits/memoryfwd.h
+++ b/libstdc++-v3/include/bits/memoryfwd.h
@@ -72,6 +72,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   /// Declare uses_allocator so it can be specialized in \<queue\> etc.
   template<typename, typename>
     struct uses_allocator;
+
+  /// Declare align so it can be used in shared_ptr_base
+  void*
+  align(size_t, size_t, void*&, size_t&) noexcept;
 #endif
 
   /// @} group memory
diff --git a/libstdc++-v3/include/bits/multi_dim.h b/libstdc++-v3/include/bits/multi_dim.h
new file mode 100644
index 00000000000..a9189f9e0e9
--- /dev/null
+++ b/libstdc++-v3/include/bits/multi_dim.h
@@ -0,0 +1,117 @@
+// Multi dimensional tools and helpers -*- C++ -*-
+
+// Copyright (C) 2011-2019 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+// <http://www.gnu.org/licenses/>.
+
+/** @file bits/mutli_dim.h
+ *  This is an internal header file, included by other library headers.
+ *  Do not attempt to use it directly. @headername{memory}
+ */
+
+#ifndef _MULTI_DIM_H
+#define _MULTI_DIM_H 1
+
+#if __cplusplus > 201703L
+
+#include <array>
+#include <type_traits>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+  template<typename _Ty>
+    struct __total_extent : extent<_Ty> {};
+
+  template<typename _Ty, size_t _Extent>
+    struct __total_extent<_Ty[_Extent]>
+    : ::std::integral_constant<size_t, extent<_Ty>::value == 0
+	? _Extent
+	: __total_extent<_Ty>::value * _Extent> {};
+
+  template<typename _Ty>
+    // internal: total_extent helper
+   constexpr inline size_t __total_extent_v = __total_extent<_Ty>::value;
+
+  template <typename _Ty, typename _SizeType = size_t, size_t... _Idx, typename... _Args>
+    constexpr std::array<_SizeType, sizeof...(_Args) + rank_v<_Ty>>
+    __extents_of (index_sequence<_Idx...>, _Args&&... __args) noexcept
+    {
+      std::array<_SizeType, sizeof...(_Args) + rank_v<_Ty>> __extents{
+	std::forward<_Args>(__args)..., extent_v<_Ty, _Idx>...
+      };
+      return __extents;
+    }
+
+  template <typename _Ty, typename _SizeType = size_t, typename... _Args>
+    constexpr std::array<_SizeType, sizeof...(_Args) + rank_v<_Ty>>
+    __extents_of (_Args&&... __args) noexcept
+    {
+      return __extents_of<_Ty>(make_index_sequence<rank_v<_Ty>>(),
+			       std::forward<_Args>(__args)...);
+    }
+
+  template <typename _Ty, typename _SizeType = size_t, typename _Extents>
+    constexpr array<_SizeType, rank_v<_Ty>>
+    __strides_of (const _Extents& __extents) noexcept
+    {
+      array<_SizeType, rank_v<_Ty>> __strides{};
+      _SizeType __rank_stride = 1;
+      for (size_t __rank_idx = rank_v<_Ty>; __rank_idx-- > 0;) {
+	__strides[__rank_idx] = __rank_stride;
+	__rank_stride *= __extents[__rank_idx];
+      }
+      return __strides;
+    }
+
+  template <size_t _Rank, typename _SizeType = size_t, typename _Extents>
+    constexpr array<_SizeType, _Rank>
+    __strides_of (const _Extents& __extents) noexcept
+    {
+      array<_SizeType, _Rank> __strides{};
+      _SizeType __rank_stride = 1;
+      for (size_t __rank_idx = _Rank; __rank_idx-- > 0;) {
+	__strides[__rank_idx] = __rank_stride;
+	__rank_stride *= __extents[__rank_idx];
+      }
+      return __strides;
+    }
+
+  template <size_t _Rank, typename _SizeType, typename _Strides>
+    constexpr std::array<_SizeType, _Rank>
+    __index_to_multi_index(_SizeType __idx, const _Strides& __strides) noexcept
+    {
+      std::array<_SizeType, _Rank> __multi_index{};
+      for (size_t __rank_index = 0; __rank_index < _Rank; ++__rank_index) {
+	_SizeType __stride = __strides[__rank_index];
+	__multi_index[__rank_index] = __idx / __stride;
+	__idx -= __multi_index[__rank_index] * __stride;
+      }
+      return __multi_index;
+    }
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace
+
+#endif // C++20 or above
+
+#endif /* _MULTI_DIM_H */
diff --git a/libstdc++-v3/include/bits/shared_ptr.h b/libstdc++-v3/include/bits/shared_ptr.h
index c4df3582e20..2d1a8a2bba3 100644
--- a/libstdc++-v3/include/bits/shared_ptr.h
+++ b/libstdc++-v3/include/bits/shared_ptr.h
@@ -99,6 +99,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
     }
 
+  namespace __detail
+  {
+    // friendship cooperation due to C++20 because constraints
+    // do not handle template function friending well at all
+    struct __shared_friend;
+  }
+
+
   /**
    *  @brief  A smart pointer with reference-counted copy semantics.
    *
@@ -408,9 +416,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	: __shared_ptr<_Tp>(__tag, std::forward<_Args>(__args)...)
 	{ }
 
-      template<typename _Yp, typename _Alloc, typename... _Args>
-	friend shared_ptr<_Yp>
-	allocate_shared(const _Alloc& __a, _Args&&... __args);
+#if __cplusplus > 201703L
+      // This constructor is non-standard, it is used by allocate_shared.
+      template<typename _Alloc, typename... _Args>
+	shared_ptr(in_place_type_t<_Tp> __type_tag,
+		   _Sp_alloc_shared_tag<_Alloc> __tag, _Args&&... __args)
+	: __shared_ptr<_Tp>(__type_tag, __tag, std::forward<_Args>(__args)...)
+	{ }
+#endif // C++20 support for proper array make_shared
+
+      friend struct __detail::__shared_friend;
 
       // This constructor is non-standard, it is used by weak_ptr::lock().
       shared_ptr(const weak_ptr<_Tp>& __r, std::nothrow_t)
@@ -419,6 +434,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       friend class weak_ptr<_Tp>;
     };
 
+    namespace __detail
+    {
+      struct __shared_friend
+	{
+	  template <typename _Tp, typename _Alloc, typename... _Args>
+	    static shared_ptr<_Tp>
+	    __allocate_shared (_Sp_alloc_shared_tag<_Alloc> __tag, 
+			       _Args&&... __args)
+	    {
+#if __cplusplus <= 201703L
+	      return shared_ptr<_Tp> (__tag, ::std::forward<_Args>(__args)...);
+#else
+	      return shared_ptr<_Tp> (in_place_type<_Tp>, __tag,
+				      ::std::forward<_Args>(__args)...);
+#endif
+	    }
+	};
+    }
+
 #if __cpp_deduction_guides >= 201606
   template<typename _Tp>
     shared_ptr(weak_ptr<_Tp>) ->  shared_ptr<_Tp>;
@@ -825,6 +859,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   /// @relates shared_ptr @{
 
+#if __cplusplus <= 201703L
+
   /**
    *  @brief  Create an object that is owned by a shared_ptr.
    *  @param  __a     An allocator.
@@ -838,10 +874,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
    */
   template<typename _Tp, typename _Alloc, typename... _Args>
     inline shared_ptr<_Tp>
-    allocate_shared(const _Alloc& __a, _Args&&... __args)
+    allocate_shared (const _Alloc& __a, _Args&&... __args)
     {
-      return shared_ptr<_Tp>(_Sp_alloc_shared_tag<_Alloc>{__a},
-			     std::forward<_Args>(__args)...);
+      return __detail::__shared_friend::__allocate_shared<_Tp> (
+	_Sp_alloc_shared_tag<_Alloc>{__a}, std::forward<_Args>(__args)...);
     }
 
   /**
@@ -860,6 +896,190 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 				       std::forward<_Args>(__args)...);
     }
 
+#else
+
+  /**
+   *  @brief  Create an object that is owned by a shared_ptr.
+   *  @param  __a     An allocator.
+   *  @param  __args  Arguments for the @a _Tp object's constructor.
+   *  @return A shared_ptr that owns the newly created object.
+   *  @throw  An exception thrown from @a _Alloc::allocate or from the
+   *          constructor of @a _Tp.
+   *
+   *  A copy of @a __a will be used to allocate memory for the shared_ptr
+   *  and the new object.
+   */
+  template<typename _Tp, typename _Alloc, typename... _Args>
+    requires (!is_array_v<_Tp>)
+    inline shared_ptr<_Tp>
+    allocate_shared (const _Alloc& __a, _Args&&... __args)
+    {
+      return __detail::__shared_friend::__allocate_shared<_Tp> (
+	_Sp_alloc_shared_tag<_Alloc>{__a},
+	std::forward<_Args>(__args)...);
+    }
+
+  /**
+   *  @brief  Create a bounded array object that is owned by a shared_ptr,
+   *          with all elements given an initial value of __value.
+   *  @param  __value  Initial value to construct all elements with.
+   *  @return A shared_ptr that owns the newly created bounded array object.
+   *  @throw  std::bad_alloc, or an exception thrown from the
+   *          copy constructor of @a std::remove_extent_t<_Tp>.
+   */
+  template<typename _Tp, typename _Alloc>
+    requires (is_bounded_array_v<_Tp>)
+    inline shared_ptr<_Tp>
+    allocate_shared (const _Alloc& __a, const remove_extent_t<_Tp>& __value)
+    {
+      return __detail::__shared_friend::__allocate_shared<_Tp> (
+	_Sp_alloc_shared_tag<_Alloc>{__a},
+	__value);
+    }
+
+  /**
+   *  @brief  Create a bounded array object that is owned by a shared_ptr,
+   *          with the elements value initialized.
+   *  @return A shared_ptr that owns the newly created bounded array object.
+   *  @throw  std::bad_alloc, or an exception thrown from the
+   *          value initialization of @a std::remove_extent_t<_Tp>.
+   */
+  template<typename _Tp, typename _Alloc>
+    requires (is_bounded_array_v<_Tp>)
+    inline shared_ptr<_Tp>
+    allocate_shared (const _Alloc& __a)
+    {
+      return __detail::__shared_friend::__allocate_shared<_Tp> (
+	_Sp_alloc_shared_tag<_Alloc>{__a});
+    }
+
+  /**
+   *  @brief  Create an array object that is owned by a shared_ptr, 
+   *          with copy constructed elements.
+   *  @param  __value  Initial value to construct all elements with.
+   *  @param  __num_elements  The number of elements to create the array with.
+   *  @return A shared_ptr that owns the newly created bounded array object.
+   *  @throw  std::bad_alloc, or an exception thrown from the
+   *          copy constructor of @a std::remove_extent_t<_Tp>.
+   */
+  template<typename _Tp, typename _Alloc>
+    requires (is_unbounded_array_v<_Tp>)
+    inline shared_ptr<_Tp>
+    allocate_shared (const _Alloc& __a, 
+		    __alloc_size_t<_Alloc> __num_elements,
+		    const remove_extent_t<_Tp>& __value)
+    {
+      return __detail::__shared_friend::__allocate_shared<_Tp> (
+	_Sp_alloc_shared_tag<_Alloc>{__a}, __num_elements, __value);
+    }
+
+  /**
+   *  @brief  Create an array object that is owned by a shared_ptr, 
+   *          with value-initialized elements.
+   *  @param  __num_elements  The number of elements to create the array with.
+   *  @return A shared_ptr that owns the newly created array object of size __num_elements.
+   *  @throw  std::bad_alloc, or an exception thrown from the
+   *          value initialization of @a std::remove_extent_t<_Tp>.
+   */
+  template<typename _Tp, typename _Alloc>
+    requires (is_unbounded_array_v<_Tp>)
+    inline shared_ptr<_Tp>
+    allocate_shared (const _Alloc& __a,
+		    __alloc_size_t<_Alloc> __num_elements)
+    {
+      return __detail::__shared_friend::__allocate_shared<_Tp> (
+	_Sp_alloc_shared_tag<_Alloc>{__a}, __num_elements);
+    }
+
+  /**
+   *  @brief  Create an object that is owned by a shared_ptr.
+   *  @param  __args  Arguments for the @a _Tp object's constructor.
+   *  @return A shared_ptr that owns the newly created object.
+   *  @throw  std::bad_alloc, or an exception thrown from the
+   *          constructor of @a _Tp.
+   */
+  template<typename _Tp, typename... _Args>
+    requires (!is_array_v<_Tp>)
+    inline shared_ptr<_Tp>
+    make_shared(_Args&&... __args)
+    {
+      using _Tp_nc = remove_cv_t<_Tp>;
+      return ::std::allocate_shared<_Tp>(std::allocator<_Tp_nc>(),
+					 std::forward<_Args>(__args)...);
+    }
+
+  /**
+   *  @brief  Create a bounded array object that is owned by a shared_ptr,
+   *          with all elements given an initial value of __value.
+   *  @param  __value  Initial value to construct all elements with.
+   *  @return A shared_ptr that owns the newly created bounded array object.
+   *  @throw  std::bad_alloc, or an exception thrown from the
+   *          copy constructor of @a std::remove_extent_t<_Tp>.
+   */
+  template<typename _Tp>
+    requires (is_bounded_array_v<_Tp>)
+    inline shared_ptr<_Tp>
+    make_shared(const remove_extent_t<_Tp>& __value)
+    {
+      using _Tp_nc = remove_cv_t<_Tp>;
+      return ::std::allocate_shared<_Tp>(std::allocator<_Tp_nc>(), __value);
+    }
+
+  /**
+   *  @brief  Create a bounded array object that is owned by a shared_ptr,
+   *          with the elements value initialized.
+   *  @return A shared_ptr that owns the newly created bounded array object.
+   *  @throw  std::bad_alloc, or an exception thrown from the
+   *          value initialization of @a std::remove_extent_t<_Tp>.
+   */
+  template<typename _Tp>
+    requires (is_bounded_array_v<_Tp>)
+    inline shared_ptr<_Tp>
+    make_shared()
+    {
+      using _Tp_nc = remove_cv_t<_Tp>;
+      return ::std::allocate_shared<_Tp>(std::allocator<_Tp_nc>());
+    }
+
+  /**
+   *  @brief  Create an array object that is owned by a shared_ptr, 
+   *          with copy constructed elements.
+   *  @param  __value  Initial value to construct all elements with.
+   *  @param  __num_elements  The number of elements to create the array with.
+   *  @return A shared_ptr that owns the newly created bounded array object.
+   *  @throw  std::bad_alloc, or an exception thrown from the
+   *          copy constructor of @a std::remove_extent_t<_Tp>.
+   */
+  template<typename _Tp>
+    requires (is_unbounded_array_v<_Tp>)
+    inline shared_ptr<_Tp>
+    make_shared(size_t __num_elements, const remove_extent_t<_Tp>& __value)
+    {
+      using _Tp_nc = remove_cv_t<remove_extent_t<_Tp>>;
+      return ::std::allocate_shared<_Tp>(std::allocator<_Tp_nc>(),
+					 __num_elements, __value);
+    }
+
+  /**
+   *  @brief  Create an array object that is owned by a shared_ptr, 
+   *          with value-initialized elements.
+   *  @param  __num_elements  The number of elements to create the array with.
+   *  @return A shared_ptr that owns the newly created array object of size __num_elements.
+   *  @throw  std::bad_alloc, or an exception thrown from the
+   *          value initialization of @a std::remove_extent_t<_Tp>.
+   */
+  template<typename _Tp>
+    requires (is_unbounded_array_v<_Tp>)
+    inline shared_ptr<_Tp>
+    make_shared(size_t __num_elements)
+    {
+      using _Tp_nc = remove_cv_t<remove_extent_t<_Tp>>;
+      return ::std::allocate_shared<_Tp>(std::allocator<_Tp_nc>(),
+					 __num_elements);
+    }
+
+#endif // C++17 and below | C++20 and above make/allocate_shared
+
   /// std::hash specialization for shared_ptr.
   template<typename _Tp>
     struct hash<shared_ptr<_Tp>>
diff --git a/libstdc++-v3/include/bits/shared_ptr_base.h b/libstdc++-v3/include/bits/shared_ptr_base.h
index c9017ede4cb..e0b2e5774ab 100644
--- a/libstdc++-v3/include/bits/shared_ptr_base.h
+++ b/libstdc++-v3/include/bits/shared_ptr_base.h
@@ -54,6 +54,9 @@
 #include <bits/refwrap.h>
 #include <bits/stl_function.h>
 #include <ext/aligned_buffer.h>
+#if __cplusplus > 201703L
+#include <utility>
+#endif // C++20 and above make/allocate_shared array support
 
 namespace std _GLIBCXX_VISIBILITY(default)
 {
@@ -89,6 +92,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   using __gnu_cxx::_S_mutex;
   using __gnu_cxx::_S_atomic;
 
+  class _Zero
+  { };
+
   // Empty helper class except when the template argument is _S_mutex.
   template<_Lock_policy _Lp>
     class _Mutex_base
@@ -363,6 +369,38 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     class __shared_count;
 
 
+  template <typename _Tp>
+    using __Sp_needs_alloc_size =
+      integral_constant<bool, is_array<_Tp>::value
+			      && (extent<_Tp>::value == 0)>;
+
+  template<typename _SizeType, size_t _Extent>
+    class _Sp_alloc_size
+    {
+    public:
+      _Sp_alloc_size(_SizeType __size) noexcept
+      { __glibcxx_assert(__size == _Extent); }
+
+      static _SizeType
+      _M_alloc_size () noexcept
+      { return _Extent; }
+    };
+
+  template<typename _SizeType>
+    class _Sp_alloc_size<_SizeType, 0>
+    {
+    public:
+      _Sp_alloc_size(_SizeType __size) noexcept
+      : _M_size(__size)
+      { }
+
+      _SizeType
+      _M_alloc_size() const noexcept 
+      { return _M_size; }
+
+      _SizeType _M_size;
+    };
+
   // Counted ptr with no deleter or allocator support
   template<typename _Ptr, _Lock_policy _Lp>
     class _Sp_counted_ptr final : public _Sp_counted_base<_Lp>
@@ -416,6 +454,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       static _Tp&
       _S_get(_Sp_ebo_helper& __eboh) { return static_cast<_Tp&>(__eboh); }
+
+      static const _Tp&
+      _S_get(const _Sp_ebo_helper& __eboh)
+      { return static_cast<const _Tp&>(__eboh); }
     };
 
   /// Specialization not using EBO.
@@ -429,6 +471,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _S_get(_Sp_ebo_helper& __eboh)
       { return __eboh._M_tp; }
 
+      static const _Tp&
+      _S_get(const _Sp_ebo_helper& __eboh)
+      { return __eboh._M_tp; }
+
     private:
       _Tp _M_tp;
     };
@@ -437,18 +483,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Ptr, typename _Deleter, typename _Alloc, _Lock_policy _Lp>
     class _Sp_counted_deleter final : public _Sp_counted_base<_Lp>
     {
-      class _Impl : _Sp_ebo_helper<0, _Deleter>, _Sp_ebo_helper<1, _Alloc>
+      using _Del_base = _Sp_ebo_helper<0, _Deleter>;
+      using _Alloc_base = _Sp_ebo_helper<1, _Alloc>;
+      
+      class _Impl : _Del_base, _Alloc_base
       {
-	typedef _Sp_ebo_helper<0, _Deleter>	_Del_base;
-	typedef _Sp_ebo_helper<1, _Alloc>	_Alloc_base;
-
       public:
 	_Impl(_Ptr __p, _Deleter __d, const _Alloc& __a) noexcept
 	: _M_ptr(__p), _Del_base(std::move(__d)), _Alloc_base(__a)
 	{ }
 
-	_Deleter& _M_del() noexcept { return _Del_base::_S_get(*this); }
-	_Alloc& _M_alloc() noexcept { return _Alloc_base::_S_get(*this); }
+	_Deleter& _M_del() noexcept
+	{ return _Del_base::_S_get(*this); }
+
+	_Alloc& _M_alloc() noexcept
+	{ return _Alloc_base::_S_get(*this); }
 
 	_Ptr _M_ptr;
       };
@@ -468,7 +517,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
       virtual void
       _M_dispose() noexcept
-      { _M_impl._M_del()(_M_impl._M_ptr); }
+      {
+	_M_impl._M_del()(_M_impl._M_ptr);
+      }
 
       virtual void
       _M_destroy() noexcept
@@ -503,6 +554,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   private:
     template<typename _Tp, typename _Alloc, _Lock_policy _Lp>
       friend class _Sp_counted_ptr_inplace;
+    template<typename _Tp, typename _Alloc, size_t _Extent, _Lock_policy _Lp>
+      friend class _Sp_counted_sized_ptr_inplace;
 
     static const type_info&
     _S_ti() noexcept _GLIBCXX_VISIBILITY(default)
@@ -523,14 +576,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<typename _Tp, typename _Alloc, _Lock_policy _Lp>
     class _Sp_counted_ptr_inplace final : public _Sp_counted_base<_Lp>
     {
-      class _Impl : _Sp_ebo_helper<0, _Alloc>
-      {
-	typedef _Sp_ebo_helper<0, _Alloc>	_A_base;
+      using _Alloc_base = _Sp_ebo_helper<0, _Alloc>;
 
+      class _Impl : _Alloc_base
+      {
       public:
-	explicit _Impl(_Alloc __a) noexcept : _A_base(__a) { }
+	explicit _Impl(_Alloc __a) noexcept
+	: _Alloc_base(__a)
+	{ }
 
-	_Alloc& _M_alloc() noexcept { return _A_base::_S_get(*this); }
+	_Alloc& _M_alloc() noexcept { return _Alloc_base::_S_get(*this); }
 
 	__gnu_cxx::__aligned_buffer<_Tp> _M_storage;
       };
@@ -596,6 +651,179 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Impl _M_impl;
     };
 
+#if __cplusplus > 201703L
+  template<typename _Ty, typename _Alloc, size_t _Extent, _Lock_policy _Lp>
+    class _Sp_counted_sized_ptr_inplace final : public _Sp_counted_base<_Lp>
+    {
+      // it is the user's job to track
+      // allocation size and deletion size
+      // when they create something manually with an allocator
+      // and/or a deleter and pass it directly
+      // to std::shared_ptr's constructor,
+      // that's why this machinery is present nowhere else
+      // but here. Originally, we attempted to modify
+      // _Sp_counted_ptr_inplace,
+      // but function changes that relied on data members
+      // -- even ones that can be optimized out -- can
+      // affect ABI and thus blow up code.
+      // Therefore, we needed a new derived
+      // implementation.
+      using _Tp = conditional_t<_Extent == 0
+	, _Ty
+	, remove_extent_t<_Ty>>;
+      using _SizeType = __alloc_size_t<_Alloc>;
+      using _AllocSize = _Sp_alloc_size<_SizeType, _Extent>;
+      using _Alloc_base = _Sp_ebo_helper<0, _Alloc>;
+      using _Alloc_size_base = _Sp_ebo_helper<1, _AllocSize>;
+      using _StorageType = conditional_t<_Extent == 0
+	, _Zero
+	, __gnu_cxx::__aligned_buffer<_Ty>>;
+
+      alignas(_Ty) class _Impl : _Alloc_base, _Alloc_size_base
+      {
+      public:
+	explicit _Impl(_Alloc __a, _SizeType __size) noexcept 
+	: _Alloc_base(__a), _Alloc_size_base(__size)
+	{ }
+
+	_Alloc& _M_alloc() noexcept { return _Alloc_base::_S_get(*this); }
+	
+	_SizeType
+	_M_alloc_size() const noexcept
+	{ return _Alloc_size_base::_S_get(*this)._M_alloc_size(); }
+	
+	_SizeType
+	_M_Impl_size() const noexcept
+	{ return this->_S_Impl_size(this->_M_alloc_size()); }
+
+	static _SizeType
+	_S_Impl_size (_SizeType __num_elements) noexcept
+	{
+	  if constexpr (_Extent == 0)
+	    {
+	      _SizeType __division_step = sizeof(_Ty) - 1;
+	      _SizeType __offset
+		= static_cast<_SizeType>(sizeof(_Sp_counted_sized_ptr_inplace));
+	      return ((__offset + __division_step) / sizeof(_Ty))
+		+ __num_elements;
+	    }
+	  else
+	    {
+	      return 1;
+	    }
+	}
+
+	[[no_unique_address]] _StorageType _M_storage;
+      };
+
+    public:
+      using __allocator_type
+	= conditional_t<_Extent == 0
+	  , _Alloc
+	  , __alloc_rebind<_Alloc, _Sp_counted_sized_ptr_inplace>>;
+
+      // Alloc parameter is not a reference so doesn't alias anything in __args
+      // On failure (exception), destroy + rethrow
+      template<typename... _Args>
+	_Sp_counted_sized_ptr_inplace(_Alloc __a, _SizeType __num_elements,
+				      _Args&&... __args)
+	: _M_impl(__a, __num_elements)
+	{
+	  // constructors might throw...
+	  // FIXME: use destructor to unwind construct,
+	  // rather than __catch
+	  // (much faster codegen on many compilers)
+	  _SizeType __num{};
+	  __try 
+	    {
+	      __unbounded_array_construct (__a, this->_M_ptr(),
+					   this->_M_impl._M_alloc_size(),
+					   __num,
+					   std::forward<_Args>(__args)...);
+	      __glibcxx_assert(__num == this->_M_impl._M_alloc_size());
+	    }
+	  __catch(...)
+	    {
+	      __unbounded_array_destroy (__a, this->_M_ptr(),
+					 this->_M_impl._M_alloc_size(), __num);
+	      __throw_exception_again;
+	    }
+	}
+
+      ~_Sp_counted_sized_ptr_inplace() noexcept { }
+
+      virtual void
+      _M_dispose() noexcept
+      {
+	__unbounded_array_destroy (this->_M_impl._M_alloc(), this->_M_ptr(),
+				   this->_M_impl._M_alloc_size(),
+				   this->_M_impl._M_alloc_size());
+      }
+
+      // Override because the allocator needs to know the dynamic type
+      virtual void
+      _M_destroy() noexcept
+      {
+	using _Pointer = typename allocator_traits<__allocator_type>::pointer;
+	__allocator_type __al(this->_M_impl._M_alloc());
+	__sized_allocated_ptr<__allocator_type> __guard_ptr{ __al,
+	  this->_M_impl._M_Impl_size(), reinterpret_cast<_Pointer>(this) };
+	this->~_Sp_counted_sized_ptr_inplace();
+      }
+
+    private:
+      friend class __shared_count<_Lp>; // To be able to call _M_ptr().
+
+      // No longer used, but code compiled against old libstdc++ headers
+      // might still call it from __shared_ptr ctor to get the pointer out.
+      virtual void*
+      _M_get_deleter(const std::type_info& __ti) noexcept override
+      {
+	auto __ptr = const_cast<typename remove_cv<_Tp>::type*>(_M_ptr());
+	// Check for the fake type_info first, so we don't try to access it
+	// as a real type_info object. Otherwise, check if it's the real
+	// type_info for this class. With RTTI enabled we can check directly,
+	// or call a library function to do it.
+	if (&__ti == &_Sp_make_shared_tag::_S_ti()
+	    ||
+#if __cpp_rtti
+	    __ti == typeid(_Sp_make_shared_tag)
+#else
+	    _Sp_make_shared_tag::_S_eq(__ti)
+#endif
+	   )
+	  return __ptr;
+	return nullptr;
+      }
+
+      _Tp*
+      _M_ptr() noexcept
+      {
+	if constexpr (_Extent == 0)
+	  {
+	    void* __ptr = static_cast<void*>(this + 1);
+	    size_t __space = this->_M_impl._M_alloc_size()
+	      * sizeof(_Ty);
+	    void* __adjusted
+	      = std::align(alignof(_Ty), __space, __ptr, __space);
+	    // if this triggers then alignof() and friends failed us
+	    // at a language level
+	    __glibcxx_assert(__adjusted != nullptr);
+	    return reinterpret_cast<_Tp*>(__adjusted);
+	  }
+	else
+	  return this->_M_impl._M_storage._M_ptr()[0];
+      }
+
+      static _SizeType
+      _S_Impl_size (_SizeType __num_elements) noexcept
+      { return _Impl::_S_Impl_size(__num_elements); }
+
+      _Impl _M_impl;
+    };
+
+#endif // C++20 and above make/allocate_shared array support
+
   // The default deleter for shared_ptr<T[]> and shared_ptr<T[N]>.
   struct __sp_array_delete
   {
@@ -672,16 +900,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	__shared_count(_Tp*& __p, _Sp_alloc_shared_tag<_Alloc> __a,
 		       _Args&&... __args)
 	{
-	  typedef _Sp_counted_ptr_inplace<_Tp, _Alloc, _Lp> _Sp_cp_type;
-	  typename _Sp_cp_type::__allocator_type __a2(__a._M_a);
-	  auto __guard = std::__allocate_guarded(__a2);
-	  _Sp_cp_type* __mem = __guard.get();
-	  auto __pi = ::new (__mem)
-	    _Sp_cp_type(__a._M_a, std::forward<_Args>(__args)...);
-	  __guard = nullptr;
-	  _M_pi = __pi;
-	  __p = __pi->_M_ptr();
+	  this->__single_allocate(__p, __a, std::forward<_Args>(__args)...);
+	}
+
+#if __cplusplus > 201703L
+      template<typename _Real, typename _Tp, typename _Alloc, typename... _Args>
+	__shared_count(in_place_type_t<_Real>, _Tp*& __p,
+		       _Sp_alloc_shared_tag<_Alloc> __a, _Args&&... __args)
+	{
+	  this->__sized_allocate<_Real>(__p, std::move(__a),
+					std::forward<_Args>(__args)...);
 	}
+#endif // C++20 and above make/allocate_shared array support
 
 #if _GLIBCXX_USE_DEPRECATED
 #pragma GCC diagnostic push
@@ -788,6 +1018,77 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     private:
       friend class __weak_count<_Lp>;
 
+      template <typename _Tp, typename _Alloc, typename... _Args>
+	void __single_allocate (_Tp*& __p, _Sp_alloc_shared_tag<_Alloc> __a,
+			       _Args&&... __args)
+	{
+	  typedef _Sp_counted_ptr_inplace<_Tp, _Alloc, _Lp> _Sp_cp_type;
+	  typename _Sp_cp_type::__allocator_type __a2(__a._M_a);
+	  auto __guard = std::__allocate_guarded(__a2);
+	  _Sp_cp_type* __mem = __guard.get();
+	  auto __pi = ::new (__mem)
+	    _Sp_cp_type(__a._M_a, std::forward<_Args>(__args)...);
+	  __guard = nullptr;
+	  _M_pi = __pi;
+	  __p = __pi->_M_ptr();
+	}
+
+#if __cplusplus > 201703L
+      template<typename _Real, typename _Tp,
+	typename _Alloc, typename... _Args>
+	requires (is_unbounded_array_v<_Real>)
+	void __sized_allocate (_Tp*& __p,
+			       _Sp_alloc_shared_tag<_Alloc> __a,
+			       __alloc_size_t<_Alloc> __num_elements,
+			       _Args&&... __args)
+	{
+	  using _Sp_cp_type =
+	    _Sp_counted_sized_ptr_inplace<_Tp, _Alloc, 0, _Lp>;
+	  typename _Sp_cp_type::__allocator_type __a2(__a._M_a);
+	  __alloc_size<_Alloc> __impl_size
+	    = _Sp_cp_type::_S_Impl_size(__num_elements);
+	  auto __guard = std::__allocate_guarded_size(__a2, __impl_size);
+	  _Sp_cp_type* __mem = reinterpret_cast<_Sp_cp_type*>(__guard.get());
+	  auto __pi = 
+	    ::new (__mem) _Sp_cp_type(__a._M_a, __num_elements,
+				      std::forward<_Args>(__args)...);
+	  __guard = nullptr;
+	  _M_pi = __pi;
+	  __p = __pi->_M_ptr();
+	}
+
+      template<typename _Real, typename _Tp,
+	typename _Alloc, typename... _Args>
+	requires (is_bounded_array_v<_Real>)
+	void __sized_allocate (_Tp*& __p, 
+			       _Sp_alloc_shared_tag<_Alloc> __a,
+			       _Args&&... __args)
+	{
+	  using _Sp_cp_type =
+	    _Sp_counted_sized_ptr_inplace<remove_cv_t<_Real>
+	      , _Alloc, extent_v<remove_cv_t<_Real>>, _Lp>;
+	  typename _Sp_cp_type::__allocator_type __a2(__a._M_a);
+	  auto __guard = std::__allocate_guarded(__a2);
+	  _Sp_cp_type* __mem = __guard.get();
+	  auto __pi = 
+	    ::new (__mem) _Sp_cp_type(__a._M_a, extent_v<remove_cv_t<_Real>>,
+				      std::forward<_Args>(__args)...);
+	  __guard = nullptr;
+	  _M_pi = __pi;
+	  __p = __pi->_M_ptr();
+	}
+
+      template<typename _Real, typename _Tp,
+	typename _Alloc, typename... _Args>
+	requires (!is_array_v<_Real>)
+	void __sized_allocate (_Tp*& __p,
+			       _Sp_alloc_shared_tag<_Alloc> __a,
+			       _Args&&... __args)
+	{
+	  this->__single_allocate (__p, __a, std::forward<_Args>(__args)...);
+	}
+#endif // C++20 and above make/allocate_shared array support
+
       _Sp_counted_base<_Lp>*  _M_pi;
     };
 
@@ -1362,11 +1663,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // @}
 
     protected:
-      // This constructor is non-standard, it is used by allocate_shared.
+      // This constructor is non-standard, it is used by __allocate_shared.
       template<typename _Alloc, typename... _Args>
 	__shared_ptr(_Sp_alloc_shared_tag<_Alloc> __tag, _Args&&... __args)
 	: _M_ptr(), _M_refcount(_M_ptr, __tag, std::forward<_Args>(__args)...)
 	{ _M_enable_shared_from_this_with(_M_ptr); }
+	
+#if __cplusplus > 201703L
+      // This constructor is non-standard, it is used by allocate_shared.
+      template<typename _Alloc, typename... _Args>
+	__shared_ptr(in_place_type_t<_Tp> __type_tag,
+		     _Sp_alloc_shared_tag<_Alloc> __tag, _Args&&... __args)
+	: _M_ptr(),
+	_M_refcount(__type_tag, _M_ptr, __tag, std::forward<_Args>(__args)...)
+	{ _M_enable_shared_from_this_with(_M_ptr); }
+#endif // C++20 and above make/allocate_shared array support
 
       template<typename _Tp1, _Lock_policy _Lp1, typename _Alloc,
 	       typename... _Args>
diff --git a/libstdc++-v3/include/bits/stl_construct.h b/libstdc++-v3/include/bits/stl_construct.h
index 5c9a84d9497..914be803025 100644
--- a/libstdc++-v3/include/bits/stl_construct.h
+++ b/libstdc++-v3/include/bits/stl_construct.h
@@ -60,6 +60,9 @@
 #include <bits/move.h>
 #include <bits/stl_iterator_base_types.h> // for iterator_traits
 #include <bits/stl_iterator_base_funcs.h> // for advance
+#if __cplusplus > 201703L
+#include <array>
+#endif // C++20 array construction helpers
 
 /* This file provides the C++17 functions std::destroy_at, std::destroy, and
  * std::destroy_n, and the C++20 function std::construct_at.
@@ -163,8 +166,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct _Destroy_aux<true>
     {
       template<typename _ForwardIterator>
-        static void
-        __destroy(_ForwardIterator, _ForwardIterator) { }
+	static void
+	__destroy(_ForwardIterator, _ForwardIterator) { }
     };
 
   /**
@@ -177,7 +180,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Destroy(_ForwardIterator __first, _ForwardIterator __last)
     {
       typedef typename iterator_traits<_ForwardIterator>::value_type
-                       _Value_type;
+		       _Value_type;
 #if __cplusplus >= 201103L
       // A deleted destructor is trivial, this ensures we reject such types:
       static_assert(is_destructible<_Value_type>::value,
@@ -208,8 +211,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     struct _Destroy_n_aux<true>
     {
       template<typename _ForwardIterator, typename _Size>
-        static _ForwardIterator
-        __destroy_n(_ForwardIterator __first, _Size __count)
+	static _ForwardIterator
+	__destroy_n(_ForwardIterator __first, _Size __count)
 	{
 	  std::advance(__first, __count);
 	  return __first;
@@ -226,7 +229,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
     _Destroy_n(_ForwardIterator __first, _Size __count)
     {
       typedef typename iterator_traits<_ForwardIterator>::value_type
-                       _Value_type;
+		       _Value_type;
 #if __cplusplus >= 201103L
       // A deleted destructor is trivial, this ensures we reject such types:
       static_assert(is_destructible<_Value_type>::value,
diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/creation/array_support.allocate.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/creation/array_support.allocate.cc
new file mode 100644
index 00000000000..23f0cc702f8
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/shared_ptr/creation/array_support.allocate.cc
@@ -0,0 +1,153 @@
+// Copyright (C) 2007-2019 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do run { target c++2a } }
+
+#include <memory>
+#include <cstddef>
+#include <cstring>
+#include <vector>
+#include <testsuite_hooks.h>
+#include <testsuite_allocator.h>
+
+// p0674 - Extending make_shared support for Arrays
+// [util.smartptr.shared.create] - C++2a
+
+void
+test01()
+{
+  const double expected{};
+  const std::size_t count = 124;
+  __gnu_test::tracker_allocator<double> alloc;
+  std::shared_ptr<double[]> p
+    = std::allocate_shared<double[]>(alloc, count);
+  for (std::size_t i = 0; i < count; ++i)
+    {
+      VERIFY( p[i] == expected );
+    }
+}
+
+void
+test02()
+{
+  const int expected[2][2]{};
+  const std::size_t count = 84;
+  __gnu_test::tracker_allocator<int[2][2]> alloc{};
+  std::shared_ptr<int[][2][2]> p
+    = std::allocate_shared<int[][2][2]>(alloc, count);
+  for (std::size_t i = 0; i < count; ++i)
+    {
+      VERIFY( std::memcmp(&p[i], &expected, sizeof (expected)) == 0 );
+    }
+}
+
+void
+test03()
+{
+  const double expected[6]{ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 };
+  const std::size_t count = 1;
+  __gnu_test::tracker_allocator<double[6]> alloc{};
+  std::shared_ptr<double[6]> p
+    = std::allocate_shared<double[6]>(alloc, 1.0);
+  for (std::size_t i = 0; i < count; ++i)
+    {
+      VERIFY( std::memcmp(&p[i], &expected, sizeof (expected)) == 0 );
+    }
+}
+
+void
+test04()
+{
+  const double expected[46]{};
+  const std::size_t count = 1;
+  __gnu_test::tracker_allocator<double[46]> alloc{};
+  std::shared_ptr<double[46]> p
+    = std::allocate_shared<double[46]>(alloc);
+  for (std::size_t i = 0; i < count; ++i)
+    {
+      VERIFY( std::memcmp(&p[i], &expected, sizeof (expected)) == 0 );
+    }
+}
+
+void
+test05()
+{
+  const unsigned char expected[6][2][2]{};
+  const std::size_t count = 1;
+  __gnu_test::tracker_allocator<unsigned char[6][2][2]> alloc{};
+  std::shared_ptr<unsigned char[6][2][2]> p
+    = std::allocate_shared<unsigned char[6][2][2]>(alloc);
+  for (std::size_t i = 0; i < count; ++i)
+    {
+      VERIFY( std::memcmp(&p[i], &expected, sizeof (expected)) == 0 );
+    }
+}
+
+void
+test06()
+{
+  const short expected[2][2]{{1, 0}, {0, 1}};
+  const std::size_t count = 6;
+  __gnu_test::tracker_allocator<short[6][2][2]> alloc{};
+  std::shared_ptr<short[6][2][2]> p
+    = std::allocate_shared<short[6][2][2]>(alloc, {{1, 0}, {0, 1}});
+  for (std::size_t i = 0; i < count; ++i)
+    {
+      VERIFY( std::memcmp(&p[i][0], &expected, sizeof (expected)) == 0 );
+    }
+}
+
+void
+test07()
+{
+  const std::vector<int> expected{1, 2};
+  const std::size_t count = 4;
+  __gnu_test::tracker_allocator<std::vector<int>[4]> alloc{};
+  std::shared_ptr<std::vector<int>[4]> p 
+    = std::allocate_shared<std::vector<int>[4]>(alloc, {1, 2});
+  for (std::size_t i = 0; i < count; ++i)
+    {
+      VERIFY( p[i] == expected );
+    }
+}
+
+void
+test08()
+{
+  const std::vector<int> expected{1, 2};
+  const std::size_t count = 4;
+  __gnu_test::tracker_allocator<std::vector<int>> alloc{};
+  std::shared_ptr<std::vector<int>[]> p =
+    std::allocate_shared<std::vector<int>[]>(alloc, count, {1, 2});
+  for (std::size_t i = 0; i < count; ++i) 
+    {
+      VERIFY( p[i] == expected );
+    }
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  test05();
+  test06();
+  test07();
+  test08();
+}
diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/creation/array_support.make.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/creation/array_support.make.cc
new file mode 100644
index 00000000000..94ef72829ce
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/shared_ptr/creation/array_support.make.cc
@@ -0,0 +1,144 @@
+// Copyright (C) 2007-2019 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-do run { target c++2a } }
+
+#include <memory>
+#include <cstddef>
+#include <cstring>
+#include <vector>
+#include <testsuite_hooks.h>
+
+// p0674 - Extending make_shared support for Arrays
+// [util.smartptr.shared.create] - C++2a
+
+void
+test01()
+{
+  const double expected{};
+  const std::size_t count = 124;
+  std::shared_ptr<double[]> p
+    = std::make_shared<double[]>(count);
+  for (std::size_t i = 0; i < count; ++i)
+    {
+      VERIFY( p[i] == expected );
+    }
+}
+
+void
+test02()
+{
+  const int expected[2][2]{};
+  const std::size_t count = 84;
+  std::shared_ptr<int[][2][2]> p
+    = std::make_shared<int[][2][2]>(count);
+  for (std::size_t i = 0; i < count; ++i)
+    {
+      VERIFY( std::memcmp(&p[i], &expected, sizeof (expected)) == 0 );
+    }
+}
+
+void
+test03()
+{
+  const double expected[6]{ 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 };
+  const std::size_t count = 1;
+  std::shared_ptr<double[6]> p
+    = std::make_shared<double[6]>(1.0);
+  for (std::size_t i = 0; i < count; ++i)
+    {
+      VERIFY( std::memcmp(&p[i], &expected, sizeof (expected)) == 0 );
+    }
+}
+
+void
+test04()
+{
+  const double expected[46]{};
+  const std::size_t count = 1;
+  std::shared_ptr<double[46]> p
+    = std::make_shared<double[46]>();
+  for (std::size_t i = 0; i < count; ++i)
+    {
+      VERIFY( std::memcmp(&p[i], &expected, sizeof (expected)) == 0 );
+    }
+}
+
+void
+test05()
+{
+  const unsigned char expected[6][2][2]{};
+  const std::size_t count = 1;
+  std::shared_ptr<unsigned char[6][2][2]> p
+    = std::make_shared<unsigned char[6][2][2]>();
+  for (std::size_t i = 0; i < count; ++i)
+    {
+      VERIFY( std::memcmp(&p[i], &expected, sizeof (expected)) == 0 );
+    }
+}
+
+void
+test06()
+{
+  const short expected[2][2]{{1, 0}, {0, 1}};
+  const std::size_t count = 6;
+  std::shared_ptr<short[6][2][2]> p
+    = std::make_shared<short[6][2][2]>({{1, 0}, {0, 1}});
+  for (std::size_t i = 0; i < count; ++i)
+    {
+      VERIFY( std::memcmp(&p[i][0], &expected, sizeof (expected)) == 0 );
+    }
+}
+
+void
+test07()
+{
+  const std::vector<int> expected{1, 2};
+  const std::size_t count = 4;
+  std::shared_ptr<std::vector<int>[4]> p 
+    = std::make_shared<std::vector<int>[4]>({1, 2});
+  for (std::size_t i = 0; i < count; ++i)
+    {
+      VERIFY( p[i] == expected );
+    }
+}
+
+void
+test08()
+{
+  const std::vector<int> expected{1, 2};
+  const std::size_t count = 4;
+  std::shared_ptr<std::vector<int>[]> p =
+    std::make_shared<std::vector<int>[]>(count, {1, 2});
+  for (std::size_t i = 0; i < count; ++i) 
+    {
+      VERIFY( p[i] == expected );
+    }
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  test05();
+  test06();
+  test07();
+  test08();
+}

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [ PATCH ] [ C++ ] [ libstdc++ ] P0674r1 - Extend support for arrays in make/allocate_shared
  2020-01-02 22:16 [ PATCH ] [ C++ ] [ libstdc++ ] P0674r1 - Extend support for arrays in make/allocate_shared JeanHeyd Meneide
@ 2020-01-03 15:05 ` Jonathan Wakely
  2020-07-17 13:41 ` Jonathan Wakely
  1 sibling, 0 replies; 3+ messages in thread
From: Jonathan Wakely @ 2020-01-03 15:05 UTC (permalink / raw)
  To: JeanHeyd Meneide; +Cc: gcc-patches, libstdc++, Glen Fernandes

On 02/01/20 17:16 -0500, JeanHeyd Meneide wrote:
>     This implementation does not update the internal
>__allocate_shared and __make_shared functions (I don't know why there
>seems to be a duplicate front-end for those functions: it seems a
>little weird to use both? Maybe it's for legacy reasons, albeit if
>that is the case then I don't need to update the internal versions and
>people should move to the non-internal version, yes?).

Those functions return a __shared_ptr<T, Lp> not a shared_ptr<T>.

It's OK to not update them, if anybody is using them they can submit
patches to add array support themselves.

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [ PATCH ] [ C++ ] [ libstdc++ ] P0674r1 - Extend support for arrays in make/allocate_shared
  2020-01-02 22:16 [ PATCH ] [ C++ ] [ libstdc++ ] P0674r1 - Extend support for arrays in make/allocate_shared JeanHeyd Meneide
  2020-01-03 15:05 ` Jonathan Wakely
@ 2020-07-17 13:41 ` Jonathan Wakely
  1 sibling, 0 replies; 3+ messages in thread
From: Jonathan Wakely @ 2020-07-17 13:41 UTC (permalink / raw)
  To: JeanHeyd Meneide; +Cc: gcc-patches, libstdc++, Glen Fernandes

Sorry for the late review. I hope you're still willing to work on
this!


On 02/01/20 17:16 -0500, JeanHeyd Meneide wrote:
>Glen Fernandes is already working on a patch, so I decided to finish
>off my changes and throw them up on the Patch List so that Glen can
>access the tests parts that I had written (they are not incredibly
>extensive but do pass).
>
>    For a small implementation description: the code I use does the
>same in-place, fused-allocation technique used by other internal
>classes of shared_ptr, and also creates some helpers in a new
>bits/multi_dim.h header to simplify accessing multiple dimensions in a
>proper order (useful for destruction, but construction is easy
>enough). This might also come in handy down the road when mdspan
>(finally) gets standardized.
>
>     This implementation does not update the internal
>__allocate_shared and __make_shared functions (I don't know why there
>seems to be a duplicate front-end for those functions: it seems a
>little weird to use both? Maybe it's for legacy reasons, albeit if
>that is the case then I don't need to update the internal versions and
>people should move to the non-internal version, yes?).

As I replied at the time, those functions are for creating
__shared_ptr objects, e.g. to use __shared_ptr<T, _S_single> which
doesn't use atomics even in programs with multiple threads. They're
not internal functions, they're just non-standard extensions. But they
don't need to be updated to support arrays.


>2020-01-02  JeanHeyd "ThePhD" Meneide  <phdofthehouse@gmail.com>
>
>        * include/bits/multi_dim.h (new): New helpers for multi
>          dimensional access.
>        * include/bits/alloc_traits.h: New helpers for multi dimensional
>          array construction and destruction.
>        * include/bits/allocated_ptr.h: New guard type for sized
>          allocation.
>        * include/bits/memoryfwd.h: std::align forward declaration.
>        * include/bits/shared_ptr.h: Changes to support multi
>          dimensional array access with optimized size/extent storage.
>        * include/bits/stl_construct.h: Formatting.
>        * testsuite/20_util/shared_ptr/creation.array_support.allocate.cc:
>          New test.
>        * testsuite/20_util/shared_ptr/creation.array_support.make.cc:
>          New test.

>diff --git a/libstdc++-v3/include/bits/alloc_traits.h b/libstdc++-v3/include/bits/alloc_traits.h
>index 061d353e3f0..39dcb639143 100644
>--- a/libstdc++-v3/include/bits/alloc_traits.h
>+++ b/libstdc++-v3/include/bits/alloc_traits.h
>@@ -37,6 +37,9 @@
> # include <bits/ptr_traits.h>
> # include <ext/numeric_traits.h>
> #endif
>+#if __cplusplus > 201703L
>+#include <bits/multi_dim.h>
>+#endif // C++20 and above multi dimensional tools

Do we need a new header if it's only being included in one place?

Is it going to be needed elsewhere later?


> namespace std _GLIBCXX_VISIBILITY(default)
> {
>@@ -703,6 +706,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>   template<typename _Alloc>
>     using _RequireNotAllocator
>       = typename enable_if<!__is_allocator<_Alloc>::value, _Alloc>::type;
>+
>+  template <typename _Alloc>
>+    using __alloc_size
>+      = typename allocator_traits<_Alloc>::size_type;

Is there a reason to use the allocator's size_type instead of just
using size_t everywhere?

> #endif // C++11
>
>   /**
>@@ -733,6 +740,189 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       _Destroy(__first, __last);
>     }
>
>+
>+
>+#if __cplusplus > 201703L
>+  /// Helpers for getting multi-dimensional construction an destruction
>+  /// done properly. Obeys C++ Standard rules for array acccess: works
>+  /// in constexpr.

This shouldn't be a Doxygen /// comment, just //

Also, "acccess" is a typo.

>+  template<typename _ElemAlloc, typename _Ty, typename _Init>
>+    constexpr void
>+    __recursive_element_construct_1 (_ElemAlloc& __elem_alloc,

We don't put a space before the opening paren in libstdc++ code
(that's inconsistent with the GCC compiler code, I know).

>+				     _Ty* __ptr,
>+				     __alloc_size<_ElemAlloc>& __constructed,
>+				     __alloc_size<_ElemAlloc> __extent,
>+				     const _Init& __value)
>+    {
>+      using _SizeType = __alloc_size<_ElemAlloc>;
>+      if constexpr (rank_v<_Ty> == 0)
>+	{
>+	  for (_SizeType __idx = 0; __idx < __extent; ++__idx)
>+	    {
>+	      _Ty* __element_ptr = __ptr + __idx;
>+	      allocator_traits<_ElemAlloc>::construct(__elem_alloc,
>+				       __element_ptr,
>+				       __value[__idx]);
>+	      ++__constructed;
>+	    }
>+	}
>+      else
>+	{
>+	  using _Tl = std::remove_extent_t<_Ty>;
>+	  for (_SizeType __idx = 0; __idx < __extent; ++__idx)
>+	    {
>+	      _Tl* __elem_ptr = __ptr[__idx];
>+	      __recursive_element_construct_1(__elem_alloc,
>+			      __elem_ptr,
>+			      __constructed,
>+			      extent_v<_Ty>,
>+			      __value[__idx]);
>+	    }
>+	}
>+    }
>+
>+  template<typename _Ty, typename _ElemAlloc, typename _Init>
>+    constexpr void
>+    __recursive_element_construct (_ElemAlloc& __elem_alloc, _Ty* __ptr,
>+				   __alloc_size<_ElemAlloc>& __constructed,
>+				   __alloc_size<_ElemAlloc> __extent,
>+				   const _Init& __value)
>+    {
>+      using _SizeType = __alloc_size<_ElemAlloc>;
>+      if constexpr (rank_v<_Ty> == 0)
>+	{
>+	  for (_SizeType __idx = 0; __idx < __extent; ++__idx)
>+	    {
>+	      _Ty* __elem_ptr = __ptr + __idx;
>+	      allocator_traits<_ElemAlloc>::construct(__elem_alloc,
>+						      __elem_ptr,
>+						      __value);
>+	      ++__constructed;
>+	    }
>+	}
>+      else
>+	{
>+	  using _Tl = std::remove_extent_t<_Ty>;
>+	  for (_SizeType __idx = 0; __idx < __extent; ++__idx)
>+	    {
>+	      _Tl* __elem_ptr = __ptr[__idx];
>+	      __recursive_element_construct_1(__elem_alloc,
>+			    __elem_ptr,
>+			    __constructed,
>+			    extent_v<_Ty>,
>+			    __value);
>+	    }
>+	}
>+    }
>+
>+  template<typename _Ty, typename _Alloc, typename... _Args>
>+    constexpr void
>+    __unbounded_array_construct (_Alloc& __alloc, _Ty* __ptr,
>+		    __alloc_size<_Alloc> __extent,
>+		    __alloc_size<_Alloc>& __constructed,
>+		    _Args&&... __args)
>+    {
>+      static_assert((sizeof...(_Args) == 0) || (sizeof...(_Args) == 1),
>+	"must either be value-initialized or copy-initialized");
>+      if constexpr (sizeof...(_Args) == 0)
>+	{
>+	  for (__alloc_size<_Alloc> __idx = 0; __idx < __extent; ++__idx)
>+	    {
>+	      _Ty* __elem_ptr = __ptr + __idx;
>+	      allocator_traits<_Alloc>::construct(__alloc, __elem_ptr);
>+	      __constructed += sizeof(_Ty) / sizeof(remove_all_extents_t<_Ty>);
>+	    }
>+	}
>+      else
>+	{
>+	  using _ElemAlloc = __alloc_rebind<_Alloc, remove_all_extents_t<_Ty>>;
>+	  _ElemAlloc __elem_alloc{__alloc};
>+	  const _Ty& __value{forward<_Args>(__args)...};
>+	  __recursive_element_construct(__elem_alloc, __ptr, __constructed,
>+			  __extent,
>+			  __value);
>+	}
>+    }
>+
>+  template<typename _Ty, typename _Alloc, typename... _Args>
>+    constexpr void
>+    __array_construct (_Alloc& __alloc, _Ty& __arr,
>+		    __alloc_size<_Alloc>& __constructed,
>+		    _Args&&... __args)
>+    {
>+      __unbounded_array_construct(__alloc, __arr, extent_v<_Ty>, __constructed, forward<_Args>(__args)...);
>+    }
>+
>+  template<typename _ElemAlloc, typename _Ty, typename _Access>
>+    constexpr void __array_destroy_element_at (_ElemAlloc& __elem_alloc, _Ty* __ptr,
>+			   const _Access& __access,
>+			   __alloc_size<_ElemAlloc> __access_idx) noexcept
>+    {
>+      constexpr size_t _Rank = rank_v<_Ty>;
>+      if constexpr (_Rank == 0)
>+	{
>+	  _Ty* __elem_ptr = __ptr + __access[__access_idx];
>+	  allocator_traits<_ElemAlloc>::destroy(__elem_alloc, __elem_ptr);
>+	}
>+      else
>+	{
>+	  using _Tl = remove_extent_t<_Ty>;
>+	  _Tl* __elem_ptr = __ptr[__access[__access_idx]];
>+	  __array_destroy_element_at(__elem_alloc,
>+		        __elem_ptr,
>+		        __access,
>+		        __access_idx + 1);
>+	}
>+    }
>+
>+  template<typename _Ty, typename _Alloc>
>+    constexpr void __unbounded_array_destroy (_Alloc& __alloc, _Ty* __arr,

Newline after the return type.

>+		        __alloc_size<_Alloc> __extent,
>+		        __alloc_size<_Alloc> __count) noexcept
>+    {
>+      if (__count == 0)
>+	return;
>+
>+      using _SizeType = __alloc_size<_Alloc>;
>+      constexpr size_t __total_sub_extents = __total_extent_v<_Ty>;
>+      _SizeType __max_elements = (__total_sub_extents == 0
>+        ? 1
>+	: __total_sub_extents)
>+	* __extent;
>+      if (__max_elements == __count)
>+	{
>+	  // if possible, shortcut the shenanigans
>+	  for (_SizeType __idx = __extent; __idx-- > 0; --__idx)
>+	    {
>+	      _Ty* __elem_ptr = __arr + __idx;
>+	      allocator_traits<_Alloc>::destroy(__alloc, __elem_ptr);
>+	    }
>+
>+	  return;
>+	}
>+
>+      using _ElemAlloc = __alloc_rebind<_Alloc, remove_all_extents_t<_Ty>>;
>+      constexpr size_t _Rank = 1 + rank_v<_Ty>;
>+      const auto __extents = __extents_of<_Ty>(__extent);
>+      const auto __strides = __strides_of<_Rank>(__extents);
>+      _ElemAlloc __elem_alloc{__alloc};
>+      for (_SizeType __idx = __count; __idx-- > 0 ;)
>+	{
>+	  auto __multi_idx = __index_to_multi_index<_Rank>(__idx, __strides);
>+	  __array_destroy_element_at(__elem_alloc, __arr, __multi_idx, 0);
>+	}
>+    }
>+
>+  template<typename _Ty, typename _Alloc>
>+    constexpr void __array_destroy (_Alloc& __alloc, _Ty& __arr,

Newline after the return type.

>+				    __alloc_size<_Alloc> __count) noexcept
>+    {
>+      return __unbounded_array_destroy(__alloc, __arr,
>+				       extent_v<_Ty>, __count);
>+    }
>+#endif // C++20 array allocator construction helpers
>+
> _GLIBCXX_END_NAMESPACE_VERSION
> } // namespace std
> #endif // _ALLOC_TRAITS_H
>diff --git a/libstdc++-v3/include/bits/allocated_ptr.h b/libstdc++-v3/include/bits/allocated_ptr.h
>index 5058ab08466..84857185924 100644
>--- a/libstdc++-v3/include/bits/allocated_ptr.h
>+++ b/libstdc++-v3/include/bits/allocated_ptr.h
>@@ -97,6 +97,77 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       return { __a, std::allocator_traits<_Alloc>::allocate(__a, 1) };
>     }
>
>+#if __cplusplus > 201703L
>+
>+  template <typename _Alloc>
>+    using __alloc_size_t =
>+      typename ::std::allocator_traits<_Alloc>::size_type;

Why is this __alloc_size_t and not reusing the __alloc_size alias you
already defined earlier?

>+
>+  /// Non-standard RAII type for managing sized pointers
>+  /// obtained from allocators with additional information.
>+  template<typename _Alloc>
>+    struct __sized_allocated_ptr
>+    {
>+      using pointer = typename allocator_traits<_Alloc>::pointer;
>+      using value_type = typename allocator_traits<_Alloc>::value_type;
>+      using size_type = __alloc_size_t<_Alloc>;
>+
>+      /// Take ownership of __ptr
>+      __sized_allocated_ptr(_Alloc& __a, size_type __ptr_size,
>+		      pointer __ptr) noexcept
>+      : _M_alloc(std::__addressof(__a)), _M_ptr(__ptr), _M_size(__ptr_size)
>+      { }
>+
>+      /// Convert __ptr to allocator's pointer type and take ownership of it
>+      template<typename _Ptr,
>+	       typename _Req = _Require<is_same<_Ptr, value_type*>>>
>+      __sized_allocated_ptr(_Alloc& __a, size_type __ptr_size,
>+		      _Ptr __ptr) noexcept
>+      : _M_alloc(std::__addressof(__a)),
>+	_M_ptr(pointer_traits<pointer>::pointer_to(*__ptr)),
>+	_M_size(__ptr_size)
>+      { }
>+
>+      /// Transfer ownership of the owned pointer
>+      __sized_allocated_ptr(__sized_allocated_ptr&& __gd) noexcept
>+      : _M_alloc(__gd._M_alloc), _M_ptr(__gd._M_ptr), _M_size(__gd._M_size)
>+      { __gd._M_ptr = nullptr; }
>+
>+      /// Deallocate the owned pointer
>+      ~__sized_allocated_ptr()
>+      {
>+	if (_M_ptr != nullptr)
>+	  std::allocator_traits<_Alloc>::deallocate(*_M_alloc, _M_ptr, _M_size);
>+      }
>+
>+      /// Release ownership of the owned pointer
>+      __sized_allocated_ptr&
>+      operator=(std::nullptr_t) noexcept
>+      {
>+	_M_ptr = nullptr;
>+	return *this;
>+      }
>+
>+      /// Get the address that the owned pointer refers to.
>+      value_type* get() { return std::__to_address(_M_ptr); }
>+
>+    private:
>+      _Alloc* _M_alloc;
>+      pointer _M_ptr;
>+      size_type _M_size;
>+    };
>+
>+  /// Allocate space for maybe multiple objects using __a
>+  template<typename _Alloc>
>+    __sized_allocated_ptr<_Alloc>
>+    __allocate_guarded_size(_Alloc& __a,
>+      typename ::std::allocator_traits<_Alloc>::size_type __ptr_size)
>+    {
>+      return { __a, __ptr_size,
>+	      std::allocator_traits<_Alloc>::allocate(__a, __ptr_size) };
>+    }
>+#endif // C++20 and above unbounded array sized allocate support
>+
> _GLIBCXX_END_NAMESPACE_VERSION
> } // namespace std
>
>diff --git a/libstdc++-v3/include/bits/memoryfwd.h b/libstdc++-v3/include/bits/memoryfwd.h
>index af1a1c69c64..4d246fd6123 100644
>--- a/libstdc++-v3/include/bits/memoryfwd.h
>+++ b/libstdc++-v3/include/bits/memoryfwd.h
>@@ -72,6 +72,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>   /// Declare uses_allocator so it can be specialized in \<queue\> etc.
>   template<typename, typename>
>     struct uses_allocator;
>+
>+  /// Declare align so it can be used in shared_ptr_base
>+  void*
>+  align(size_t, size_t, void*&, size_t&) noexcept;
> #endif

I keep meaning to move std::align out of <memory> so it can be used
without including the whole of <memory>.

I think this is OK for now, because nothing internal to the library
that uses shared_ptr currently uses a shared_ptr of arrays, so
wouldn't try to use std::align without seeing its definition.


>   /// @} group memory
>diff --git a/libstdc++-v3/include/bits/multi_dim.h b/libstdc++-v3/include/bits/multi_dim.h
>new file mode 100644
>index 00000000000..a9189f9e0e9
>--- /dev/null
>+++ b/libstdc++-v3/include/bits/multi_dim.h
>@@ -0,0 +1,117 @@
>+// Multi dimensional tools and helpers -*- C++ -*-
>+
>+// Copyright (C) 2011-2019 Free Software Foundation, Inc.

This file seems to be entirely new code, is the 2011 copyright year a
mistake?

>+// This file is part of the GNU ISO C++ Library.  This library is free
>+// software; you can redistribute it and/or modify it under the
>+// terms of the GNU General Public License as published by the
>+// Free Software Foundation; either version 3, or (at your option)
>+// any later version.
>+
>+// This library is distributed in the hope that it will be useful,
>+// but WITHOUT ANY WARRANTY; without even the implied warranty of
>+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>+// GNU General Public License for more details.
>+
>+// Under Section 7 of GPL version 3, you are granted additional
>+// permissions described in the GCC Runtime Library Exception, version
>+// 3.1, as published by the Free Software Foundation.
>+
>+// You should have received a copy of the GNU General Public License and
>+// a copy of the GCC Runtime Library Exception along with this program;
>+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
>+// <http://www.gnu.org/licenses/>.
>+
>+/** @file bits/mutli_dim.h
>+ *  This is an internal header file, included by other library headers.
>+ *  Do not attempt to use it directly. @headername{memory}
>+ */
>+
>+#ifndef _MULTI_DIM_H
>+#define _MULTI_DIM_H 1
>+
>+#if __cplusplus > 201703L
>+
>+#include <array>
>+#include <type_traits>
>+
>+namespace std _GLIBCXX_VISIBILITY(default)
>+{
>+_GLIBCXX_BEGIN_NAMESPACE_VERSION
>+
>+  template<typename _Ty>
>+    struct __total_extent : extent<_Ty> {};
>+
>+  template<typename _Ty, size_t _Extent>
>+    struct __total_extent<_Ty[_Extent]>
>+    : ::std::integral_constant<size_t, extent<_Ty>::value == 0

There's no need for the ::std:: qualification here (if we were going
to do that we'd need to do it *everywhere*, and we don't do it
anywhere.


>+	? _Extent
>+	: __total_extent<_Ty>::value * _Extent> {};
>+
>+  template<typename _Ty>
>+    // internal: total_extent helper
>+   constexpr inline size_t __total_extent_v = __total_extent<_Ty>::value;
>+
>+  template <typename _Ty, typename _SizeType = size_t, size_t... _Idx, typename... _Args>
>+    constexpr std::array<_SizeType, sizeof...(_Args) + rank_v<_Ty>>

No need to say std::array rather than just array.

I wondered whether this really needs to return std::array, rather than
index_sequence. I haven't stared at it long enough to decide if that
could work.

>+    __extents_of (index_sequence<_Idx...>, _Args&&... __args) noexcept
>+    {
>+      std::array<_SizeType, sizeof...(_Args) + rank_v<_Ty>> __extents{
>+	std::forward<_Args>(__args)..., extent_v<_Ty, _Idx>...
>+      };
>+      return __extents;
>+    }
>+
>+  template <typename _Ty, typename _SizeType = size_t, typename... _Args>
>+    constexpr std::array<_SizeType, sizeof...(_Args) + rank_v<_Ty>>
>+    __extents_of (_Args&&... __args) noexcept
>+    {
>+      return __extents_of<_Ty>(make_index_sequence<rank_v<_Ty>>(),
>+			       std::forward<_Args>(__args)...);
>+    }
>+
>+  template <typename _Ty, typename _SizeType = size_t, typename _Extents>
>+    constexpr array<_SizeType, rank_v<_Ty>>
>+    __strides_of (const _Extents& __extents) noexcept
>+    {
>+      array<_SizeType, rank_v<_Ty>> __strides{};
>+      _SizeType __rank_stride = 1;
>+      for (size_t __rank_idx = rank_v<_Ty>; __rank_idx-- > 0;) {
>+	__strides[__rank_idx] = __rank_stride;
>+	__rank_stride *= __extents[__rank_idx];
>+      }
>+      return __strides;
>+    }
>+
>+  template <size_t _Rank, typename _SizeType = size_t, typename _Extents>
>+    constexpr array<_SizeType, _Rank>
>+    __strides_of (const _Extents& __extents) noexcept
>+    {
>+      array<_SizeType, _Rank> __strides{};
>+      _SizeType __rank_stride = 1;
>+      for (size_t __rank_idx = _Rank; __rank_idx-- > 0;) {
>+	__strides[__rank_idx] = __rank_stride;
>+	__rank_stride *= __extents[__rank_idx];
>+      }
>+      return __strides;
>+    }
>+
>+  template <size_t _Rank, typename _SizeType, typename _Strides>
>+    constexpr std::array<_SizeType, _Rank>
>+    __index_to_multi_index(_SizeType __idx, const _Strides& __strides) noexcept
>+    {
>+      std::array<_SizeType, _Rank> __multi_index{};
>+      for (size_t __rank_index = 0; __rank_index < _Rank; ++__rank_index) {
>+	_SizeType __stride = __strides[__rank_index];
>+	__multi_index[__rank_index] = __idx / __stride;
>+	__idx -= __multi_index[__rank_index] * __stride;
>+      }
>+      return __multi_index;
>+    }
>+
>+_GLIBCXX_END_NAMESPACE_VERSION
>+} // namespace
>+
>+#endif // C++20 or above
>+
>+#endif /* _MULTI_DIM_H */
>diff --git a/libstdc++-v3/include/bits/shared_ptr.h b/libstdc++-v3/include/bits/shared_ptr.h
>index c4df3582e20..2d1a8a2bba3 100644
>--- a/libstdc++-v3/include/bits/shared_ptr.h
>+++ b/libstdc++-v3/include/bits/shared_ptr.h
>@@ -99,6 +99,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> #endif
>     }
>
>+  namespace __detail
>+  {
>+    // friendship cooperation due to C++20 because constraints
>+    // do not handle template function friending well at all
>+    struct __shared_friend;
>+  }
>+
>+
>   /**
>    *  @brief  A smart pointer with reference-counted copy semantics.
>    *
>@@ -408,9 +416,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	: __shared_ptr<_Tp>(__tag, std::forward<_Args>(__args)...)
> 	{ }
>
>-      template<typename _Yp, typename _Alloc, typename... _Args>
>-	friend shared_ptr<_Yp>
>-	allocate_shared(const _Alloc& __a, _Args&&... __args);
>+#if __cplusplus > 201703L
>+      // This constructor is non-standard, it is used by allocate_shared.
>+      template<typename _Alloc, typename... _Args>
>+	shared_ptr(in_place_type_t<_Tp> __type_tag,
>+		   _Sp_alloc_shared_tag<_Alloc> __tag, _Args&&... __args)
>+	: __shared_ptr<_Tp>(__type_tag, __tag, std::forward<_Args>(__args)...)
>+	{ }
>+#endif // C++20 support for proper array make_shared
>+
>+      friend struct __detail::__shared_friend;
>
>       // This constructor is non-standard, it is used by weak_ptr::lock().
>       shared_ptr(const weak_ptr<_Tp>& __r, std::nothrow_t)
>@@ -419,6 +434,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       friend class weak_ptr<_Tp>;
>     };
>
>+    namespace __detail
>+    {
>+      struct __shared_friend
>+	{
>+	  template <typename _Tp, typename _Alloc, typename... _Args>
>+	    static shared_ptr<_Tp>
>+	    __allocate_shared (_Sp_alloc_shared_tag<_Alloc> __tag,
>+			       _Args&&... __args)
>+	    {
>+#if __cplusplus <= 201703L
>+	      return shared_ptr<_Tp> (__tag, ::std::forward<_Args>(__args)...);
>+#else
>+	      return shared_ptr<_Tp> (in_place_type<_Tp>, __tag,
>+				      ::std::forward<_Args>(__args)...);
>+#endif
>+	    }
>+	};
>+    }
>+
> #if __cpp_deduction_guides >= 201606
>   template<typename _Tp>
>     shared_ptr(weak_ptr<_Tp>) ->  shared_ptr<_Tp>;
>@@ -825,6 +859,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
>   /// @relates shared_ptr @{
>
>+#if __cplusplus <= 201703L

This needs to be __cplusplus <= 201703L || ! __cpp_concepts
bcause your new code uses concepts, and not all non-GCC compilers that
have a C++2a mode support concepts (e.g. Clang 9 defines __cplusplus
to 20170&l but doesn't support concepts).

>+
>   /**
>    *  @brief  Create an object that is owned by a shared_ptr.
>    *  @param  __a     An allocator.
>@@ -838,10 +874,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>    */
>   template<typename _Tp, typename _Alloc, typename... _Args>
>     inline shared_ptr<_Tp>
>-    allocate_shared(const _Alloc& __a, _Args&&... __args)
>+    allocate_shared (const _Alloc& __a, _Args&&... __args)

Nope :-)

>     {
>-      return shared_ptr<_Tp>(_Sp_alloc_shared_tag<_Alloc>{__a},
>-			     std::forward<_Args>(__args)...);
>+      return __detail::__shared_friend::__allocate_shared<_Tp> (
>+	_Sp_alloc_shared_tag<_Alloc>{__a}, std::forward<_Args>(__args)...);
>     }
>
>   /**
>@@ -860,6 +896,190 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 				       std::forward<_Args>(__args)...);
>     }
>
>+#else
>+
>+  /**
>+   *  @brief  Create an object that is owned by a shared_ptr.
>+   *  @param  __a     An allocator.
>+   *  @param  __args  Arguments for the @a _Tp object's constructor.
>+   *  @return A shared_ptr that owns the newly created object.
>+   *  @throw  An exception thrown from @a _Alloc::allocate or from the
>+   *          constructor of @a _Tp.
>+   *
>+   *  A copy of @a __a will be used to allocate memory for the shared_ptr
>+   *  and the new object.
>+   */
>+  template<typename _Tp, typename _Alloc, typename... _Args>
>+    requires (!is_array_v<_Tp>)
>+    inline shared_ptr<_Tp>
>+    allocate_shared (const _Alloc& __a, _Args&&... __args)
>+    {
>+      return __detail::__shared_friend::__allocate_shared<_Tp> (
>+	_Sp_alloc_shared_tag<_Alloc>{__a},
>+	std::forward<_Args>(__args)...);
>+    }
>+
>+  /**
>+   *  @brief  Create a bounded array object that is owned by a shared_ptr,
>+   *          with all elements given an initial value of __value.
>+   *  @param  __value  Initial value to construct all elements with.
>+   *  @return A shared_ptr that owns the newly created bounded array object.
>+   *  @throw  std::bad_alloc, or an exception thrown from the
>+   *          copy constructor of @a std::remove_extent_t<_Tp>.
>+   */
>+  template<typename _Tp, typename _Alloc>
>+    requires (is_bounded_array_v<_Tp>)
>+    inline shared_ptr<_Tp>
>+    allocate_shared (const _Alloc& __a, const remove_extent_t<_Tp>& __value)
>+    {
>+      return __detail::__shared_friend::__allocate_shared<_Tp> (
>+	_Sp_alloc_shared_tag<_Alloc>{__a},
>+	__value);
>+    }
>+
>+  /**
>+   *  @brief  Create a bounded array object that is owned by a shared_ptr,
>+   *          with the elements value initialized.
>+   *  @return A shared_ptr that owns the newly created bounded array object.
>+   *  @throw  std::bad_alloc, or an exception thrown from the
>+   *          value initialization of @a std::remove_extent_t<_Tp>.
>+   */
>+  template<typename _Tp, typename _Alloc>
>+    requires (is_bounded_array_v<_Tp>)
>+    inline shared_ptr<_Tp>
>+    allocate_shared (const _Alloc& __a)
>+    {
>+      return __detail::__shared_friend::__allocate_shared<_Tp> (
>+	_Sp_alloc_shared_tag<_Alloc>{__a});
>+    }
>+
>+  /**
>+   *  @brief  Create an array object that is owned by a shared_ptr,
>+   *          with copy constructed elements.
>+   *  @param  __value  Initial value to construct all elements with.
>+   *  @param  __num_elements  The number of elements to create the array with.
>+   *  @return A shared_ptr that owns the newly created bounded array object.
>+   *  @throw  std::bad_alloc, or an exception thrown from the
>+   *          copy constructor of @a std::remove_extent_t<_Tp>.
>+   */
>+  template<typename _Tp, typename _Alloc>
>+    requires (is_unbounded_array_v<_Tp>)
>+    inline shared_ptr<_Tp>
>+    allocate_shared (const _Alloc& __a,
>+		    __alloc_size_t<_Alloc> __num_elements,
>+		    const remove_extent_t<_Tp>& __value)
>+    {
>+      return __detail::__shared_friend::__allocate_shared<_Tp> (
>+	_Sp_alloc_shared_tag<_Alloc>{__a}, __num_elements, __value);
>+    }
>+
>+  /**
>+   *  @brief  Create an array object that is owned by a shared_ptr,
>+   *          with value-initialized elements.
>+   *  @param  __num_elements  The number of elements to create the array with.
>+   *  @return A shared_ptr that owns the newly created array object of size __num_elements.
>+   *  @throw  std::bad_alloc, or an exception thrown from the
>+   *          value initialization of @a std::remove_extent_t<_Tp>.
>+   */
>+  template<typename _Tp, typename _Alloc>
>+    requires (is_unbounded_array_v<_Tp>)
>+    inline shared_ptr<_Tp>
>+    allocate_shared (const _Alloc& __a,
>+		    __alloc_size_t<_Alloc> __num_elements)
>+    {
>+      return __detail::__shared_friend::__allocate_shared<_Tp> (
>+	_Sp_alloc_shared_tag<_Alloc>{__a}, __num_elements);
>+    }
>+
>+  /**
>+   *  @brief  Create an object that is owned by a shared_ptr.
>+   *  @param  __args  Arguments for the @a _Tp object's constructor.
>+   *  @return A shared_ptr that owns the newly created object.
>+   *  @throw  std::bad_alloc, or an exception thrown from the
>+   *          constructor of @a _Tp.
>+   */
>+  template<typename _Tp, typename... _Args>
>+    requires (!is_array_v<_Tp>)
>+    inline shared_ptr<_Tp>
>+    make_shared(_Args&&... __args)
>+    {
>+      using _Tp_nc = remove_cv_t<_Tp>;
>+      return ::std::allocate_shared<_Tp>(std::allocator<_Tp_nc>(),
>+					 std::forward<_Args>(__args)...);
>+    }
>+
>+  /**
>+   *  @brief  Create a bounded array object that is owned by a shared_ptr,
>+   *          with all elements given an initial value of __value.
>+   *  @param  __value  Initial value to construct all elements with.
>+   *  @return A shared_ptr that owns the newly created bounded array object.
>+   *  @throw  std::bad_alloc, or an exception thrown from the
>+   *          copy constructor of @a std::remove_extent_t<_Tp>.
>+   */
>+  template<typename _Tp>
>+    requires (is_bounded_array_v<_Tp>)
>+    inline shared_ptr<_Tp>
>+    make_shared(const remove_extent_t<_Tp>& __value)
>+    {
>+      using _Tp_nc = remove_cv_t<_Tp>;
>+      return ::std::allocate_shared<_Tp>(std::allocator<_Tp_nc>(), __value);
>+    }
>+
>+  /**
>+   *  @brief  Create a bounded array object that is owned by a shared_ptr,
>+   *          with the elements value initialized.
>+   *  @return A shared_ptr that owns the newly created bounded array object.
>+   *  @throw  std::bad_alloc, or an exception thrown from the
>+   *          value initialization of @a std::remove_extent_t<_Tp>.
>+   */
>+  template<typename _Tp>
>+    requires (is_bounded_array_v<_Tp>)
>+    inline shared_ptr<_Tp>
>+    make_shared()
>+    {
>+      using _Tp_nc = remove_cv_t<_Tp>;
>+      return ::std::allocate_shared<_Tp>(std::allocator<_Tp_nc>());
>+    }
>+
>+  /**
>+   *  @brief  Create an array object that is owned by a shared_ptr,
>+   *          with copy constructed elements.
>+   *  @param  __value  Initial value to construct all elements with.
>+   *  @param  __num_elements  The number of elements to create the array with.
>+   *  @return A shared_ptr that owns the newly created bounded array object.
>+   *  @throw  std::bad_alloc, or an exception thrown from the
>+   *          copy constructor of @a std::remove_extent_t<_Tp>.
>+   */
>+  template<typename _Tp>
>+    requires (is_unbounded_array_v<_Tp>)
>+    inline shared_ptr<_Tp>
>+    make_shared(size_t __num_elements, const remove_extent_t<_Tp>& __value)
>+    {
>+      using _Tp_nc = remove_cv_t<remove_extent_t<_Tp>>;
>+      return ::std::allocate_shared<_Tp>(std::allocator<_Tp_nc>(),
>+					 __num_elements, __value);
>+    }
>+
>+  /**
>+   *  @brief  Create an array object that is owned by a shared_ptr,
>+   *          with value-initialized elements.
>+   *  @param  __num_elements  The number of elements to create the array with.
>+   *  @return A shared_ptr that owns the newly created array object of size __num_elements.
>+   *  @throw  std::bad_alloc, or an exception thrown from the
>+   *          value initialization of @a std::remove_extent_t<_Tp>.
>+   */
>+  template<typename _Tp>
>+    requires (is_unbounded_array_v<_Tp>)
>+    inline shared_ptr<_Tp>
>+    make_shared(size_t __num_elements)
>+    {
>+      using _Tp_nc = remove_cv_t<remove_extent_t<_Tp>>;
>+      return ::std::allocate_shared<_Tp>(std::allocator<_Tp_nc>(),
>+					 __num_elements);
>+    }
>+
>+#endif // C++17 and below | C++20 and above make/allocate_shared
>+
>   /// std::hash specialization for shared_ptr.
>   template<typename _Tp>
>     struct hash<shared_ptr<_Tp>>
>diff --git a/libstdc++-v3/include/bits/shared_ptr_base.h b/libstdc++-v3/include/bits/shared_ptr_base.h
>index c9017ede4cb..e0b2e5774ab 100644
>--- a/libstdc++-v3/include/bits/shared_ptr_base.h
>+++ b/libstdc++-v3/include/bits/shared_ptr_base.h
>@@ -54,6 +54,9 @@
> #include <bits/refwrap.h>
> #include <bits/stl_function.h>
> #include <ext/aligned_buffer.h>
>+#if __cplusplus > 201703L
>+#include <utility>

Why do we need <utility> here? For in_place_type_t?
Please add a comment saying so, something like:

#include <utility>  // for std::in_place_type_t

>+#endif // C++20 and above make/allocate_shared array support
>
> namespace std _GLIBCXX_VISIBILITY(default)
> {
>@@ -89,6 +92,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>   using __gnu_cxx::_S_mutex;
>   using __gnu_cxx::_S_atomic;
>
>+  class _Zero
>+  { };

_Empty seems like a more descriptive name than _Zero. We already have
ranges::__detail::_Empty which is also present in C++20 but we can
consolidate them another time.


>+
>   // Empty helper class except when the template argument is _S_mutex.
>   template<_Lock_policy _Lp>
>     class _Mutex_base
>@@ -363,6 +369,38 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     class __shared_count;
>
>
>+  template <typename _Tp>
>+    using __Sp_needs_alloc_size =
>+      integral_constant<bool, is_array<_Tp>::value
>+			      && (extent<_Tp>::value == 0)>;

This is std::__is_array_unknown_bounds<_Tp>::type.

>+
>+  template<typename _SizeType, size_t _Extent>
>+    class _Sp_alloc_size
>+    {
>+    public:
>+      _Sp_alloc_size(_SizeType __size) noexcept
>+      { __glibcxx_assert(__size == _Extent); }
>+
>+      static _SizeType
>+      _M_alloc_size () noexcept
>+      { return _Extent; }
>+    };
>+
>+  template<typename _SizeType>
>+    class _Sp_alloc_size<_SizeType, 0>
>+    {
>+    public:
>+      _Sp_alloc_size(_SizeType __size) noexcept
>+      : _M_size(__size)
>+      { }
>+
>+      _SizeType
>+      _M_alloc_size() const noexcept
>+      { return _M_size; }
>+
>+      _SizeType _M_size;
>+    };
>+
>   // Counted ptr with no deleter or allocator support
>   template<typename _Ptr, _Lock_policy _Lp>
>     class _Sp_counted_ptr final : public _Sp_counted_base<_Lp>
>@@ -416,6 +454,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
>       static _Tp&
>       _S_get(_Sp_ebo_helper& __eboh) { return static_cast<_Tp&>(__eboh); }
>+
>+      static const _Tp&
>+      _S_get(const _Sp_ebo_helper& __eboh)
>+      { return static_cast<const _Tp&>(__eboh); }
>     };
>
>   /// Specialization not using EBO.
>@@ -429,6 +471,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       _S_get(_Sp_ebo_helper& __eboh)
>       { return __eboh._M_tp; }
>
>+      static const _Tp&
>+      _S_get(const _Sp_ebo_helper& __eboh)
>+      { return __eboh._M_tp; }
>+
>     private:
>       _Tp _M_tp;
>     };
>@@ -437,18 +483,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>   template<typename _Ptr, typename _Deleter, typename _Alloc, _Lock_policy _Lp>
>     class _Sp_counted_deleter final : public _Sp_counted_base<_Lp>
>     {
>-      class _Impl : _Sp_ebo_helper<0, _Deleter>, _Sp_ebo_helper<1, _Alloc>
>+      using _Del_base = _Sp_ebo_helper<0, _Deleter>;
>+      using _Alloc_base = _Sp_ebo_helper<1, _Alloc>;
>+
>+      class _Impl : _Del_base, _Alloc_base
>       {
>-	typedef _Sp_ebo_helper<0, _Deleter>	_Del_base;
>-	typedef _Sp_ebo_helper<1, _Alloc>	_Alloc_base;
>-
>       public:
> 	_Impl(_Ptr __p, _Deleter __d, const _Alloc& __a) noexcept
> 	: _M_ptr(__p), _Del_base(std::move(__d)), _Alloc_base(__a)
> 	{ }
>
>-	_Deleter& _M_del() noexcept { return _Del_base::_S_get(*this); }
>-	_Alloc& _M_alloc() noexcept { return _Alloc_base::_S_get(*this); }
>+	_Deleter& _M_del() noexcept
>+	{ return _Del_base::_S_get(*this); }
>+
>+	_Alloc& _M_alloc() noexcept
>+	{ return _Alloc_base::_S_get(*this); }
>
> 	_Ptr _M_ptr;
>       };
>@@ -468,7 +517,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
>       virtual void
>       _M_dispose() noexcept
>-      { _M_impl._M_del()(_M_impl._M_ptr); }
>+      {
>+	_M_impl._M_del()(_M_impl._M_ptr);
>+      }
>
>       virtual void
>       _M_destroy() noexcept
>@@ -503,6 +554,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>   private:
>     template<typename _Tp, typename _Alloc, _Lock_policy _Lp>
>       friend class _Sp_counted_ptr_inplace;
>+    template<typename _Tp, typename _Alloc, size_t _Extent, _Lock_policy _Lp>
>+      friend class _Sp_counted_sized_ptr_inplace;
>
>     static const type_info&
>     _S_ti() noexcept _GLIBCXX_VISIBILITY(default)
>@@ -523,14 +576,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>   template<typename _Tp, typename _Alloc, _Lock_policy _Lp>
>     class _Sp_counted_ptr_inplace final : public _Sp_counted_base<_Lp>
>     {
>-      class _Impl : _Sp_ebo_helper<0, _Alloc>
>-      {
>-	typedef _Sp_ebo_helper<0, _Alloc>	_A_base;
>+      using _Alloc_base = _Sp_ebo_helper<0, _Alloc>;
>
>+      class _Impl : _Alloc_base
>+      {
>       public:
>-	explicit _Impl(_Alloc __a) noexcept : _A_base(__a) { }
>+	explicit _Impl(_Alloc __a) noexcept
>+	: _Alloc_base(__a)
>+	{ }
>
>-	_Alloc& _M_alloc() noexcept { return _A_base::_S_get(*this); }
>+	_Alloc& _M_alloc() noexcept { return _Alloc_base::_S_get(*this); }
>
> 	__gnu_cxx::__aligned_buffer<_Tp> _M_storage;
>       };
>@@ -596,6 +651,179 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       _Impl _M_impl;
>     };
>
>+#if __cplusplus > 201703L
>+  template<typename _Ty, typename _Alloc, size_t _Extent, _Lock_policy _Lp>
>+    class _Sp_counted_sized_ptr_inplace final : public _Sp_counted_base<_Lp>
>+    {
>+      // it is the user's job to track
>+      // allocation size and deletion size
>+      // when they create something manually with an allocator
>+      // and/or a deleter and pass it directly
>+      // to std::shared_ptr's constructor,
>+      // that's why this machinery is present nowhere else
>+      // but here. Originally, we attempted to modify
>+      // _Sp_counted_ptr_inplace,
>+      // but function changes that relied on data members
>+      // -- even ones that can be optimized out -- can
>+      // affect ABI and thus blow up code.
>+      // Therefore, we needed a new derived
>+      // implementation.
>+      using _Tp = conditional_t<_Extent == 0
>+	, _Ty
>+	, remove_extent_t<_Ty>>;

Please format this as:

       using _Tp
         = conditional_t<_Extent == 0, _Ty, remove_extent_t<_Ty>>;

>+      using _SizeType = __alloc_size_t<_Alloc>;
>+      using _AllocSize = _Sp_alloc_size<_SizeType, _Extent>;
>+      using _Alloc_base = _Sp_ebo_helper<0, _Alloc>;
>+      using _Alloc_size_base = _Sp_ebo_helper<1, _AllocSize>;
>+      using _StorageType = conditional_t<_Extent == 0
>+	, _Zero
>+	, __gnu_cxx::__aligned_buffer<_Ty>>;

Similarly here.

>+      alignas(_Ty) class _Impl : _Alloc_base, _Alloc_size_base
>+      {
>+      public:
>+	explicit _Impl(_Alloc __a, _SizeType __size) noexcept
>+	: _Alloc_base(__a), _Alloc_size_base(__size)
>+	{ }
>+
>+	_Alloc& _M_alloc() noexcept { return _Alloc_base::_S_get(*this); }
>+	
>+	_SizeType
>+	_M_alloc_size() const noexcept
>+	{ return _Alloc_size_base::_S_get(*this)._M_alloc_size(); }
>+	
>+	_SizeType
>+	_M_Impl_size() const noexcept
>+	{ return this->_S_Impl_size(this->_M_alloc_size()); }
>+
>+	static _SizeType
>+	_S_Impl_size (_SizeType __num_elements) noexcept
>+	{
>+	  if constexpr (_Extent == 0)
>+	    {
>+	      _SizeType __division_step = sizeof(_Ty) - 1;
>+	      _SizeType __offset
>+		= static_cast<_SizeType>(sizeof(_Sp_counted_sized_ptr_inplace));
>+	      return ((__offset + __division_step) / sizeof(_Ty))
>+		+ __num_elements;
>+	    }
>+	  else
>+	    {
>+	      return 1;
>+	    }
>+	}
>+
>+	[[no_unique_address]] _StorageType _M_storage;
>+      };
>+
>+    public:
>+      using __allocator_type
>+	= conditional_t<_Extent == 0
>+	  , _Alloc
>+	  , __alloc_rebind<_Alloc, _Sp_counted_sized_ptr_inplace>>;

Reformat this too please. Break after the commas, not before. Maybe
like this:

       using __allocator_type
	= conditional_t<_Extent == 0, _Alloc,
             __alloc_rebind<_Alloc, _Sp_counted_sized_ptr_inplace>>;


>+      // Alloc parameter is not a reference so doesn't alias anything in __args
>+      // On failure (exception), destroy + rethrow

Since we have two complete sentences here, please put periods at the
end of them.

>+      template<typename... _Args>
>+	_Sp_counted_sized_ptr_inplace(_Alloc __a, _SizeType __num_elements,
>+				      _Args&&... __args)
>+	: _M_impl(__a, __num_elements)
>+	{
>+	  // constructors might throw...
>+	  // FIXME: use destructor to unwind construct,
>+	  // rather than __catch
>+	  // (much faster codegen on many compilers)
>+	  _SizeType __num{};
>+	  __try
>+	    {
>+	      __unbounded_array_construct (__a, this->_M_ptr(),
>+					   this->_M_impl._M_alloc_size(),
>+					   __num,
>+					   std::forward<_Args>(__args)...);
>+	      __glibcxx_assert(__num == this->_M_impl._M_alloc_size());
>+	    }
>+	  __catch(...)
>+	    {
>+	      __unbounded_array_destroy (__a, this->_M_ptr(),
>+					 this->_M_impl._M_alloc_size(), __num);
>+	      __throw_exception_again;
>+	    }
>+	}
>+
>+      ~_Sp_counted_sized_ptr_inplace() noexcept { }
>+
>+      virtual void
>+      _M_dispose() noexcept
>+      {
>+	__unbounded_array_destroy (this->_M_impl._M_alloc(), this->_M_ptr(),
>+				   this->_M_impl._M_alloc_size(),
>+				   this->_M_impl._M_alloc_size());
>+      }
>+
>+      // Override because the allocator needs to know the dynamic type
>+      virtual void
>+      _M_destroy() noexcept
>+      {
>+	using _Pointer = typename allocator_traits<__allocator_type>::pointer;
>+	__allocator_type __al(this->_M_impl._M_alloc());
>+	__sized_allocated_ptr<__allocator_type> __guard_ptr{ __al,
>+	  this->_M_impl._M_Impl_size(), reinterpret_cast<_Pointer>(this) };
>+	this->~_Sp_counted_sized_ptr_inplace();
>+      }
>+
>+    private:
>+      friend class __shared_count<_Lp>; // To be able to call _M_ptr().
>+
>+      // No longer used, but code compiled against old libstdc++ headers
>+      // might still call it from __shared_ptr ctor to get the pointer out.

But code compiled against old libstdc++ headers can't use make_shared
with arrays, so can't create one of these types, right? So it can't
get called.

>+      virtual void*
>+      _M_get_deleter(const std::type_info& __ti) noexcept override
>+      {
>+	auto __ptr = const_cast<typename remove_cv<_Tp>::type*>(_M_ptr());
>+	// Check for the fake type_info first, so we don't try to access it
>+	// as a real type_info object. Otherwise, check if it's the real
>+	// type_info for this class. With RTTI enabled we can check directly,
>+	// or call a library function to do it.
>+	if (&__ti == &_Sp_make_shared_tag::_S_ti()
>+	    ||
>+#if __cpp_rtti
>+	    __ti == typeid(_Sp_make_shared_tag)
>+#else
>+	    _Sp_make_shared_tag::_S_eq(__ti)
>+#endif
>+	   )
>+	  return __ptr;
>+	return nullptr;
>+      }
>+
>+      _Tp*
>+      _M_ptr() noexcept
>+      {
>+	if constexpr (_Extent == 0)
>+	  {
>+	    void* __ptr = static_cast<void*>(this + 1);
>+	    size_t __space = this->_M_impl._M_alloc_size()
>+	      * sizeof(_Ty);
>+	    void* __adjusted
>+	      = std::align(alignof(_Ty), __space, __ptr, __space);
>+	    // if this triggers then alignof() and friends failed us
>+	    // at a language level
>+	    __glibcxx_assert(__adjusted != nullptr);
>+	    return reinterpret_cast<_Tp*>(__adjusted);
>+	  }
>+	else
>+	  return this->_M_impl._M_storage._M_ptr()[0];
>+      }
>+
>+      static _SizeType
>+      _S_Impl_size (_SizeType __num_elements) noexcept
>+      { return _Impl::_S_Impl_size(__num_elements); }
>+
>+      _Impl _M_impl;
>+    };
>+
>+#endif // C++20 and above make/allocate_shared array support
>+
>   // The default deleter for shared_ptr<T[]> and shared_ptr<T[N]>.
>   struct __sp_array_delete
>   {
>@@ -672,16 +900,18 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> 	__shared_count(_Tp*& __p, _Sp_alloc_shared_tag<_Alloc> __a,
> 		       _Args&&... __args)
> 	{
>-	  typedef _Sp_counted_ptr_inplace<_Tp, _Alloc, _Lp> _Sp_cp_type;
>-	  typename _Sp_cp_type::__allocator_type __a2(__a._M_a);
>-	  auto __guard = std::__allocate_guarded(__a2);
>-	  _Sp_cp_type* __mem = __guard.get();
>-	  auto __pi = ::new (__mem)
>-	    _Sp_cp_type(__a._M_a, std::forward<_Args>(__args)...);
>-	  __guard = nullptr;
>-	  _M_pi = __pi;
>-	  __p = __pi->_M_ptr();
>+	  this->__single_allocate(__p, __a, std::forward<_Args>(__args)...);
>+	}
>+
>+#if __cplusplus > 201703L
>+      template<typename _Real, typename _Tp, typename _Alloc, typename... _Args>
>+	__shared_count(in_place_type_t<_Real>, _Tp*& __p,
>+		       _Sp_alloc_shared_tag<_Alloc> __a, _Args&&... __args)
>+	{
>+	  this->__sized_allocate<_Real>(__p, std::move(__a),
>+					std::forward<_Args>(__args)...);
> 	}
>+#endif // C++20 and above make/allocate_shared array support
>
> #if _GLIBCXX_USE_DEPRECATED
> #pragma GCC diagnostic push
>@@ -788,6 +1018,77 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     private:
>       friend class __weak_count<_Lp>;
>
>+      template <typename _Tp, typename _Alloc, typename... _Args>
>+	void __single_allocate (_Tp*& __p, _Sp_alloc_shared_tag<_Alloc> __a,

Newline after the return type please.

>+			       _Args&&... __args)
>+	{
>+	  typedef _Sp_counted_ptr_inplace<_Tp, _Alloc, _Lp> _Sp_cp_type;
>+	  typename _Sp_cp_type::__allocator_type __a2(__a._M_a);
>+	  auto __guard = std::__allocate_guarded(__a2);
>+	  _Sp_cp_type* __mem = __guard.get();
>+	  auto __pi = ::new (__mem)
>+	    _Sp_cp_type(__a._M_a, std::forward<_Args>(__args)...);
>+	  __guard = nullptr;
>+	  _M_pi = __pi;
>+	  __p = __pi->_M_ptr();
>+	}
>+
>+#if __cplusplus > 201703L
>+      template<typename _Real, typename _Tp,
>+	typename _Alloc, typename... _Args>
>+	requires (is_unbounded_array_v<_Real>)
>+	void __sized_allocate (_Tp*& __p,
>+			       _Sp_alloc_shared_tag<_Alloc> __a,
>+			       __alloc_size_t<_Alloc> __num_elements,
>+			       _Args&&... __args)
>+	{
>+	  using _Sp_cp_type =
>+	    _Sp_counted_sized_ptr_inplace<_Tp, _Alloc, 0, _Lp>;
>+	  typename _Sp_cp_type::__allocator_type __a2(__a._M_a);
>+	  __alloc_size<_Alloc> __impl_size
>+	    = _Sp_cp_type::_S_Impl_size(__num_elements);
>+	  auto __guard = std::__allocate_guarded_size(__a2, __impl_size);
>+	  _Sp_cp_type* __mem = reinterpret_cast<_Sp_cp_type*>(__guard.get());
>+	  auto __pi =
>+	    ::new (__mem) _Sp_cp_type(__a._M_a, __num_elements,
>+				      std::forward<_Args>(__args)...);
>+	  __guard = nullptr;
>+	  _M_pi = __pi;
>+	  __p = __pi->_M_ptr();
>+	}
>+
>+      template<typename _Real, typename _Tp,
>+	typename _Alloc, typename... _Args>
>+	requires (is_bounded_array_v<_Real>)
>+	void __sized_allocate (_Tp*& __p,
>+			       _Sp_alloc_shared_tag<_Alloc> __a,
>+			       _Args&&... __args)
>+	{
>+	  using _Sp_cp_type =
>+	    _Sp_counted_sized_ptr_inplace<remove_cv_t<_Real>
>+	      , _Alloc, extent_v<remove_cv_t<_Real>>, _Lp>;
>+	  typename _Sp_cp_type::__allocator_type __a2(__a._M_a);
>+	  auto __guard = std::__allocate_guarded(__a2);
>+	  _Sp_cp_type* __mem = __guard.get();
>+	  auto __pi =
>+	    ::new (__mem) _Sp_cp_type(__a._M_a, extent_v<remove_cv_t<_Real>>,
>+				      std::forward<_Args>(__args)...);
>+	  __guard = nullptr;
>+	  _M_pi = __pi;
>+	  __p = __pi->_M_ptr();
>+	}
>+
>+      template<typename _Real, typename _Tp,
>+	typename _Alloc, typename... _Args>
>+	requires (!is_array_v<_Real>)
>+	void __sized_allocate (_Tp*& __p,
>+			       _Sp_alloc_shared_tag<_Alloc> __a,
>+			       _Args&&... __args)
>+	{
>+	  this->__single_allocate (__p, __a, std::forward<_Args>(__args)...);
>+	}
>+#endif // C++20 and above make/allocate_shared array support
>+
>       _Sp_counted_base<_Lp>*  _M_pi;
>     };
>
>@@ -1362,11 +1663,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>       // @}
>
>     protected:
>-      // This constructor is non-standard, it is used by allocate_shared.
>+      // This constructor is non-standard, it is used by __allocate_shared.
>       template<typename _Alloc, typename... _Args>
> 	__shared_ptr(_Sp_alloc_shared_tag<_Alloc> __tag, _Args&&... __args)
> 	: _M_ptr(), _M_refcount(_M_ptr, __tag, std::forward<_Args>(__args)...)
> 	{ _M_enable_shared_from_this_with(_M_ptr); }
>+	
>+#if __cplusplus > 201703L
>+      // This constructor is non-standard, it is used by allocate_shared.
>+      template<typename _Alloc, typename... _Args>
>+	__shared_ptr(in_place_type_t<_Tp> __type_tag,
>+		     _Sp_alloc_shared_tag<_Alloc> __tag, _Args&&... __args)
>+	: _M_ptr(),
>+	_M_refcount(__type_tag, _M_ptr, __tag, std::forward<_Args>(__args)...)
>+	{ _M_enable_shared_from_this_with(_M_ptr); }
>+#endif // C++20 and above make/allocate_shared array support
>
>       template<typename _Tp1, _Lock_policy _Lp1, typename _Alloc,
> 	       typename... _Args>
>diff --git a/libstdc++-v3/include/bits/stl_construct.h b/libstdc++-v3/include/bits/stl_construct.h
>index 5c9a84d9497..914be803025 100644
>--- a/libstdc++-v3/include/bits/stl_construct.h
>+++ b/libstdc++-v3/include/bits/stl_construct.h
>@@ -60,6 +60,9 @@
> #include <bits/move.h>
> #include <bits/stl_iterator_base_types.h> // for iterator_traits
> #include <bits/stl_iterator_base_funcs.h> // for advance
>+#if __cplusplus > 201703L
>+#include <array>
>+#endif // C++20 array construction helpers

Does this header really need to be added here? Nothing in the file
uses it.

>
> /* This file provides the C++17 functions std::destroy_at, std::destroy, and
>  * std::destroy_n, and the C++20 function std::construct_at.
>@@ -163,8 +166,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     struct _Destroy_aux<true>
>     {
>       template<typename _ForwardIterator>
>-        static void
>-        __destroy(_ForwardIterator, _ForwardIterator) { }
>+	static void
>+	__destroy(_ForwardIterator, _ForwardIterator) { }
>     };
>
>   /**
>@@ -177,7 +180,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     _Destroy(_ForwardIterator __first, _ForwardIterator __last)
>     {
>       typedef typename iterator_traits<_ForwardIterator>::value_type
>-                       _Value_type;
>+		       _Value_type;
> #if __cplusplus >= 201103L
>       // A deleted destructor is trivial, this ensures we reject such types:
>       static_assert(is_destructible<_Value_type>::value,
>@@ -208,8 +211,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     struct _Destroy_n_aux<true>
>     {
>       template<typename _ForwardIterator, typename _Size>
>-        static _ForwardIterator
>-        __destroy_n(_ForwardIterator __first, _Size __count)
>+	static _ForwardIterator
>+	__destroy_n(_ForwardIterator __first, _Size __count)
> 	{
> 	  std::advance(__first, __count);
> 	  return __first;
>@@ -226,7 +229,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>     _Destroy_n(_ForwardIterator __first, _Size __count)
>     {
>       typedef typename iterator_traits<_ForwardIterator>::value_type
>-                       _Value_type;
>+		       _Value_type;
> #if __cplusplus >= 201103L
>       // A deleted destructor is trivial, this ensures we reject such types:
>       static_assert(is_destructible<_Value_type>::value,
>diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/creation/array_support.allocate.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/creation/array_support.allocate.cc
>new file mode 100644
>index 00000000000..23f0cc702f8
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/20_util/shared_ptr/creation/array_support.allocate.cc
>@@ -0,0 +1,153 @@
>+// Copyright (C) 2007-2019 Free Software Foundation, Inc.

Is the 2007 copyright year a mistake?


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2020-07-17 13:41 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-01-02 22:16 [ PATCH ] [ C++ ] [ libstdc++ ] P0674r1 - Extend support for arrays in make/allocate_shared JeanHeyd Meneide
2020-01-03 15:05 ` Jonathan Wakely
2020-07-17 13:41 ` 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).