diff --git a/libstdc++-v3/include/Makefile.am b/libstdc++-v3/include/Makefile.am
index 6209f390e08..dab9f720cbb 100644
--- a/libstdc++-v3/include/Makefile.am
+++ b/libstdc++-v3/include/Makefile.am
@@ -130,6 +130,7 @@ bits_freestanding = \
 	${bits_srcdir}/max_size_type.h \
 	${bits_srcdir}/memoryfwd.h \
 	${bits_srcdir}/move.h \
+	${bits_srcdir}/out_ptr.h \
 	${bits_srcdir}/predefined_ops.h \
 	${bits_srcdir}/parse_numbers.h \
 	${bits_srcdir}/ptr_traits.h \
diff --git a/libstdc++-v3/include/Makefile.in b/libstdc++-v3/include/Makefile.in
index 596fa0d2390..4f7ab2dfbab 100644
--- a/libstdc++-v3/include/Makefile.in
+++ b/libstdc++-v3/include/Makefile.in
@@ -485,6 +485,7 @@ bits_freestanding = \
 	${bits_srcdir}/max_size_type.h \
 	${bits_srcdir}/memoryfwd.h \
 	${bits_srcdir}/move.h \
+	${bits_srcdir}/out_ptr.h \
 	${bits_srcdir}/predefined_ops.h \
 	${bits_srcdir}/parse_numbers.h \
 	${bits_srcdir}/ptr_traits.h \
