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  <phdoftheho...@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?

Reply via email to