diff mbox series

[committed] libstdc++: Add C++20 std::make_shared enhancements (P0674R1)

Message ID 20220114102101.2975426-1-jwakely@redhat.com
State Committed
Commit 9a0b518a82db68c5cbd9ea8ccc47c2ff45182519
Headers show
Series [committed] libstdc++: Add C++20 std::make_shared enhancements (P0674R1) | expand

Commit Message

Jonathan Wakely Jan. 14, 2022, 10:21 a.m. UTC
Tested powerpc64-le-linux, pushed to trunk.
N.B. we had a patch from JeanHeyd two years ago adding the first part of
this (P0674R1), but this is much simpler, works without concepts, and
also adds make_shared_for_overwrite.



This adds the overloads of std::make_shared and std::allocate_shared for
creating arrays, added to C++20 by P0674R1.

It also adds std::make_shared_for_overwrite, added to C++20 by P1020R1
(and renamed by P1973R1). The std::make_unique_for_overwite overloads
are already supported.

The original std::make_shared overload is changed to construct a
shared_ptr directly instead of calling std::allocate_shared. This
removes a function call at runtime, and avoids having to do overload
resolution for std::allocate_shared, now that there are five overloads
of it.

Allocating a shared array is done by a new __shared_count constructor.
An array is allocated with space for additional elements at the end and
an instance of new _Sp_counted_array class template is constructed in
that unused capacity.

The non-array form of std::make_shared_for_overwrite uses the same
__shared_count constructor as the original std::make_shared overload,
but a new partial specialization of _Sp_counted_ptr_inplace is selected
when the allocator's value_type is the new _Sp_overwrite_tag type. That
new partial specialization default-initializes its contained object and
destroys it with a destructor call rather than using the allocator.

Despite being C++20 features, this implementation only uses concepts
conditionally, with workarounds when they are not supported. This allows
it to work with older non-GCC compilers (Clang 9 and icc 2021). At some
point we can simplify the code by removing the workarounds.

libstdc++-v3/ChangeLog:

	* include/bits/shared_ptr.h (__cpp_lib_shared_ptr_weak_type):
	Correct type of macro value.
	(shared_ptr): Add additional friend declarations.
	(make_shared, allocate_shared): Constrain existing overloads and
	remove static_assert.
	* include/bits/shared_ptr_base.h (__cpp_lib_smart_ptr_for_overwrite):
	New macro.
	(_Sp_counted_ptr_inplace<T, Alloc, Lp>): New partial
	specialization for use with make_shared_for_overwrite.
	(__cpp_lib_shared_ptr_arrays): Update value for C++20.
	(_Sp_counted_array_base): New class template.
	(_Sp_counted_array): New class template.
	(__shared_count(_Tp*&, const _Sp_counted_array_base&, _Init)):
	New constructor for allocating shared arrays.
	(__shared_ptr(const _Sp_counted_array_base&, _Init)): Likewise.
	* include/std/version (__cpp_lib_shared_ptr_weak_type): Correct
	type.
	(__cpp_lib_shared_ptr_arrays): Update value for C++20.
	(__cpp_lib_smart_ptr_for_overwrite): New macro.
	* testsuite/20_util/shared_ptr/creation/99006.cc: Adjust
	expected errors.
	* testsuite/20_util/shared_ptr/creation/array.cc: New test.
	* testsuite/20_util/shared_ptr/creation/overwrite.cc: New test.
	* testsuite/20_util/shared_ptr/creation/version.cc: New test.
	* testsuite/20_util/unique_ptr/creation/for_overwrite.cc: Check
	feature test macro. Test non-trivial default-initialization.
---
 libstdc++-v3/include/bits/shared_ptr.h        | 271 +++++++++++++++-
 libstdc++-v3/include/bits/shared_ptr_base.h   | 299 +++++++++++++++++-
 libstdc++-v3/include/std/version              |   6 +-
 .../20_util/shared_ptr/creation/99006.cc      |   7 +-
 .../20_util/shared_ptr/creation/array.cc      | 224 +++++++++++++
 .../20_util/shared_ptr/creation/overwrite.cc  | 143 +++++++++
 .../20_util/shared_ptr/creation/version.cc    |  18 ++
 .../unique_ptr/creation/for_overwrite.cc      |  25 +-
 8 files changed, 975 insertions(+), 18 deletions(-)
 create mode 100644 libstdc++-v3/testsuite/20_util/shared_ptr/creation/array.cc
 create mode 100644 libstdc++-v3/testsuite/20_util/shared_ptr/creation/overwrite.cc
 create mode 100644 libstdc++-v3/testsuite/20_util/shared_ptr/creation/version.cc
diff mbox series

Patch

diff --git a/libstdc++-v3/include/bits/shared_ptr.h b/libstdc++-v3/include/bits/shared_ptr.h
index 5fdfcb2c4e1..f44008ed2b1 100644
--- a/libstdc++-v3/include/bits/shared_ptr.h
+++ b/libstdc++-v3/include/bits/shared_ptr.h
@@ -100,6 +100,57 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #endif
     }
 