diff --git a/libstdc++-v3/include/bits/out_ptr.h b/libstdc++-v3/include/bits/out_ptr.h
new file mode 100644
index 00000000000..49712fa7e31
--- /dev/null
+++ b/libstdc++-v3/include/bits/out_ptr.h
@@ -0,0 +1,432 @@
+// Smart pointer adaptors -*- C++ -*-
+
+// Copyright The GNU Toolchain Authors.
+//
+// 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 include/bits/out_ptr.h
+ *  This is an internal header file, included by other library headers.
+ *  Do not attempt to use it directly. @headername{memory}
+ */
+
+#ifndef _GLIBCXX_OUT_PTR_H
+#define _GLIBCXX_OUT_PTR_H 1
+
+#pragma GCC system_header
+
+#include <bits/version.h>
+
+#ifdef __glibcxx_out_ptr // C++ >= 23
+
+#include <tuple>
+#include <bits/ptr_traits.h>
+
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+#ifdef __glibcxx_out_ptr // C++ >= 23
+  /// Adapt a smart pointer for functions taking an output pointer parameter.
+  template<typename _Smart, typename _Pointer, typename... _Args>
+    class out_ptr_t
+    {
+      static_assert(!__is_shared_ptr<_Smart> || sizeof...(_Args) != 0,
+		    "a deleter must be used when adapting std::shared_ptr "
+		    "with std::out_ptr");
+
+    public:
+      explicit
+      out_ptr_t(_Smart& __smart, _Args... __args)
+      : _M_impl{__smart, std::forward<_Args>(__args)...}
+      {
+	if constexpr (requires { _M_impl._M_out_init(); })
+	  _M_impl._M_out_init();
+      }
+
+      out_ptr_t(const out_ptr_t&) = delete;
+
+      ~out_ptr_t() = default;
+
+      operator _Pointer*() const noexcept
+      { return _M_impl._M_get(); }
+
+      operator void**() const noexcept requires (!same_as<_Pointer, void*>)
+      {
+	static_assert(is_pointer_v<_Pointer>);
+	_Pointer* __p = *this;
+	return static_cast<void**>(static_cast<void*>(__p));
+      }
+
+    private:
+      // TODO: Move this to namespace scope? e.g. __detail::_Ptr_adapt_impl
+      template<typename, typename, typename...>
+	struct _Impl
+	{
+	  // This constructor must not modify __s because out_ptr_t and
+	  // inout_ptr_t want to do different things. After construction
+	  // they call _M_out_init() or _M_inout_init() respectively.
+	  _Impl(_Smart& __s, _Args&&... __args)
+	  : _M_smart(__s), _M_args(std::forward<_Args>(__args)...)
+	  { }
+
+	  // Called by out_ptr_t to clear the smart pointer before using it.
+	  void
+	  _M_out_init()
+	  {
+	    // _GLIBCXX_RESOLVE_LIB_DEFECTS
+	    // 3734. Inconsistency in inout_ptr and out_ptr for empty case
+	    if constexpr (requires { _M_smart.reset(); })
+	      _M_smart.reset();
+	    else
+	      _M_smart = _Smart();
+	  }
+
+	  // Called by inout_ptr_t to copy the smart pointer's value
+	  // to the pointer that is returned from _M_get().
+	  void
+	  _M_inout_init()
+	  { _M_ptr = _M_smart.release(); }
+
+	  // The pointer value returned by operator Pointer*().
+	  _Pointer*
+	  _M_get() const
+	  { return __builtin_addressof(const_cast<_Pointer&>(_M_ptr)); }
+
+	  // Finalize the effects on the smart pointer.
+	  ~_Impl() noexcept(false);
+
+	  _Smart& _M_smart;
+	  [[no_unique_address]] _Pointer _M_ptr{};
+	  [[no_unique_address]] tuple<_Args...> _M_args;
+	};
+
+      // Partial specialization for raw pointers.
+      template<typename _Tp>
+	struct _Impl<_Tp*, _Tp*>
+	{
+	  void
+	  _M_out_init()
+	  { _M_p = nullptr; }
+
+	  void
+	  _M_inout_init()
+	  { }
+
+	  _Tp**
+	  _M_get() const
+	  { return __builtin_addressof(const_cast<_Tp*&>(_M_p)); }
+
+	  _Tp*& _M_p;
+	};
+
+      // Partial specialization for raw pointers, with conversion.
+      template<typename _Tp, typename _Ptr> requires (!is_same_v<_Ptr, _Tp*>)
+	struct _Impl<_Tp*, _Ptr>
+	{
+	  explicit
+	  _Impl(_Tp*& __p)
+	  : _M_p(__p)
+	  { }
+
+	  void
+	  _M_out_init()
+	  { _M_p = nullptr; }
+
+	  void
+	  _M_inout_init()
+	  { _M_ptr = _M_p; }
+
+	  _Pointer*
+	  _M_get() const
+	  { return __builtin_addressof(const_cast<_Pointer&>(_M_ptr)); }
+
+	  ~_Impl() { _M_p = static_cast<_Tp*>(_M_ptr); }
+
+	  _Tp*& _M_p;
+	  _Pointer _M_ptr{};
+	};
+
+      // Partial specialization for std::unique_ptr.
+      // This specialization gives direct access to the private member
+      // of the unique_ptr, avoiding the overhead of storing a separate
+      // pointer and then resetting the unique_ptr in the destructor.
+      // FIXME: constrain to only match the primary template,
+      // not program-defined specializations of unique_ptr.
+      template<typename _Tp, typename _Del>
+	struct _Impl<unique_ptr<_Tp, _Del>,
+		     typename unique_ptr<_Tp, _Del>::pointer>
+	{
+	  void
+	  _M_out_init()
+	  { _M_smart.reset(); }
+
+	  _Pointer*
+	  _M_get() const noexcept
+	  { return __builtin_addressof(_M_smart._M_t._M_ptr()); }
+
+	  _Smart& _M_smart;
+	};
+
+      // Partial specialization for std::unique_ptr with replacement deleter.
+      // FIXME: constrain to only match the primary template,
+      // not program-defined specializations of unique_ptr.
+      template<typename _Tp, typename _Del, typename _Del2>
+	struct _Impl<unique_ptr<_Tp, _Del>,
+		     typename unique_ptr<_Tp, _Del>::pointer, _Del2>
+	{
+	  void
+	  _M_out_init()
+	  { _M_smart.reset(); }
+
+	  _Pointer*
+	  _M_get() const noexcept
+	  { return __builtin_addressof(_M_smart._M_t._M_ptr()); }
+
+	  ~_Impl()
+	  {
+	    if (_M_smart.get())
+	      _M_smart._M_t._M_deleter() = std::forward<_Del2>(_M_del);
+	  }
+
+	  _Smart& _M_smart;
+	  [[no_unique_address]] _Del2 _M_del;
+	};
+
+      // Partial specialization for std::shared_ptr.
+      // This specialization gives direct access to the private member
+      // of the shared_ptr, avoiding the overhead of storing a separate
+      // pointer and then resetting the shared_ptr in the destructor.
+      // A new control block is allocated in the constructor, so that if
+      // allocation fails it doesn't throw an exception from the destructor.
+      template<typename _Tp, typename _Del, typename _Alloc>
+	requires (is_base_of_v<__shared_ptr<_Tp>, shared_ptr<_Tp>>)
+	struct _Impl<shared_ptr<_Tp>,
+		     typename shared_ptr<_Tp>::element_type*, _Del, _Alloc>
+	{
+	  _Impl(_Smart& __s, _Del __d, _Alloc __a = _Alloc())
+	  : _M_smart(__s)
+	  {
+	    // We know shared_ptr cannot be used with inout_ptr_t
+	    // so we can do all set up here, instead of in _M_out_init().
+	    _M_smart.reset();
+
+	    // Similar to the shared_ptr(Y*, D, A) constructor, except that if
+	    // the allocation throws we do not need (or want) to call deleter.
+	    typename _Scd::__allocator_type __a2(__a);
+	    auto __mem = __a2.allocate(1);
+	    ::new (__mem) _Scd(nullptr, std::forward<_Del>(__d),
+			       std::forward<_Alloc>(__a));
+	    _M_smart._M_refcount._M_pi = __mem;
+	  }
+
+	  _Pointer*
+	  _M_get() const noexcept
+	  { return __builtin_addressof(_M_smart._M_ptr); }
+
+	  ~_Impl()
+	  {
+	    auto& __pi = _M_smart._M_refcount._M_pi;
+
+	    if (_Sp __ptr = _M_smart.get())
+	      static_cast<_Scd*>(__pi)->_M_impl._M_ptr = __ptr;
+	    else // Destroy the control block manually without invoking deleter.
+	      std::__exchange(__pi, nullptr)->_M_destroy();
+	  }
+
+	  _Smart& _M_smart;
+
+	  using _Sp = typename _Smart::element_type*;
+	  using _Scd = _Sp_counted_deleter<_Sp, decay_t<_Del>,
+					   remove_cvref_t<_Alloc>,
+					   __default_lock_policy>;
+	};
+
+      // Partial specialization for std::shared_ptr, without custom allocator.
+      template<typename _Tp, typename _Del>
+	requires (is_base_of_v<__shared_ptr<_Tp>, shared_ptr<_Tp>>)
+	struct _Impl<shared_ptr<_Tp>,
+		     typename shared_ptr<_Tp>::element_type*, _Del>
+	: _Impl<_Smart, _Pointer, _Del, allocator<void>>
+	{
+	  using _Impl<_Smart, _Pointer, _Del, allocator<void>>::_Impl;
+	};
+
+      using _Impl_t = _Impl<_Smart, _Pointer, _Args...>;
+
+      _Impl_t _M_impl;
+
+      template<typename, typename, typename...> friend class inout_ptr_t;
+    };
+
+  /// Adapt a smart pointer for functions taking an output pointer parameter.
+  template<typename _Smart, typename _Pointer, typename... _Args>
+    class inout_ptr_t
+    {
+      static_assert(!__is_shared_ptr<_Smart>,
+		    "std::inout_ptr can not be used to wrap std::shared_ptr");
+
+    public:
+      explicit
+      inout_ptr_t(_Smart& __smart, _Args... __args)
+      : _M_impl{__smart, std::forward<_Args>(__args)...}
+      {
+	if constexpr (requires { _M_impl._M_inout_init(); })
+	  _M_impl._M_inout_init();
+      }
+
+      inout_ptr_t(const inout_ptr_t&) = delete;
+
+      ~inout_ptr_t() = default;
+
+      operator _Pointer*() const noexcept
+      { return _M_impl._M_get(); }
+
+      operator void**() const noexcept requires (!same_as<_Pointer, void*>)
+      {
+	static_assert(is_pointer_v<_Pointer>);
+	_Pointer* __p = *this;
+	return static_cast<void**>(static_cast<void*>(__p));
+      }
+
+    private:
+      // Avoid an invalid instantiation of out_ptr_t<shared_ptr<T>, ...>
+      using _Out_ptr_t
+	= __conditional_t<__is_shared_ptr<_Smart>,
+			  out_ptr_t<void*, void*>,
+			  out_ptr_t<_Smart, _Pointer, _Args...>>;
+      using _Impl_t = typename _Out_ptr_t::_Impl_t;
+      _Impl_t _M_impl;
+    };
+
+/// @cond undocumented
+namespace __detail
+{
+  // POINTER_OF metafunction
+  template<typename _Tp>
+    consteval auto
+    __pointer_of()
+    {
+      if constexpr (requires { typename _Tp::pointer; })
+	return type_identity<typename _Tp::pointer>{};
+      else if constexpr (requires { typename _Tp::element_type; })
+	return type_identity<typename _Tp::element_type*>{};
+      else
+	{
+	  using _Traits = pointer_traits<_Tp>;
+	  if constexpr (requires { typename _Traits::element_type; })
+	    return type_identity<typename _Traits::element_type*>{};
+	}
+      // else POINTER_OF(S) is not a valid type, return void.
+    }
+
+  // POINTER_OF_OR metafunction
+  template<typename _Smart, typename _Ptr>
+    consteval auto
+    __pointer_of_or()
+    {
+      using _TypeId = decltype(__detail::__pointer_of<_Smart>());
+      if constexpr (is_void_v<_TypeId>)
+	return type_identity<_Ptr>{};
+      else
+	return _TypeId{};
+    }
+
+  // Returns Pointer if !is_void_v<Pointer>, otherwise POINTER_OF(Smart).
+  template<typename _Ptr, typename _Smart>
+    consteval auto
+    __choose_ptr()
+    {
+      if constexpr (!is_void_v<_Ptr>)
+	return type_identity<_Ptr>{};
+      else
+	return __detail::__pointer_of<_Smart>();
+    }
+
+  template<typename _Smart, typename _Sp, typename... _Args>
+    concept __resettable = requires (_Smart& __s) {
+      __s.reset(std::declval<_Sp>(), std::declval<_Args>()...);
+    };
+}
+/// @endcond
+
+  template<typename _Pointer = void, typename _Smart, typename... _Args>
+    inline auto
+    out_ptr(_Smart& __s, _Args&&... __args)
+    {
+      using _TypeId = decltype(__detail::__choose_ptr<_Pointer, _Smart>());
+      static_assert(!is_void_v<_TypeId>, "first argument to std::out_ptr "
+		    "must be a pointer-like type");
+
+      using _Ret = out_ptr_t<_Smart, typename _TypeId::type, _Args&&...>;
+      return _Ret(__s, std::forward<_Args>(__args)...);
+    }
+
+  template<typename _Pointer = void, typename _Smart, typename... _Args>
+    inline auto
+    inout_ptr(_Smart& __s, _Args&&... __args)
+    {
+      using _TypeId = decltype(__detail::__choose_ptr<_Pointer, _Smart>());
+      static_assert(!is_void_v<_TypeId>, "first argument to std::inout_ptr "
+		    "must be a pointer-like type");
+
+      using _Ret = inout_ptr_t<_Smart, typename _TypeId::type, _Args&&...>;
+      return _Ret(__s, std::forward<_Args>(__args)...);
+    }
+
+  template<typename _Smart, typename _Pointer, typename... _Args>
+  template<typename _Smart2, typename _Pointer2, typename... _Args2>
+    inline
+    out_ptr_t<_Smart, _Pointer, _Args...>::
+    _Impl<_Smart2, _Pointer2, _Args2...>::~_Impl()
+    {
+      using _TypeId = decltype(__detail::__pointer_of_or<_Smart, _Pointer>());
+      using _Sp = typename _TypeId::type;
+
+      if (!_M_ptr)
+	return;
+
+      _Smart& __s = _M_smart;
+      _Pointer& __p = _M_ptr;
+
+      auto __reset = [&](auto&&... __args) {
+	if constexpr (__detail::__resettable<_Smart, _Sp, _Args...>)
+	  __s.reset(static_cast<_Sp>(__p), std::forward<_Args>(__args)...);
+	else if constexpr (is_constructible_v<_Smart, _Sp, _Args...>)
+	  __s = _Smart(static_cast<_Sp>(__p), std::forward<_Args>(__args)...);
+	else
+	  static_assert(is_constructible_v<_Smart, _Sp, _Args...>);
+      };
+
+      if constexpr (sizeof...(_Args) >= 2)
+	std::apply(__reset, std::move(_M_args));
+      else if constexpr (sizeof...(_Args) == 1)
+	__reset(std::get<0>(std::move(_M_args)));
+      else
+	__reset();
+    }
+#endif // __glibcxx_out_ptr
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace
+
+#endif // __glibcxx_out_ptr
+#endif /* _GLIBCXX_OUT_PTR_H */
+
diff --git a/libstdc++-v3/include/bits/shared_ptr.h b/libstdc++-v3/include/bits/shared_ptr.h
index 02bfdabfd1c..e3c48dead72 100644
--- a/libstdc++-v3/include/bits/shared_ptr.h
+++ b/libstdc++-v3/include/bits/shared_ptr.h
@@ -1158,6 +1158,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
     };
 