+  /// @cond undocumented
+
+  // Constraint for overloads taking non-array types.
+#if __cpp_concepts && __cpp_lib_type_trait_variable_templates
+  template<typename _Tp>
+    requires (!is_array_v<_Tp>)
+    using _NonArray = _Tp;
+#else
+  template<typename _Tp>
+    using _NonArray = __enable_if_t<!is_array<_Tp>::value, _Tp>;
+#endif
+
+#if __cpp_lib_shared_ptr_arrays >= 201707L
+  // Constraint for overloads taking array types with unknown bound, U[].
+#if __cpp_concepts
+  template<typename _Tp>
+    requires is_array_v<_Tp> && (extent_v<_Tp> == 0)
+    using _UnboundedArray = _Tp;
+#else
+  template<typename _Tp>
+    using _UnboundedArray
+      = __enable_if_t<__is_array_unknown_bounds<_Tp>::value, _Tp>;
+#endif
+
+  // Constraint for overloads taking array types with known bound, U[N].
+#if __cpp_concepts
+  template<typename _Tp>
+    requires (extent_v<_Tp> != 0)
+    using _BoundedArray = _Tp;
+#else
+  template<typename _Tp>
+    using _BoundedArray
+      = __enable_if_t<__is_array_known_bounds<_Tp>::value, _Tp>;
+#endif
+
+#if __cpp_lib_smart_ptr_for_overwrite
+  // Constraint for overloads taking either non-array or bounded array, U[N].
+#if __cpp_concepts
+  template<typename _Tp>
+    requires (!is_array_v<_Tp>) || (extent_v<_Tp> != 0)
+    using _NotUnboundedArray = _Tp;
+#else
+  template<typename _Tp>
+    using _NotUnboundedArray
+      = __enable_if_t<!__is_array_unknown_bounds<_Tp>::value, _Tp>;
+#endif
+#endif // smart_ptr_for_overwrite
+#endif // shared_ptr_arrays
+
+  /// @endcond
+
   /**
    *  @brief  A smart pointer with reference-counted copy semantics.
    *  @headerfile memory
@@ -139,7 +190,7 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       using element_type = typename __shared_ptr<_Tp>::element_type;
 
 #if __cplusplus >= 201703L
-# define __cpp_lib_shared_ptr_weak_type 201606
+# define __cpp_lib_shared_ptr_weak_type 201606L
       /// The corresponding weak_ptr type for this shared_ptr
       /// @since C++17
       using weak_type = weak_ptr<_Tp>;
@@ -414,8 +465,71 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	{ }
 
       template<typename _Yp, typename _Alloc, typename... _Args>
-	friend shared_ptr<_Yp>
-	allocate_shared(const _Alloc& __a, _Args&&... __args);
+	friend shared_ptr<_NonArray<_Yp>>
+	allocate_shared(const _Alloc&, _Args&&...);
+
+      template<typename _Yp, typename... _Args>
+	friend shared_ptr<_NonArray<_Yp>>
+	make_shared(_Args&&...);
+
+#if __cpp_lib_shared_ptr_arrays >= 201707L
+      // This constructor is non-standard, it is used by allocate_shared<T[]>.
+      template<typename _Alloc, typename _Init = const remove_extent_t<_Tp>*>
+	shared_ptr(const _Sp_counted_array_base<_Alloc>& __a,
+		   _Init __init = nullptr)
+	: __shared_ptr<_Tp>(__a, __init)
+	{ }
+
+      template<typename _Yp, typename _Alloc>
+	friend shared_ptr<_UnboundedArray<_Yp>>
+	allocate_shared(const _Alloc&, size_t);
+
+      template<typename _Yp>
+	friend shared_ptr<_UnboundedArray<_Yp>>
+	make_shared(size_t);
+
+      template<typename _Yp, typename _Alloc>
+	friend shared_ptr<_UnboundedArray<_Yp>>
+	allocate_shared(const _Alloc&, size_t, const remove_extent_t<_Yp>&);
+
+      template<typename _Yp>
+	friend shared_ptr<_UnboundedArray<_Yp>>
+	make_shared(size_t, const remove_extent_t<_Yp>&);
+
+      template<typename _Yp, typename _Alloc>
+	friend shared_ptr<_BoundedArray<_Yp>>
+	allocate_shared(const _Alloc&);
+
+      template<typename _Yp>
+	friend shared_ptr<_BoundedArray<_Yp>>
+	make_shared();
+
+      template<typename _Yp, typename _Alloc>
+	friend shared_ptr<_BoundedArray<_Yp>>
+	allocate_shared(const _Alloc&, const remove_extent_t<_Yp>&);
+
+      template<typename _Yp>
+	friend shared_ptr<_BoundedArray<_Yp>>
+	make_shared(const remove_extent_t<_Yp>&);
+
+#if __cpp_lib_smart_ptr_for_overwrite
+      template<typename _Yp, typename _Alloc>
+	friend shared_ptr<_NotUnboundedArray<_Yp>>
+	allocate_shared_for_overwrite(const _Alloc&);
+
+      template<typename _Yp>
+	friend shared_ptr<_NotUnboundedArray<_Yp>>
+	make_shared_for_overwrite();
+
+      template<typename _Yp, typename _Alloc>
+	friend shared_ptr<_UnboundedArray<_Yp>>
+	allocate_shared_for_overwrite(const _Alloc&, size_t);
+
+      template<typename _Yp>
+	friend shared_ptr<_UnboundedArray<_Yp>>
+	make_shared_for_overwrite(size_t);
+#endif
+#endif
 
       // This constructor is non-standard, it is used by weak_ptr::lock().
       shared_ptr(const weak_ptr<_Tp>& __r, std::nothrow_t) noexcept
@@ -872,11 +986,9 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
    *  and the new object.
    */
   template<typename _Tp, typename _Alloc, typename... _Args>
-    inline shared_ptr<_Tp>
+    inline shared_ptr<_NonArray<_Tp>>
     allocate_shared(const _Alloc& __a, _Args&&... __args)
     {
-      static_assert(!is_array<_Tp>::value, "make_shared<T[]> not supported");
-
       return shared_ptr<_Tp>(_Sp_alloc_shared_tag<_Alloc>{__a},
 			     std::forward<_Args>(__args)...);
     }
@@ -889,14 +1001,153 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
    *          constructor of @a _Tp.
    */
   template<typename _Tp, typename... _Args>
-    inline shared_ptr<_Tp>
+    inline shared_ptr<_NonArray<_Tp>>
     make_shared(_Args&&... __args)
     {
-      typedef typename std::remove_cv<_Tp>::type _Tp_nc;
-      return std::allocate_shared<_Tp>(std::allocator<_Tp_nc>(),
-				       std::forward<_Args>(__args)...);
+      using _Alloc = allocator<void>;
+      _Alloc __a;
+      return shared_ptr<_Tp>(_Sp_alloc_shared_tag<_Alloc>{__a},
+			     std::forward<_Args>(__args)...);
     }
 