+#if __cpp_variable_templates
+  template<typename _Tp>
+    static constexpr bool __is_shared_ptr = false;
+  template<typename _Tp>
+    static constexpr bool __is_shared_ptr<shared_ptr<_Tp>> = true;
+#endif
+
   /// @} relates shared_ptr
   /// @} group pointer_abstractions
 
diff --git a/libstdc++-v3/include/bits/shared_ptr_atomic.h b/libstdc++-v3/include/bits/shared_ptr_atomic.h
index d0be43aec2d..22b1a1d6c50 100644
--- a/libstdc++-v3/include/bits/shared_ptr_atomic.h
+++ b/libstdc++-v3/include/bits/shared_ptr_atomic.h
@@ -388,11 +388,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
    * @{
    */
 
-  template<typename _Up>
-    static constexpr bool __is_shared_ptr = false;
-  template<typename _Up>
-    static constexpr bool __is_shared_ptr<shared_ptr<_Up>> = true;
-
   template<typename _Tp>
     class _Sp_atomic
     {
diff --git a/libstdc++-v3/include/bits/shared_ptr_base.h b/libstdc++-v3/include/bits/shared_ptr_base.h
index edb3eb654ea..1961a9ae9e9 100644
--- a/libstdc++-v3/include/bits/shared_ptr_base.h
+++ b/libstdc++-v3/include/bits/shared_ptr_base.h
@@ -549,6 +549,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       }
 
     private:
+#ifdef __glibcxx_out_ptr
+      template<typename, typename, typename...> friend class out_ptr_t;
+#endif
       _Impl _M_impl;
     };
 