+#if __cpp_lib_shared_ptr_arrays >= 201707L
+  /// @cond undocumented
+  template<typename _Tp, typename _Alloc = allocator<void>>
+    auto
+    __make_shared_arr_tag(size_t __n, const _Alloc& __a = _Alloc()) noexcept
+    {
+      using _Up = remove_all_extents_t<_Tp>;
+      using _UpAlloc = __alloc_rebind<_Alloc, _Up>;
+      size_t __s = sizeof(remove_extent_t<_Tp>) / sizeof(_Up);
+      if (__builtin_mul_overflow(__s, __n, &__n))
+	std::__throw_bad_array_new_length();
+      return _Sp_counted_array_base<_UpAlloc>{_UpAlloc(__a), __n};
+    }
+  /// @endcond
+
+  template<typename _Tp, typename _Alloc>
+    inline shared_ptr<_UnboundedArray<_Tp>>
+    allocate_shared(const _Alloc& __a, size_t __n)
+    {
+      return shared_ptr<_Tp>(std::__make_shared_arr_tag<_Tp>(__n, __a));
+    }
+
+  template<typename _Tp>
+    inline shared_ptr<_UnboundedArray<_Tp>>
+    make_shared(size_t __n)
+    {
+      return shared_ptr<_Tp>(std::__make_shared_arr_tag<_Tp>(__n));
+    }
+
+  template<typename _Tp, typename _Alloc>
+    inline shared_ptr<_UnboundedArray<_Tp>>
+    allocate_shared(const _Alloc& __a, size_t __n,
+		    const remove_extent_t<_Tp>& __u)
+    {
+      return shared_ptr<_Tp>(std::__make_shared_arr_tag<_Tp>(__n, __a),
+			     std::__addressof(__u));
+    }
+
+  template<typename _Tp>
+    inline shared_ptr<_UnboundedArray<_Tp>>
+    make_shared(size_t __n, const remove_extent_t<_Tp>& __u)
+    {
+      return shared_ptr<_Tp>(std::__make_shared_arr_tag<_Tp>(__n),
+			     std::__addressof(__u));
+    }
+
+  /// @cond undocumented
+  template<typename _Tp, typename _Alloc = allocator<void>>
+    auto
+    __make_shared_arrN_tag(const _Alloc& __a = _Alloc()) noexcept
+    {
+      using _Up = remove_all_extents_t<_Tp>;
+      using _UpAlloc = __alloc_rebind<_Alloc, _Up>;
+      size_t __n = sizeof(_Tp) / sizeof(_Up);
+      return _Sp_counted_array_base<_UpAlloc>{_UpAlloc(__a), __n};
+    }
+  /// @endcond
+
+  template<typename _Tp, typename _Alloc>
+    inline shared_ptr<_BoundedArray<_Tp>>
+    allocate_shared(const _Alloc& __a)
+    {
+      return shared_ptr<_Tp>(std::__make_shared_arrN_tag<_Tp>(__a));
+    }
+
+  template<typename _Tp>
+    inline shared_ptr<_BoundedArray<_Tp>>
+    make_shared()
+    {
+      return shared_ptr<_Tp>(std::__make_shared_arrN_tag<_Tp>());
+    }
+
+  template<typename _Tp, typename _Alloc>
+    inline shared_ptr<_BoundedArray<_Tp>>
+    allocate_shared(const _Alloc& __a, const remove_extent_t<_Tp>& __u)
+    {
+      return shared_ptr<_Tp>(std::__make_shared_arrN_tag<_Tp>(__a),
+			     std::__addressof(__u));
+    }
+
+  template<typename _Tp>
+    inline shared_ptr<_BoundedArray<_Tp>>
+    make_shared(const remove_extent_t<_Tp>& __u)
+    {
+      return shared_ptr<_Tp>(std::__make_shared_arrN_tag<_Tp>(),
+			     std::__addressof(__u));
+    }
+
+#if __cpp_lib_smart_ptr_for_overwrite
+  template<typename _Tp, typename _Alloc>
+    inline shared_ptr<_NotUnboundedArray<_Tp>>
+    allocate_shared_for_overwrite(const _Alloc& __a)
+    {
+      if constexpr (is_array_v<_Tp>)
+	return shared_ptr<_Tp>(std::__make_shared_arrN_tag<_Tp>(__a),
+			       _Sp_overwrite_tag{});
+      else
+	{
+	  // Rebind the allocator to _Sp_overwrite_tag, so that the
+	  // relevant _Sp_counted_ptr_inplace specialization is used.
+	  using _Alloc2 = __alloc_rebind<_Alloc, _Sp_overwrite_tag>;
+	  _Alloc2 __a2 = __a;
+	  return shared_ptr<_Tp>(_Sp_alloc_shared_tag<_Alloc2>{__a2});
+	}
+    }
+
+  template<typename _Tp>
+    inline shared_ptr<_NotUnboundedArray<_Tp>>
+    make_shared_for_overwrite()
+    {
+      if constexpr (is_array_v<_Tp>)
+	return shared_ptr<_Tp>(std::__make_shared_arrN_tag<_Tp>(),
+			       _Sp_overwrite_tag{});
+      else
+	{
+	  using _Alloc = allocator<_Sp_overwrite_tag>;
+	  return shared_ptr<_Tp>(_Sp_alloc_shared_tag<_Alloc>{{}});
+	}
+    }
+
+  template<typename _Tp, typename _Alloc>
+    inline shared_ptr<_UnboundedArray<_Tp>>
+    allocate_shared_for_overwrite(const _Alloc& __a, size_t __n)
+    {
+      return shared_ptr<_Tp>(std::__make_shared_arr_tag<_Tp>(__n, __a),
+			     _Sp_overwrite_tag{});
+    }
+
+  template<typename _Tp>
+    inline shared_ptr<_UnboundedArray<_Tp>>
+    make_shared_for_overwrite(size_t __n)
+    {
+      return shared_ptr<_Tp>(std::__make_shared_arr_tag<_Tp>(__n),
+			     _Sp_overwrite_tag{});
+    }
+#endif // smart_ptr_for_overwrite
+#endif // shared_ptr_arrays
+
   /// 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 e9f4743fedb..e16a9250592 100644
--- a/libstdc++-v3/include/bits/shared_ptr_base.h
+++ b/libstdc++-v3/include/bits/shared_ptr_base.h
@@ -60,8 +60,11 @@ 
 #include <ext/aligned_buffer.h>
 #include <ext/atomicity.h>
 #include <ext/concurrence.h>
-#if __cplusplus > 201703L
+#if __cplusplus >= 202002L
+# include <bit>          // __bit_floor
 # include <compare>
+# include <bits/align.h> // std::align
+# include <bits/stl_uninitialized.h>
 #endif
 
 namespace std _GLIBCXX_VISIBILITY(default)
@@ -447,6 +450,11 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
     inline void
     _Sp_counted_ptr<nullptr_t, _S_atomic>::_M_dispose() noexcept { }
 
+  // FIXME: once __has_cpp_attribute(__no_unique_address__)) is true for
+  // all supported compilers we can greatly simplify _Sp_ebo_helper.
+  // N.B. unconditionally applying the attribute could change layout for
+  // final types, which currently cannot use EBO so have a unique address.
+
   template<int _Nm, typename _Tp,
 	   bool __use_ebo = !__is_final(_Tp) && __is_empty(_Tp)>
     struct _Sp_ebo_helper;
@@ -640,6 +648,233 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       _Impl _M_impl;
     };
 