@@ -1126,6 +1129,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #ifdef __glibcxx_atomic_shared_ptr
       template<typename> friend class _Sp_atomic;
 #endif
+#ifdef __glibcxx_out_ptr
+      template<typename, typename, typename...> friend class out_ptr_t;
+#endif
 
       _Sp_counted_base<_Lp>*  _M_pi;
     };
@@ -1776,6 +1782,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
 #ifdef __glibcxx_atomic_shared_ptr
       friend _Sp_atomic<shared_ptr<_Tp>>;
 #endif
+#ifdef __glibcxx_out_ptr
+      template<typename, typename, typename...> friend class out_ptr_t;
+#endif
 
       element_type*	   _M_ptr;         // Contained pointer.
       __shared_count<_Lp>  _M_refcount;    // Reference counter.
diff --git a/libstdc++-v3/include/bits/unique_ptr.h b/libstdc++-v3/include/bits/unique_ptr.h
index 164f5f7e945..9d6f5af0a04 100644
--- a/libstdc++-v3/include/bits/unique_ptr.h
+++ b/libstdc++-v3/include/bits/unique_ptr.h
@@ -515,6 +515,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // Disable copy from lvalue.
       unique_ptr(const unique_ptr&) = delete;
       unique_ptr& operator=(const unique_ptr&) = delete;
+
+    private:
+#ifdef __glibcxx_out_ptr
+      template<typename, typename, typename...>
+	friend class out_ptr_t;
+      template<typename, typename, typename...>
+	friend class inout_ptr_t;
+#endif
   };
 
   // 20.7.1.3 unique_ptr for array objects with a runtime length
@@ -789,6 +797,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
       // Disable copy from lvalue.
       unique_ptr(const unique_ptr&) = delete;
       unique_ptr& operator=(const unique_ptr&) = delete;
+
+    private:
+#ifdef __glibcxx_out_ptr
+      template<typename, typename, typename...> friend class out_ptr_t;
+      template<typename, typename, typename...> friend class inout_ptr_t;
+#endif
     };
 
   /// @{
@@ -1140,6 +1154,13 @@ namespace __detail
     }
 #endif // C++20 && HOSTED
 
+#if __cpp_variable_templates
+  template<typename _Tp>
+    static constexpr bool __is_unique_ptr = false;
+  template<typename _Tp, typename _Del>
+    static constexpr bool __is_unique_ptr<unique_ptr<_Tp, _Del>> = true;
+#endif
+
   /// @} group pointer_abstractions
 
 #if __cplusplus >= 201703L
diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def
index 2aab9426ecc..60400b3b8d2 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -1568,6 +1568,14 @@ ftms = {
   };
 };
 