+#if __cplusplus >= 202002L
+# define __cpp_lib_smart_ptr_for_overwrite 202002L
+  struct _Sp_overwrite_tag { };
+
+  // Partial specialization used for make_shared_for_overwrite<non-array>().
+  // This partial specialization is used when the allocator's value type
+  // is the special _Sp_overwrite_tag type.
+#if __cpp_concepts
+  template<typename _Tp, typename _Alloc, _Lock_policy _Lp>
+    requires is_same_v<typename _Alloc::value_type, _Sp_overwrite_tag>
+    class _Sp_counted_ptr_inplace<_Tp, _Alloc, _Lp> final
+#else
+  template<typename _Tp, template<typename> class _Alloc, _Lock_policy _Lp>
+    class _Sp_counted_ptr_inplace<_Tp, _Alloc<_Sp_overwrite_tag>, _Lp> final
+#endif
+    : public _Sp_counted_base<_Lp>
+    {
+      [[no_unique_address]] _Alloc _M_alloc;
+
+      union {
+	_Tp _M_obj;
+	char _M_unused;
+      };
+
+      friend class __shared_count<_Lp>; // To be able to call _M_ptr().
+
+      _Tp* _M_ptr() noexcept { return std::__addressof(_M_obj); }
+
+    public:
+      using __allocator_type = __alloc_rebind<_Alloc, _Sp_counted_ptr_inplace>;
+
+      _Sp_counted_ptr_inplace(const _Alloc& __a)
+      : _M_alloc(__a)
+      {
+	::new((void*)_M_ptr()) _Tp; // default-initialized, for overwrite.
+      }
+
+      ~_Sp_counted_ptr_inplace() noexcept { }
+
+      virtual void
+      _M_dispose() noexcept
+      {
+	_M_obj.~_Tp();
+      }
+
+      // 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 __a(_M_alloc);
+	auto __p = pointer_traits<pointer>::pointer_to(*this);
+	__allocated_ptr<__allocator_type> __guard_ptr{ __a, __p };
+	this->~_Sp_counted_ptr_inplace();
+      }
+
+      void*
+      _M_get_deleter(const std::type_info&) noexcept override
+      { return nullptr; }
+    };
+#endif // C++20
+
+#if __cplusplus <= 201703L
+# define __cpp_lib_shared_ptr_arrays 201611L
+#else
+# define __cpp_lib_shared_ptr_arrays 201707L
+
+  struct _Sp_overwrite_tag;
+
+  // For make_shared<T[]>, make_shared<T[N]>, allocate_shared<T[]> etc.
+  template<typename _Alloc>
+    struct _Sp_counted_array_base
+    {
+      [[no_unique_address]] _Alloc _M_alloc{};
+      size_t _M_n = 0;
+      bool _M_overwrite = false;
+
+      typename allocator_traits<_Alloc>::pointer
+      _M_alloc_array(size_t __tail)
+      {
+	return allocator_traits<_Alloc>::allocate(_M_alloc, _M_n + __tail);
+      }
+
+      void
+      _M_dealloc_array(typename allocator_traits<_Alloc>::pointer __p,
+		       size_t __tail)
+      {
+	allocator_traits<_Alloc>::deallocate(_M_alloc, __p, _M_n + __tail);
+      }
+
+      // Init the array elements
+      template<typename _Init>
+	void
+	_M_init(typename allocator_traits<_Alloc>::value_type* __p,
+		_Init __init)
+	{
+	  using _Tp = remove_pointer_t<_Init>;
+	  using _Up = typename allocator_traits<_Alloc>::value_type;
+
+	  if constexpr (is_same_v<_Init, _Sp_overwrite_tag>)
+	    {
+	      std::uninitialized_default_construct_n(__p, _M_n);
+	      _M_overwrite = true;
+	    }
+	  else if (__init == nullptr)
+	    std::__uninitialized_default_n_a(__p, _M_n, _M_alloc);
+	  else if constexpr (!is_array_v<_Tp>)
+	    std::__uninitialized_fill_n_a(__p, _M_n, *__init, _M_alloc);
+	  else
+	    {
+	      struct _Iter
+	      {
+		using value_type = _Up;
+		using difference_type = ptrdiff_t;
+		using pointer = const _Up*;
+		using reference = const _Up&;
+		using iterator_category = forward_iterator_tag;
+
+		const _Up* _M_p;
+		size_t _M_len;
+		size_t _M_pos;
+
+		_Iter& operator++() { ++_M_pos; return *this; }
+		_Iter operator++(int) { auto __i(*this); ++_M_pos; return __i; }
+
+		reference operator*() const { return _M_p[_M_pos % _M_len]; }
+		pointer operator->() const { return _M_p + (_M_pos % _M_len); }
+
+		bool operator==(const _Iter& __i) const
+		{ return _M_pos == __i._M_pos; }
+	      };
+
+	      _Iter __first{_S_first_elem(__init), sizeof(_Tp) / sizeof(_Up)};
+	      _Iter __last = __first;
+	      __last._M_pos = _M_n;
+	      std::__uninitialized_copy_a(__first, __last, __p, _M_alloc);
+	    }
+	}
+
+    protected:
+      // Destroy the array elements
+      void
+      _M_dispose_array(typename allocator_traits<_Alloc>::value_type* __p)
+      {
+	if (_M_overwrite)
+	  std::destroy_n(__p, _M_n);
+	else
+	  {
+	    size_t __n = _M_n;
+	    while (__n--)
+	      allocator_traits<_Alloc>::destroy(_M_alloc, __p + __n);
+	  }
+      }
+
+    private:
+      template<typename _Tp>
+	static _Tp*
+	_S_first_elem(_Tp* __p) { return __p; }
+
+      template<typename _Tp, size_t _Nm>
+	static auto
+	_S_first_elem(_Tp (*__p)[_Nm]) { return _S_first_elem(*__p); }
+    };
+
+  // Control block for make_shared<T[]>, make_shared<T[N]> etc. that will be
+  // placed into unused memory at the end of the array.
+  template<typename _Alloc, _Lock_policy _Lp>
+    class _Sp_counted_array final
+    : public _Sp_counted_base<_Lp>, _Sp_counted_array_base<_Alloc>
+    {
+      using pointer = typename allocator_traits<_Alloc>::pointer;
+
+      pointer _M_alloc_ptr;
+
+      auto _M_ptr() const noexcept { return std::to_address(_M_alloc_ptr); }
+
+      friend class __shared_count<_Lp>; // To be able to call _M_ptr().
+
+    public:
+      _Sp_counted_array(const _Sp_counted_array_base<_Alloc>& __a,
+			pointer __p) noexcept
+      : _Sp_counted_array_base<_Alloc>(__a), _M_alloc_ptr(__p)
+      { }
+
+      ~_Sp_counted_array() = default;
+
+      virtual void
+      _M_dispose() noexcept
+      {
+	if (this->_M_n)
+	  this->_M_dispose_array(_M_ptr());
+      }
+
+      // Override because the allocator needs to know the dynamic type
+      virtual void
+      _M_destroy() noexcept
+      {
+	_Sp_counted_array_base<_Alloc> __a = *this;
+	pointer __p = _M_alloc_ptr;
+	this->~_Sp_counted_array();
+	__a._M_dealloc_array(__p, _S_tail());
+      }
+
+      // Returns the number of additional array elements that must be
+      // allocated in order to store a _Sp_counted_array at the end.
+      static constexpr size_t
+      _S_tail()
+      {
+	// The array elemenent type.
+	using _Tp = typename allocator_traits<_Alloc>::value_type;
+
+	// The space needed to store a _Sp_counted_array object.
+	size_t __bytes = sizeof(_Sp_counted_array);
+
+	// Add any padding needed for manual alignment within the buffer.
+	if constexpr (alignof(_Tp) < alignof(_Sp_counted_array))
+	  __bytes += alignof(_Sp_counted_array) - alignof(_Tp);
+
+	return (__bytes + sizeof(_Tp) - 1) / sizeof(_Tp);
+      }
+
+      void*
+      _M_get_deleter(const std::type_info&) noexcept override
+      { return nullptr; }
+    };
+#endif // C++20
+
   // The default deleter for shared_ptr<T[]> and shared_ptr<T[N]>.
   struct __sp_array_delete
   {
@@ -650,12 +885,18 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
   template<_Lock_policy _Lp>
     class __shared_count
     {
+      // Prevent _Sp_alloc_shared_tag from matching the shared_ptr(P, D) ctor.
       template<typename _Tp>
 	struct __not_alloc_shared_tag { using type = void; };
 
       template<typename _Tp>
 	struct __not_alloc_shared_tag<_Sp_alloc_shared_tag<_Tp>> { };
 
+#if __cpp_lib_shared_ptr_arrays >= 201707L
+      template<typename _Alloc>
+	struct __not_alloc_shared_tag<_Sp_counted_array_base<_Alloc>> { };
+#endif
+
     public:
       constexpr __shared_count() noexcept : _M_pi(0)
       { }
@@ -727,6 +968,51 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	  __p = __pi->_M_ptr();
 	}
 
+#if __cpp_lib_shared_ptr_arrays >= 201707L
+      template<typename _Tp, typename _Alloc, typename _Init>
+	__shared_count(_Tp*& __p, const _Sp_counted_array_base<_Alloc>& __a,
+		       _Init __init)
+	{
+	  using _Up = remove_all_extents_t<_Tp>;
+	  static_assert(is_same_v<_Up, typename _Alloc::value_type>);
+
+	  using _Sp_ca_type = _Sp_counted_array<_Alloc, _Lp>;
+	  const size_t __tail = _Sp_ca_type::_S_tail();
+
+	  struct _Guarded_ptr : _Sp_counted_array_base<_Alloc>
+	  {
+	    typename allocator_traits<_Alloc>::pointer _M_ptr;
+
+	    _Guarded_ptr(_Sp_counted_array_base<_Alloc> __a)
+	    : _Sp_counted_array_base<_Alloc>(__a),
+	      _M_ptr(this->_M_alloc_array(_Sp_ca_type::_S_tail()))
+	    { }
+
+	    ~_Guarded_ptr()
+	    {
+	      if (_M_ptr)
+		this->_M_dealloc_array(_M_ptr, _Sp_ca_type::_S_tail());
+	    }
+	  };
+
+	  _Guarded_ptr __guard{__a};
+	  _Up* const __raw = std::to_address(__guard._M_ptr);
+	  __guard._M_init(__raw, __init); // might throw
+
+	  void* __c = __raw + __a._M_n;
+	  if constexpr (alignof(_Up) < alignof(_Sp_ca_type))
+	    {
+	      size_t __space = sizeof(_Up) * __tail;
+	      __c = std::align(alignof(_Sp_ca_type), sizeof(_Sp_ca_type),
+			       __c, __space);
+	    }
+	  auto __pi = ::new(__c) _Sp_ca_type(__guard, __guard._M_ptr);
+	  __guard._M_ptr = nullptr;
+	  _M_pi = __pi;
+	  __p = reinterpret_cast<_Tp*>(__raw);
+	}
+#endif
+
 #if _GLIBCXX_USE_DEPRECATED
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
@@ -957,8 +1243,6 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	_M_pi = nullptr;
     }
 
-#define __cpp_lib_shared_ptr_arrays 201611L
-
   // Helper traits for shared_ptr of array:
 
   // A pointer type Y* is said to be compatible with a pointer type T* when
@@ -1420,6 +1704,15 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	friend __shared_ptr<_Tp1, _Lp1>
 	__allocate_shared(const _Alloc& __a, _Args&&... __args);
 
+#if __cpp_lib_shared_ptr_arrays >= 201707L
+      // This constructor is non-standard, it is used by allocate_shared<T[]>.
+      template<typename _Alloc, typename _Init = const remove_extent_t<_Tp>*>
+	__shared_ptr(const _Sp_counted_array_base<_Alloc>& __a,
+		     _Init __init = nullptr)
+	: _M_ptr(), _M_refcount(_M_ptr, __a, __init)
+	{ }
+#endif
+
       // This constructor is used by __weak_ptr::lock() and
       // shared_ptr::shared_ptr(const weak_ptr&, std::nothrow_t).
       __shared_ptr(const __weak_ptr<_Tp, _Lp>& __r, std::nothrow_t) noexcept
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index c5d5efb1314..a8b792e9b0f 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -51,8 +51,10 @@ 
 
 #if _GLIBCXX_HOSTED
 # define __cpp_lib_allocator_traits_is_always_equal 201411
+#if __cplusplus < 201703L // N.B. updated value in C++20
 # define __cpp_lib_shared_ptr_arrays 201611L
 #endif
+#endif
 
 #if !defined(__STRICT_ANSI__)
 // gnu++11
@@ -167,7 +169,7 @@ 
 # define __cpp_lib_scoped_lock 201703
 # define __cpp_lib_shared_mutex 201505L
 #endif
-#define __cpp_lib_shared_ptr_weak_type 201606
+#define __cpp_lib_shared_ptr_weak_type 201606L
 #define __cpp_lib_string_view 201803L
 #if _GLIBCXX_HAVE_USELOCALE
 # define __cpp_lib_to_chars 201611L