+ftms = {
+  name = out_ptr;
+  values = {
+    v = 202106;
+    cxxmin = 23;
+  };
+};
+
 ftms = {
   name = spanstream;
   values = {
diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h
index 6829c70cf48..2e2af3e0c41 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -1923,6 +1923,17 @@
 #undef __glibcxx_want_move_only_function
 
 // from version.def line 1572
+#if !defined(__cpp_lib_out_ptr)
+# if (__cplusplus >= 202100L)
+#  define __glibcxx_out_ptr 202106L
+#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_out_ptr)
+#   define __cpp_lib_out_ptr 202106L
+#  endif
+# endif
+#endif /* !defined(__cpp_lib_out_ptr) && defined(__glibcxx_want_out_ptr) */
+#undef __glibcxx_want_out_ptr
+
+// from version.def line 1580
 #if !defined(__cpp_lib_spanstream)
 # if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED && (__glibcxx_span)
 #  define __glibcxx_spanstream 202106L
@@ -1933,7 +1944,7 @@
 #endif /* !defined(__cpp_lib_spanstream) && defined(__glibcxx_want_spanstream) */
 #undef __glibcxx_want_spanstream
 
-// from version.def line 1582
+// from version.def line 1590
 #if !defined(__cpp_lib_stacktrace)
 # if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED && (_GLIBCXX_HAVE_STACKTRACE)
 #  define __glibcxx_stacktrace 202011L
@@ -1944,7 +1955,7 @@
 #endif /* !defined(__cpp_lib_stacktrace) && defined(__glibcxx_want_stacktrace) */
 #undef __glibcxx_want_stacktrace
 
-// from version.def line 1592
+// from version.def line 1600
 #if !defined(__cpp_lib_string_contains)
 # if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED
 #  define __glibcxx_string_contains 202011L
@@ -1955,7 +1966,7 @@
 #endif /* !defined(__cpp_lib_string_contains) && defined(__glibcxx_want_string_contains) */
 #undef __glibcxx_want_string_contains
 
-// from version.def line 1601
+// from version.def line 1609
 #if !defined(__cpp_lib_string_resize_and_overwrite)
 # if (__cplusplus >= 202100L) && _GLIBCXX_HOSTED
 #  define __glibcxx_string_resize_and_overwrite 202110L
@@ -1966,7 +1977,7 @@
 #endif /* !defined(__cpp_lib_string_resize_and_overwrite) && defined(__glibcxx_want_string_resize_and_overwrite) */
 #undef __glibcxx_want_string_resize_and_overwrite
 
-// from version.def line 1610
+// from version.def line 1618
 #if !defined(__cpp_lib_fstream_native_handle)
 # if (__cplusplus >  202302L) && _GLIBCXX_HOSTED
 #  define __glibcxx_fstream_native_handle 202306L
@@ -1977,7 +1988,7 @@
 #endif /* !defined(__cpp_lib_fstream_native_handle) && defined(__glibcxx_want_fstream_native_handle) */
 #undef __glibcxx_want_fstream_native_handle
 
-// from version.def line 1619
+// from version.def line 1627
 #if !defined(__cpp_lib_ratio)
 # if (__cplusplus >  202302L)
 #  define __glibcxx_ratio 202306L
@@ -1988,7 +1999,7 @@
 #endif /* !defined(__cpp_lib_ratio) && defined(__glibcxx_want_ratio) */
 #undef __glibcxx_want_ratio
 
-// from version.def line 1627
+// from version.def line 1635
 #if !defined(__cpp_lib_to_string)
 # if (__cplusplus >  202302L) && _GLIBCXX_HOSTED && (__glibcxx_to_chars)
 #  define __glibcxx_to_string 202306L
diff --git a/libstdc++-v3/include/std/memory b/libstdc++-v3/include/std/memory
index ac83761a26c..a526173b8cc 100644
--- a/libstdc++-v3/include/std/memory
+++ b/libstdc++-v3/include/std/memory
@@ -91,6 +91,10 @@
 #  include <bits/uses_allocator_args.h>
 #endif
 
+#if __cplusplus > 202002L
+#  include <bits/out_ptr.h>
+#endif
+
 #define __glibcxx_want_allocator_traits_is_always_equal
 #define __glibcxx_want_assume_aligned
 #define __glibcxx_want_atomic_shared_ptr
@@ -99,6 +103,7 @@
 #define __glibcxx_want_constexpr_memory
 #define __glibcxx_want_enable_shared_from_this
 #define __glibcxx_want_make_unique
+#define __glibcxx_want_out_ptr
 #define __glibcxx_want_parallel_algorithm
 #define __glibcxx_want_ranges
 #define __glibcxx_want_raw_memory_algorithms
diff --git a/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/1.cc b/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/1.cc
new file mode 100644
index 00000000000..c42e5e13b75
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/1.cc
@@ -0,0 +1,47 @@
+// { dg-do run { target c++23 } }
+
+#include <memory>
+#include <testsuite_hooks.h>
+
+// C++23 [inout.ptr.t] Class template inout_ptr_t
+
+struct star_fish* star_fish_alloc();
+int star_fish_populate(struct star_fish** ps, const char* description);
+
+struct star_fish_deleter {
+  void operator() (struct star_fish* c) const noexcept;
+};
+
+using star_fish_ptr = std::unique_ptr<star_fish, star_fish_deleter>;
+
+// Example 1 from [inout.ptr.t]
+int main(int, char**)
+{
+  star_fish_ptr peach(star_fish_alloc());
+  // ...
+  // used, need to re-make
+  int err = star_fish_populate(std::inout_ptr(peach), "caring clown-fish liker");
+  return 0;
+}
+
+#include <cstdint>
+
+star_fish* star_fish_alloc()
+{
+  static std::uintptr_t counter = 1;
+  return reinterpret_cast<star_fish*>(counter++);
+}
+
+void star_fish_deleter::operator()(star_fish* c) const noexcept
+{
+  static std::uintptr_t counter = 1;
+  VERIFY(reinterpret_cast<std::uintptr_t>(c) == counter++);
+}
+
+int star_fish_populate(star_fish** ps, const char*)
+{
+  VERIFY(ps);
+  star_fish_deleter()(*ps);
+  *ps = star_fish_alloc();
+  return 0;
+}
diff --git a/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/2.cc b/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/2.cc
new file mode 100644
index 00000000000..ca6076209c2
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/2.cc
@@ -0,0 +1,102 @@
+// { dg-do run { target c++23 } }
+
+#include <memory>
+#include <testsuite_hooks.h>
+
+// C++23 [inout.ptr.t] Class template inout_ptr_t
+
+int counter = 0;
+
+void
+test_unique_ptr()
+{
+  int* ip = new int(1);
+  std::unique_ptr<int> up(ip);
+  {
+    auto iop = std::inout_ptr(up);
+    int** ipp = iop;
+    VERIFY( *ipp == ip );
+    delete *ipp;
+    *ipp = new int(2);
+  }
+  VERIFY( *up == 2 );
+
+  ip = up.get();
+  {
+    std::default_delete<int> d;
+    auto iop = std::inout_ptr(up, d);
+    int** ipp = iop;
+    VERIFY( *ipp == ip );
+    delete *ipp;
+    *ipp = new int(3);
+  }
+  VERIFY( *up == 3 );
+
+  struct D
+  {
+    explicit D(int id) : id(id) { }
+    void operator()(long* p) const { ++counter; delete p; }
+    int id;
+  };
+  counter = 0;
+  std::unique_ptr<long, D> upd(new long(3), D(11));
+  {
+    auto iop = std::inout_ptr(upd);
+    VERIFY( counter == 0 );
+    long** lpp = iop;
+    VERIFY( **lpp == 3 );
+    delete *lpp;
+    *lpp = new long(4);
+  }
+  VERIFY( *upd == 4 );
+  VERIFY( upd.get_deleter().id == 11 );
+  VERIFY( counter == 0 );
+
+  {
+    D d(22);
+    auto iop = std::inout_ptr(upd, d);
+    VERIFY( counter == 0 );
+    long** lpp = iop;
+    VERIFY( **lpp == 4 );
+    delete *lpp;
+    *lpp = nullptr;
+  }
+  VERIFY( upd == nullptr );
+  VERIFY( upd.get_deleter().id == 11 ); // Deleter not replaced if p is null.
+  VERIFY( counter == 0 );
+
+  upd.reset(new long(5));
+  {
+    D d(33);
+    auto iop = std::inout_ptr(upd, d);
+    VERIFY( counter == 0 );
+    long** lpp = iop;
+    VERIFY( **lpp == 5 );
+    delete *lpp;
+    *lpp = new long(6);
+  }
+  VERIFY( *upd == 6 );
+  VERIFY( upd.get_deleter().id == 33 );
+  VERIFY( counter == 0 );
+
+  struct Base { };
+  struct Derived : Base
+  {
+    Derived(int id) : id(id) { }
+    int id;
+  };
+  std::unique_ptr<Derived> upbd(new Derived(1));
+  {
+    auto iop = std::inout_ptr<Base*>(upbd);
+    Base** bpp = iop;
+    VERIFY( static_cast<Derived*>(*bpp)->id == 1 );
+    delete (Derived*)*bpp;
+    *bpp = new Derived(2);
+  }
+  VERIFY( upbd->id == 2 );
+}
+
+int main()
+{
+  test_unique_ptr();
+}
diff --git a/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/shared_ptr_neg.cc b/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/shared_ptr_neg.cc
new file mode 100644
index 00000000000..b4ecf936105
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/shared_ptr_neg.cc
@@ -0,0 +1,11 @@
+// { dg-do compile { target c++23 } }
+
+#include <memory>
+
+std::shared_ptr<int> sp;
+auto iop = std::inout_ptr(sp); // { dg-error "here" }
+std::shared_ptr<long> sp2;
+std::default_delete<int> d;
+auto iop2 = std::inout_ptr(sp2, d); // { dg-error "here" }
+
+// { dg-error "can not be used" "" { target *-*-* } 0 }
diff --git a/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/void_ptr.cc b/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/void_ptr.cc
new file mode 100644
index 00000000000..4fce3df6b30
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/smartptr.adapt/inout_ptr/void_ptr.cc
@@ -0,0 +1,35 @@
+// { dg-do compile { target c++23 } }
+
+#include <memory>
+#include <testsuite_hooks.h>
+
+std::unique_ptr<int> intptr;
+
+void
+val_inout(void** p)
+{
+  // The smart pointer should have been released by the inout_ptr_t constructor
+  VERIFY( intptr == nullptr );
+  // The initial value of the pointer should be non-null.
+  VERIFY( *(int*)*p == 5678 );
+  // Although the implementation is allowed to access the unique_ptr's
+  // pointer directly, it can't do that here because the unique_ptr stores
+  // an int* and we are reading from and writing to a void*.
+  VERIFY( intptr.get() != *p );
+
+  // Return a heap-allocated int in *p.
+  *p = (void*) new int(999);
+}
+
+void
+test_inout_ptr_void()
+{
+  intptr.reset(new int(5678));
+  val_inout(std::inout_ptr<void*>(intptr));
+  VERIFY( *intptr == 999 );
+}
+
+int main()
+{
+  test_inout_ptr_void();
+}
diff --git a/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/1.cc b/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/1.cc
new file mode 100644
index 00000000000..ed542a04fc9
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/1.cc
@@ -0,0 +1,116 @@
+// { dg-do run { target c++23 } }
+// { dg-require-fileio "" }
+// { dg-additional-files "thirty_years_among_the_dead_preproc.txt" }
+
+#include <memory>
+#include <cstdio>
+#include <testsuite_hooks.h>
+
+// C++23 [out.ptr.t] Class template out_ptr_t
+
+int fopen_s(std::FILE** f, const char* name, const char* mode);
+
+struct fclose_deleter {
+  void operator()(std::FILE* f) const noexcept {
+    std::fclose(f);
+  }
+};
+
+// Example 1 from [out.ptr.t]
+void
+test_example_1()
+{
+  constexpr const char* file_name = "thirty_years_among_the_dead_preproc.txt";
+  std::unique_ptr<std::FILE, fclose_deleter> file_ptr;
+  int err = fopen_s(std::out_ptr<std::FILE*>(file_ptr), file_name, "r+b");
+  if (err != 0)
+    VERIFY(false);
+  // *file_ptr is valid
+  VERIFY(file_ptr != nullptr);
+}
+
+// Same again without explicit template argument list.
+void
+test_example_1_2()
+{
+  constexpr const char* file_name = "thirty_years_among_the_dead_preproc.txt";
+  std::unique_ptr<std::FILE, fclose_deleter> file_ptr;
+  int err = fopen_s(std::out_ptr(file_ptr), file_name, "r+b");
+  if (err != 0)
+    VERIFY(false);
+  // *file_ptr is valid
+  VERIFY(file_ptr != nullptr);
+}
+
+// And again with a deleter argument.
+void
+test_example_1_3()
+{
+  constexpr const char* file_name = "thirty_years_among_the_dead_preproc.txt";
+  std::unique_ptr<std::FILE, fclose_deleter> file_ptr;
+  int err = fopen_s(std::out_ptr(file_ptr, fclose_deleter()), file_name, "r+b");
+  if (err != 0)
+    VERIFY(false);
+  // *file_ptr is valid
+  VERIFY(file_ptr != nullptr);
+}
+
+// Same again using std::shared_ptr
+void
+test_example_1_sp()
+{
+  constexpr const char* file_name = "thirty_years_among_the_dead_preproc.txt";
+  std::shared_ptr<std::FILE> file_ptr;
+  int err = fopen_s(std::out_ptr<std::FILE*>(file_ptr, fclose_deleter()),
+		    file_name, "r+b");
+  if (err != 0)
+    VERIFY(false);
+  // *file_ptr is valid
+  VERIFY(file_ptr != nullptr);
+}
+
+// And again without explicit template argument list.
+void
+test_example_1_sp_2()
+{
+  constexpr const char* file_name = "thirty_years_among_the_dead_preproc.txt";
+  std::shared_ptr<std::FILE> file_ptr;
+  int err = fopen_s(std::out_ptr(file_ptr, fclose_deleter()), file_name, "r+b");
+  if (err != 0)
+    VERIFY(false);
+  // *file_ptr is valid
+  VERIFY(file_ptr != nullptr);
+}
+
+// And again using a raw pointer.
+void
+test_example_1_raw()
+{
+  constexpr const char* file_name = "thirty_years_among_the_dead_preproc.txt";
+  std::FILE* file_ptr;
+  int err = fopen_s(std::out_ptr(file_ptr), file_name, "r+b");
+  if (err != 0)
+    VERIFY(false);
+  // *file_ptr is valid
+  VERIFY(file_ptr != nullptr);
+  std::fclose(file_ptr);
+}
+
+int main()
+{
+  test_example_1();
+  test_example_1_2();
+  test_example_1_3();
+  test_example_1_sp();
+  test_example_1_sp_2();
+  test_example_1_raw();
+}
+
+#include <cerrno>
+
+int fopen_s(std::FILE** f, const char* name, const char* mode)
+{
+  if ((*f = std::fopen(name, mode)))
+    return 0;
+  return errno;
+}
diff --git a/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/2.cc b/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/2.cc
new file mode 100644
index 00000000000..77f952238eb
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/2.cc
@@ -0,0 +1,273 @@
+// { dg-do compile { target c++23 } }
+
+#include <memory>
+#include <testsuite_hooks.h>
+
+int counter = 0;
+
+void
+test_unique_ptr()
+{
+  std::unique_ptr<int> up(new int(1));
+  {
+    auto op = std::out_ptr(up);
+    VERIFY( up == nullptr );
+    int** p = op;
+    VERIFY( *p == nullptr );
+    *p = new int(2);
+
+    const auto& cop = op;
+    VERIFY( static_cast<int**>(cop) == static_cast<int**>(op) );
+  }
+  VERIFY( *up == 2 );
+
+  {
+    std::default_delete<int> d;
+    auto op = std::out_ptr(up, d);
+    VERIFY( up == nullptr );
+    int** p = op;
+    VERIFY( *p == nullptr );
+    *p = new int(3);
+
+    const auto& cop = op;
+    VERIFY( static_cast<int**>(cop) == static_cast<int**>(op) );
+  }
+  VERIFY( *up == 3 );
+
+  struct D
+  {
+    explicit D(int id) : id(id) { }
+    void operator()(long* p) const { ++counter; delete p; }
+    int id;
+  };
+  counter = 0;
+  std::unique_ptr<long, D> upd(new long(1), D(11));
+  {
+    auto op = std::out_ptr(upd);
+    VERIFY( counter == 1 );
+    VERIFY( upd == nullptr );
+    long** p = op;
+    VERIFY( *p == nullptr );
+    *p = new long(4);
+
+    const auto& cop = op;
+    VERIFY( static_cast<long**>(cop) == static_cast<long**>(op) );
+  }
+  VERIFY( *upd == 4 );
+  VERIFY( upd.get_deleter().id == 11 );
+  VERIFY( counter == 1 );
+
+  {
+    D d(33);
+    auto op = std::out_ptr(upd, d);
+    VERIFY( counter == 2 );
+    VERIFY( upd == nullptr );
+    long** p = op;
+    VERIFY( *p == nullptr );
+
+    const auto& cop = op;
+    VERIFY( static_cast<long**>(cop) == static_cast<long**>(op) );
+  }
+  VERIFY( upd == nullptr );
+  VERIFY( upd.get_deleter().id == 11 ); // Deleter not replaced if p is null.
+  VERIFY( counter == 2 );
+
+  {
+    D d(33);
+    auto op = std::out_ptr(upd, d);
+    VERIFY( counter == 2 );
+    VERIFY( upd == nullptr );
+    long** p = op;
+    VERIFY( *p == nullptr );
+    *p = new long(5);
+  }
+  VERIFY( *upd == 5 );
+  VERIFY( upd.get_deleter().id == 33 );
+  VERIFY( counter == 2 );
+
+  struct Base { };
+  struct Derived : Base
+  {
+    Derived(int id) : id(id) { }
+    int id;
+  };
+  std::unique_ptr<Derived> upbd(new Derived(1));
+  {
+    auto op = std::out_ptr<Base*>(upbd);
+    Base** bpp = op;
+    VERIFY( *bpp == nullptr );
+    *bpp = new Derived(2);
+
+    const auto& cop = op;
+    VERIFY( static_cast<Base**>(cop) == static_cast<Base**>(op) );
+  }
+  VERIFY( upbd->id == 2 );
+}
+
+void deleter_function(float* p) { delete p; }
+
+void
+test_shared_ptr()
+{
+  using DD = std::default_delete<int>;
+
+  std::shared_ptr<int> sp(new int(1));
+  {
+    auto op = std::out_ptr(sp, DD{});
+    VERIFY( sp == nullptr );
+    int** p = op;
+    VERIFY( *p == nullptr );
+    *p = new int(2);
+
+    const auto& cop = op;
+    VERIFY( static_cast<int**>(cop) == static_cast<int**>(op) );
+  }
+  VERIFY( *sp == 2 );
+
+  {
+    auto op = std::out_ptr(sp, DD{});
+    VERIFY( sp == nullptr );
+    int** p = op;
+    VERIFY( *p == nullptr );
+    *p = new int(3);
+  }
+  VERIFY( *sp == 3 );
+
+  struct D
+  {
+    explicit D(int id) : id(id) { }
+    void operator()(long* p) const { ++counter; delete p; }
+    int id;
+  };
+  counter = 0;
+  std::shared_ptr<long> spd(new long(1), D(11));
+  {
+    auto op = std::out_ptr(spd, D(22));
+    VERIFY( counter == 1 );
+    VERIFY( spd == nullptr );
+    long** p = op;
+    VERIFY( *p == nullptr );
+    *p = new long(5);
+  }
+  VERIFY( counter == 1 );
+  VERIFY( *spd == 5 );
+  VERIFY( std::get_deleter<D>(spd)->id == 22 );
+
+  struct Base { };
+  struct Derived : Base
+  {
+    Derived(int id) : id(id) { }
+    int id;
+  };
+  std::shared_ptr<Derived> spbd(new Derived(1));
+  {
+    auto op = std::out_ptr<Base*>(spbd, std::default_delete<Derived>());
+    Base** bpp = op;
+    VERIFY( *bpp == nullptr );
+    *bpp = new Derived(2);
+  }
+  VERIFY( spbd->id == 2 );
+
+  std::shared_ptr<float> spf;
+  {
+    auto op = std::out_ptr(spf, deleter_function);
+    float** fpp = op;
+    *fpp = new float(0.5);
+  }
+  VERIFY( std::get_deleter<void(*)(float*)>(spf) != nullptr );
+  VERIFY( *std::get_deleter<void(*)(float*)>(spf) == &deleter_function );
+  VERIFY( *spf == 0.5 );
+}
+
+void
+test_custom_ptr()
+{
+  struct UPtr : std::unique_ptr<int>
+  {
+    using std::unique_ptr<int>::unique_ptr;
+  };
+
+  UPtr up(new int(1));
+  {
+    auto op = std::out_ptr(up);
+    VERIFY( up == nullptr );
+    int** p = op;
+    VERIFY( *p == nullptr );
+    *p = new int(2);
+
+    const auto& cop = op;
+    VERIFY( static_cast<int**>(cop) == static_cast<int**>(op) );
+  }
+  VERIFY( *up == 2 );
+
+  {
+    auto op = std::out_ptr(up, std::default_delete<int>{});
+    VERIFY( up == nullptr );
+    int** p = op;
+    VERIFY( *p == nullptr );
+    *p = new int(3);
+  }
+  VERIFY( *up == 3 );
+
+  struct D
+  {
+    explicit D(int id) : id(id) { }
+    void operator()(long* p) const { ++counter; delete p; }
+    int id;
+  };
+  counter = 0;
+  struct UDPtr : std::unique_ptr<long, D>
+  {
+    using std::unique_ptr<long, D>::unique_ptr;
+  };
+  UDPtr upd(new long(1), D(11));
+  {
+    auto op = std::out_ptr(upd);
+    VERIFY( counter == 1 );
+    VERIFY( upd == nullptr );
+    long** p = op;
+    VERIFY( *p == nullptr );
+    *p = new long(4);
+  }
+  VERIFY( counter == 1 );
+  VERIFY( *upd == 4 );
+  VERIFY( upd.get_deleter().id == 11 );
+
+  {
+    auto op = std::out_ptr(upd, D(22));
+    VERIFY( upd == nullptr );
+    VERIFY( counter == 2 );
+    long** p = op;
+    VERIFY( *p == nullptr );
+    *p = new long(5);
+  }
+  VERIFY( counter == 2 );
+  VERIFY( *upd == 5 );
+  VERIFY( upd.get_deleter().id == 22 );
+}
+
+void
+test_raw_ptr()
+{
+  long l = 5, l2 = 6;
+  long* lp = &l;
+  {
+    auto op = std::out_ptr(lp);
+    VERIFY( lp == nullptr );
+    long** p = op;
+    VERIFY( *p == nullptr );
+    *p = &l2;
+
+    const auto& cop = op;
+    VERIFY( static_cast<long**>(cop) == static_cast<long**>(op) );
+  }
+  VERIFY( *lp == 6 );
+}
+
+int main()
+{
+  test_unique_ptr();
+  test_shared_ptr();
+  test_custom_ptr();
+  test_raw_ptr();
+}
diff --git a/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/shared_ptr_neg.cc b/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/shared_ptr_neg.cc
new file mode 100644
index 00000000000..b2532dc7f72
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/shared_ptr_neg.cc
@@ -0,0 +1,7 @@
+// { dg-do compile { target c++23 } }
+
+#include <memory>
+
+std::shared_ptr<int> sp;
+auto op = std::out_ptr(sp); // { dg-error "here" }
+// { dg-error "deleter must be used" "" { target *-*-* } 0 }
diff --git a/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/void_ptr.cc b/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/void_ptr.cc
new file mode 100644
index 00000000000..3ff84a3aeaf
--- /dev/null
+++ b/libstdc++-v3/testsuite/20_util/smartptr.adapt/out_ptr/void_ptr.cc
@@ -0,0 +1,60 @@
+// { dg-do compile { target c++23 } }
+
+#include <memory>
+#include <testsuite_hooks.h>
+
+std::unique_ptr<int> intptr;
+
+void
+val_out(void** p)
+{
+  // The smart pointer should be reset by the out_ptr_t constructor.
+  VERIFY( intptr == nullptr );
+  // The initial value of the pointer should be null.
+  VERIFY( *p == nullptr );
+  // Although the implementation is allowed to access the unique_ptr's
+  // pointer directly, it can't do that here because the unique_ptr stores
+  // an int* and we are writing to a void*.
+  VERIFY( intptr.get() != *p );
+
+  // Return a heap-allocated int in *p.
+  *p = (void*) new int(1234);
+}
+
+void
+test_out_ptr_void()
+{
+  intptr.reset(new int);
+  val_out(std::out_ptr<void*>(intptr));
+  VERIFY( *intptr == 1234 );
+}
+
+void
+val_inout(void** p)
+{
+  // The smart pointer should have been released by the inout_ptr_t constructor
+  VERIFY( intptr == nullptr );
+  // The initial value of the pointer should be non-null.
+  VERIFY( *(int*)*p == 5678 );
+  // Although the implementation is allowed to access the unique_ptr's
+  // pointer directly, it can't do that here because the unique_ptr stores
+  // an int* and we are reading from and writing to a void*.
+  VERIFY( intptr.get() != *p );
+
+  // Return a heap-allocated int in *p.
+  *p = (void*) new int(999);
+}
+
+void
+test_inout_ptr_void()
+{
+  intptr.reset(new int(5678));
+  val_inout(std::inout_ptr<void*>(intptr));
+  VERIFY( *intptr == 999 );
+}
+
+int main()
+{
+  test_out_ptr_void();
+  test_inout_ptr_void();
+}