@@ -275,7 +277,9 @@ 
 #if __cpp_lib_atomic_wait || _GLIBCXX_HAVE_POSIX_SEMAPHORE
 # define __cpp_lib_semaphore 201907L
 #endif
+#define __cpp_lib_shared_ptr_arrays 201707L
 #define __cpp_lib_shift 201806L
+#define __cpp_lib_smart_ptr_for_overwrite 202002L
 #if __cpp_lib_concepts
 # define __cpp_lib_span 202002L
 #endif
diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/creation/99006.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/creation/99006.cc
index e070fb9d420..67069a1ec10 100644
--- a/libstdc++-v3/testsuite/20_util/shared_ptr/creation/99006.cc
+++ b/libstdc++-v3/testsuite/20_util/shared_ptr/creation/99006.cc
@@ -1,9 +1,10 @@ 
-// FIXME: This should use { target { c++11 && { ! c++20 } } }
 // { dg-do compile { target { c++11 } } }
 
 #include <memory>
 
-auto p = std::make_shared<int[]>(2); // { dg-error "here" }
+auto p = std::make_shared<int[]>(2); // { dg-error "here" "" { target c++17_down } }
 auto q = std::make_shared<int[2]>(1, 2); // { dg-error "here" }
 
-// { dg-prune-output "static assertion failed" }
+// { dg-prune-output "enable_if<false" }
+// { dg-prune-output "template constraint failure" }
+// { dg-prune-output "no matching function" }
diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/creation/array.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/creation/array.cc
new file mode 100644
index 00000000000..cd614c0ce0a
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/shared_ptr/creation/array.cc
@@ -0,0 +1,224 @@ 
+// { dg-options "-std=gnu++20" }
+// { dg-do run { target c++20 } }
+
+// C++20 20.11.3.7 shared_ptr Creation [util.smartptr.shared.create]
+
+#include <memory>
+
+#ifndef __cpp_lib_shared_ptr_arrays
+# error "Feature-test macro for make_shared arrays missing in <memory>"
+#elif __cpp_lib_shared_ptr_arrays < 201707L
+# error "Feature-test macro for make_shared arrays has wrong value in <memory>"
+#endif
+
+#include <testsuite_hooks.h>
+
+int counter = 0;
+
+template<typename T>
+struct Alloc : std::allocator<T>
+{
+  Alloc() = default;
+
+  template<typename U>
+    Alloc(const Alloc<U>&) { }
+
+  T* allocate(std::size_t n)
+  {
+    ++counter;
+    return std::allocator<T>::allocate(n);
+  }
+};
+
+void
+test01()
+{
+  Alloc<int> a;
+
+  std::shared_ptr<int[]> p1 = std::allocate_shared<int[]>(a, 24);
+  VERIFY( counter == 1 );
+  VERIFY( p1[23] == 0 );
+  std::shared_ptr<int[48]> p2 = std::allocate_shared<int[48]>(a);
+  VERIFY( counter == 2 );
+  VERIFY( p2[47] == 0 );
+
+  std::shared_ptr<int[][12]> p3 = std::allocate_shared<int[][12]>(a, 3);
+  VERIFY( counter == 3 );
+  VERIFY( p3[2][11] == 0 );
+  std::shared_ptr<int[4][5]> p4 = std::allocate_shared<int[4][5]>(a);
+  VERIFY( counter == 4 );
+  VERIFY( p4[3][4] == 0 );
+}
+
+void
+test02()
+{
+  std::shared_ptr<int[]> p1 = std::make_shared<int[]>(24);
+  VERIFY( p1[23] == 0 );
+  std::shared_ptr<int[48]> p2 = std::make_shared<int[48]>();
+  VERIFY( p2[47] == 0 );
+
+  std::shared_ptr<int[][12]> p3 = std::make_shared<int[][12]>(3);
+  VERIFY( p3[2][11] == 0 );
+  std::shared_ptr<int[4][5]> p4 = std::make_shared<int[4][5]>();
+  VERIFY( p4[3][4] == 0 );
+}
+
+#include <vector>
+
+std::vector<std::uintptr_t> addresses;
+
+void
+test03()
+{
+  // Verify construction and destruction order
+  struct Addressed
+  {
+    Addressed() { addresses.push_back(me()); }
+    ~Addressed() { VERIFY( addresses.back() == me() ); addresses.pop_back(); }
+
+    std::uintptr_t me() const { return reinterpret_cast<std::uintptr_t>(this); }
+  };
+
+  auto check = [](auto shptr) {
+    std::uintptr_t last = 0;
+    for (auto a : addresses)
+    {
+      VERIFY( a > last );
+      last = a;
+    }
+    shptr.reset();
+    return addresses.empty();
+  };
+
+  VERIFY( check(std::make_shared<Addressed[][2]>(3)) );
+  VERIFY( check(std::make_shared<Addressed[4][2]>()) );
+}
+
+void
+test04()
+{
+  // Verify initial value
+  auto p1 = std::make_shared<int[]>(3, 9);
+  VERIFY( p1[0] == 9 && p1[1] == 9 && p1[2] == 9 );
+
+  auto p2 = std::make_shared<int[2]>(4);
+  VERIFY( p2[0] == 4 && p2[1] == 4 );
+
+  auto p3 = std::make_shared<int[][3]>(10, {1,2,3});
+  const auto& p3_0 = p3[0];
+  VERIFY( p3_0[0] == 1 && p3_0[1] == 2 && p3_0[2] == 3 );
+  for (int i = 1; i < 10; ++i)
+    for (int j = 0; j < 3; ++j)
+      VERIFY( p3[i][j] == p3_0[j] );
+
+  auto p4 = std::make_shared<int[10][3]>({4,5,6});
+  const auto& p4_0 = p4[0];
+  VERIFY( p4_0[0] == 4 && p4_0[1] == 5 && p4_0[2] == 6 );
+  for (int i = 1; i < 10; ++i)
+    for (int j = 0; j < 3; ++j)
+      VERIFY( p4[i][j] == p4_0[j] );
+
+  auto p5 = std::make_shared<int[][3][2]>(10, {{1,2},{3,4},{5,6}});
+  const auto& p5_0 = p5[0];
+  VERIFY( p5_0[0][0] == 1 && p5_0[0][1] == 2 );
+  VERIFY( p5_0[1][0] == 3 && p5_0[1][1] == 4 );
+  VERIFY( p5_0[2][0] == 5 && p5_0[2][1] == 6 );
+  for (int i = 1; i < 10; ++i)
+    for (int j = 0; j < 3; ++j)
+      for (int k = 0; k < 2; ++k)
+	VERIFY( p5[i][j][k] == p5_0[j][k] );
+
+  auto p6 = std::make_shared<int[4][3][2]>({{7,8},{9,10},{11,12}});
+  const auto& p6_0 = p6[0];
+  VERIFY( p6_0[0][0] ==  7 && p6_0[0][1] ==  8 );
+  VERIFY( p6_0[1][0] ==  9 && p6_0[1][1] == 10 );
+  VERIFY( p6_0[2][0] == 11 && p6_0[2][1] == 12 );
+  for (int i = 1; i < 4; ++i)
+    for (int j = 0; j < 3; ++j)
+      for (int k = 0; k < 2; ++k)
+	VERIFY( p6[i][j][k] == p6_0[j][k] );
+}
+
+void
+test05()
+{
+  // Examples from the standard
+  using namespace std;
+
+  // Example 2
+  {
+    shared_ptr<double[]> p = make_shared<double[]>(1024);
+    // shared_ptr to a value-initialized double[1024]
+    for (int i = 0; i < 1024; ++i)
+      VERIFY( p[i] == 0.0 );
+
+    shared_ptr<double[][2][2]> q = make_shared<double[][2][2]>(6);
+    // shared_ptr to a value-initialized double[6][2][2]
+    for (int i = 0; i < 6; ++i)
+      for (auto& j : q[i])
+	for (auto& k : j)
+	  VERIFY( k == 0.0 );
+  }
+
+  // Example 3
+  {
+    shared_ptr<double[1024]> p = make_shared<double[1024]>();
+    // shared_ptr to a value-initialized double[1024]
+    for (int i = 0; i < 1024; ++i)
+      VERIFY( p[i] == 0.0 );
+
+    shared_ptr<double[6][2][2]> q = make_shared<double[6][2][2]>();
+    // shared_ptr to a value-initialized double[6][2][2]
+    for (int i = 0; i < 6; ++i)
+      for (auto& j : q[i])
+	for (auto& k : j)
+	  VERIFY( k == 0.0 );
+  }
+
+  // Example 4
+  {
+    shared_ptr<double[]> p = make_shared<double[]>(1024, 1.0);
+    // shared_ptr to a double[1024], where each element is 1.0
+    for (int i = 0; i < 1024; ++i)
+      VERIFY( p[i] == 1.0 );
+
+    shared_ptr<double[][2]> q = make_shared<double[][2]>(6, {1.0, 0.0});
+    // shared_ptr to a double[6][2], where each double[2] element is {1.0, 0.0}
+    for (int i = 0; i < 6; ++i)
+      VERIFY( q[i][0] == 1.0 && q[i][1] == 0.0 );
+
+    shared_ptr<vector<int>[]> r = make_shared<vector<int>[]>(4, {1, 2});
+    // shared_ptr to a vector<int>[4], where each vector has contents {1, 2}
+    for (int i = 0; i < 4; ++i)
+      VERIFY( r[i] == vector<int>({1, 2}) );
+  }
+
+  // Example 5
+  {
+    shared_ptr<double[1024]> p = make_shared<double[1024]>(1.0);
+    // shared_ptr to a double[1024], where each element is 1.0
+    for (int i = 0; i < 1024; ++i)
+      VERIFY( p[i] == 1.0 );
+
+    shared_ptr<double[6][2]> q = make_shared<double[6][2]>({1.0, 0.0});
+    // shared_ptr to a double[6][2], where each double[2] element is {1.0, 0.0}
+    for (int i = 0; i < 6; ++i)
+      VERIFY( q[i][0] == 1.0 && q[i][1] == 0.0 );
+
+    shared_ptr<vector<int>[4]> r = make_shared<vector<int>[4]>({1, 2});
+    // shared_ptr to a vector<int>[4], where each vector has contents {1, 2}
+    for (int i = 0; i < 4; ++i)
+      VERIFY( r[i] == vector<int>({1, 2}) );
+  }
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+  test04();
+  test05();
+}
diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/creation/overwrite.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/creation/overwrite.cc
new file mode 100644
index 00000000000..f207fdee097
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/shared_ptr/creation/overwrite.cc
@@ -0,0 +1,143 @@ 
+// { dg-options "-std=gnu++20 -fno-lifetime-dse -O0" }
+// { dg-do run { target c++20 } }
+
+// C++20 20.11.3.7 shared_ptr Creation [util.smartptr.shared.create]
+
+#include <memory>
+
+#ifndef __cpp_lib_smart_ptr_for_overwrite
+# error "Feature-test macro for make_shared_for_overwrite missing in <memory>"
+#elif __cpp_lib_smart_ptr_for_overwrite < 202002L
+# error "Feature-test macro for make_shared_for_overwrite has wrong value in <memory>"
+#endif
+
+#include <cstring>
+#include <testsuite_hooks.h>
+
+int counter = 0;
+
+template<typename T>
+struct Alloc : std::allocator<T>
+{
+  Alloc() = default;
+
+  template<typename U>
+    Alloc(const Alloc<U>&) { }
+
+  T* allocate(std::size_t n)
+  {
+    ++counter;
+    void* p = std::allocator<T>::allocate(n);
+    // need -fno-lifetime-dse to check for these values later.
+    std::memset(p, 0xff, n * sizeof(T));
+    return (T*)p;
+  }
+
+  void construct(auto*, auto&&...)
+  {
+    // The objects must be default-initialized, not using this function.
+    VERIFY( ! "allocator_traits::construct" );
+  }
+
+  void destroy(auto*)
+  {
+    // The objects must be destroyed by ~T(), not using this function.
+    VERIFY( ! "allocator_traits::destroy" );
+  }
+};
+
+void
+test01()
+{
+  Alloc<int> a;
+  const int expected = 0xffffffff;
+
+  std::shared_ptr<int> p1 = std::allocate_shared_for_overwrite<int>(a);
+  VERIFY( counter == 1 );
+  VERIFY( *p1 == expected );
+  std::shared_ptr<int[44]> p2 = std::allocate_shared_for_overwrite<int[44]>(a);
+  VERIFY( counter == 2 );
+  VERIFY( p2[0] == expected );
+  p2.reset();
+  std::shared_ptr<int[]> p3 = std::allocate_shared_for_overwrite<int[]>(a, 88);
+  VERIFY( counter == 3 );
+  VERIFY( p3[0] == expected );
+  VERIFY( p3[87] == expected );
+  std::shared_ptr<int[3][4]> p4 = std::allocate_shared_for_overwrite<int[3][4]>(a);
+  VERIFY( counter == 4 );
+  VERIFY( p4[0][0] == expected );
+  VERIFY( p4[2][3] == expected );
+  std::shared_ptr<int[][5]> p5 = std::allocate_shared_for_overwrite<int[][5]>(a, 6);
+  VERIFY( counter == 5 );
+  VERIFY( p5[0][0] == expected );
+  VERIFY( p5[5][4] == expected );
+
+  struct BigBoi { int x[100]; };
+  std::shared_ptr<BigBoi> p6 = std::allocate_shared_for_overwrite<BigBoi>(a);
+  VERIFY( counter == 6 );
+  VERIFY( p6->x[0] == expected );
+  std::shared_ptr<BigBoi[22]> p7 = std::allocate_shared_for_overwrite<BigBoi[22]>(a);
+  VERIFY( counter == 7 );
+  VERIFY( p7[0].x[0] == expected );
+  VERIFY( p7[21].x[99] == expected );
+  std::shared_ptr<BigBoi[]> p8 = std::allocate_shared_for_overwrite<BigBoi[]>(a, 11);
+  VERIFY( counter == 8 );
+  VERIFY( p8[0].x[0] == expected );
+  VERIFY( p8[10].x[10] == expected );
+}
+
+void
+test02()
+{
+  // These aren't created by the custom allocator, so we can't check that the
+  // memory was left uninitialized. Just dereference them.
+
+  std::shared_ptr<int> p1 = std::make_shared_for_overwrite<int>();
+  (void) *p1;
+  std::shared_ptr<int[44]> p2 = std::make_shared_for_overwrite<int[44]>();
+  (void) p2[0];
+  std::shared_ptr<int[]> p3 = std::make_shared_for_overwrite<int[]>(88);
+  (void) p3[0];
+  (void) p3[87];
+  std::shared_ptr<int[3][4]> p4 = std::make_shared_for_overwrite<int[3][4]>();
+  (void) p4[0][0];
+  (void) p4[2][3];
+  std::shared_ptr<int[][5]> p5 = std::make_shared_for_overwrite<int[][5]>(6);
+  (void) p5[0][0];
+  (void) p5[5][4];
+
+  struct BigBoi { int x[100]; };
+  std::shared_ptr<BigBoi> p6 = std::make_shared_for_overwrite<BigBoi>();
+  (void) p6->x[0];
+  std::shared_ptr<BigBoi[22]> p7 = std::make_shared_for_overwrite<BigBoi[22]>();
+  (void) p7[0].x[0];
+  (void) p7[21].x[99];
+  std::shared_ptr<BigBoi[]> p8 = std::make_shared_for_overwrite<BigBoi[]>(11);
+  (void) p8[0].x[0];
+  (void) p8[10].x[10];
+}
+
+void
+test03()
+{
+  // Type with non-trivial initialization should still be default-initialized.
+  struct NonTriv
+  {
+    int init = 0xbb;
+    int uninit;
+  };
+  std::shared_ptr<NonTriv> a = std::make_shared_for_overwrite<NonTriv>();
+  VERIFY( a->init == 0xbb );
+  std::shared_ptr<NonTriv[]> b = std::make_shared_for_overwrite<NonTriv[2]>();
+  VERIFY( b[1].init == 0xbb );
+  std::shared_ptr<NonTriv[]> c = std::make_shared_for_overwrite<NonTriv[]>(2);
+  VERIFY( c[1].init == 0xbb );
+}
+
+int
+main()
+{
+  test01();
+  test02();
+  test03();
+}
diff --git a/libstdc++-v3/testsuite/20_util/shared_ptr/creation/version.cc b/libstdc++-v3/testsuite/20_util/shared_ptr/creation/version.cc
new file mode 100644
index 00000000000..0e47f19061d
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/shared_ptr/creation/version.cc
@@ -0,0 +1,18 @@ 
+// { dg-options "-std=gnu++20" }
+// { dg-do preprocess { target c++20 } }
+
+// C++20 20.11.3.7 shared_ptr Creation [util.smartptr.shared.create]
+
+#include <version>
+
+#ifndef __cpp_lib_shared_ptr_arrays
+# error "Feature-test macro for make_shared arrays missing in <version>"
+#elif __cpp_lib_shared_ptr_arrays < 201707L
+# error "Feature-test macro for make_shared arrays has wrong value in <version>"
+#endif
+
+#ifndef __cpp_lib_smart_ptr_for_overwrite
+# error "Feature-test macro for make_shared_for_overwrite missing in <version>"
+#elif __cpp_lib_smart_ptr_for_overwrite < 202002L
+# error "Feature-test macro for make_shared_for_overwrite has wrong value in <version>"
+#endif
diff --git a/libstdc++-v3/testsuite/20_util/unique_ptr/creation/for_overwrite.cc b/libstdc++-v3/testsuite/20_util/unique_ptr/creation/for_overwrite.cc
index c6bcbf08f4d..f736c762a43 100644
--- a/libstdc++-v3/testsuite/20_util/unique_ptr/creation/for_overwrite.cc
+++ b/libstdc++-v3/testsuite/20_util/unique_ptr/creation/for_overwrite.cc
@@ -1,4 +1,4 @@ 
-// { dg-options "-std=gnu++20" }
+// { dg-options "-std=gnu++20 -fno-lifetime-dse -O0" }
 // { dg-do run { target c++2a } }
 // { dg-xfail-run-if "AIX operator new" { powerpc-ibm-aix* } }
 
@@ -22,6 +22,13 @@ 
 // C++20 20.11.1.5 unique_ptr creation [unique.ptr.create]
 
 #include <memory>
+
+#ifndef __cpp_lib_smart_ptr_for_overwrite
+# error "Feature-test macro for make_unique_for_overwrite missing in <memory>"
+#elif __cpp_lib_smart_ptr_for_overwrite < 202002L
+# error "Feature-test macro for make_unique_for_overwrite has wrong value in <memory>"
+#endif
+
 #include <cstdlib>
 #include <cstring>
 #include <testsuite_hooks.h>
@@ -58,9 +65,25 @@  test02()
     VERIFY( c == 0xaa );
 }
 
+void
+test03()
+{
+  // Type with non-trivial initialization should still be default-initialized.
+  struct NonTriv
+  {
+    int init = 0xbb;
+    int uninit;
+  };
+  std::unique_ptr<NonTriv> a = std::make_unique_for_overwrite<NonTriv>();
+  VERIFY( a->init == 0xbb );
+  std::unique_ptr<NonTriv[]> b = std::make_unique_for_overwrite<NonTriv[]>(2);
+  VERIFY( b[1].init == 0xbb );
+}
+
 int
 main()
 {
   test01();
   test02();
+  test03();
 }