[libcxx-commits] [libcxx] e475bb7 - [libc++][memory] P1132R8: `out_ptr` - a scalable output pointer abstraction (#73618)

via libcxx-commits libcxx-commits at lists.llvm.org
Thu Jul 18 20:38:06 PDT 2024


Author: Hristo Hristov
Date: 2024-07-19T06:38:02+03:00
New Revision: e475bb7ac33d7bc0446fe86858f5c3073cd48c97

URL: https://github.com/llvm/llvm-project/commit/e475bb7ac33d7bc0446fe86858f5c3073cd48c97
DIFF: https://github.com/llvm/llvm-project/commit/e475bb7ac33d7bc0446fe86858f5c3073cd48c97.diff

LOG: [libc++][memory] P1132R8: `out_ptr` - a scalable output pointer abstraction (#73618)

Differential Revision: https://reviews.llvm.org/D150525

Implements:
- https://wg21.link/P1132R8 - `out_ptr` - a scalable output pointer
abstraction
- https://eel.is/c++draft/smartptr.adapt - 20.3.4 Smart pointer adaptors
- https://wg21.link/LWG3734 - Inconsistency in `inout_ptr` and `out_ptr`
for empty case
- https://wg21.link/LWG3897- `inout_ptr` will not update raw pointer to
0

---------

Co-authored-by: Hristo Hristov <zingam at outlook.com>

Added: 
    libcxx/include/__memory/inout_ptr.h
    libcxx/include/__memory/out_ptr.h
    libcxx/test/std/utilities/smartptr/adapt/inout_ptr/inout_ptr.general.pass.cpp
    libcxx/test/std/utilities/smartptr/adapt/inout_ptr/inout_ptr.verify.cpp
    libcxx/test/std/utilities/smartptr/adapt/inout_ptr/inout_ptr_t.convert.pass.cpp
    libcxx/test/std/utilities/smartptr/adapt/inout_ptr/inout_ptr_t.ctor.pass.cpp
    libcxx/test/std/utilities/smartptr/adapt/inout_ptr/inout_ptr_t.verify.cpp
    libcxx/test/std/utilities/smartptr/adapt/out_ptr/out_ptr.general.pass.cpp
    libcxx/test/std/utilities/smartptr/adapt/out_ptr/out_ptr.verify.cpp
    libcxx/test/std/utilities/smartptr/adapt/out_ptr/out_ptr_t.convert.pass.cpp
    libcxx/test/std/utilities/smartptr/adapt/out_ptr/out_ptr_t.ctor.pass.cpp
    libcxx/test/std/utilities/smartptr/adapt/out_ptr/out_ptr_t.verify.cpp
    libcxx/test/std/utilities/smartptr/adapt/types.h

Modified: 
    libcxx/docs/FeatureTestMacroTable.rst
    libcxx/docs/ReleaseNotes/19.rst
    libcxx/docs/Status/Cxx23Issues.csv
    libcxx/docs/Status/Cxx23Papers.csv
    libcxx/docs/Status/Cxx2cIssues.csv
    libcxx/include/CMakeLists.txt
    libcxx/include/__memory/allocator_traits.h
    libcxx/include/__memory/pointer_traits.h
    libcxx/include/memory
    libcxx/include/module.modulemap
    libcxx/include/version
    libcxx/modules/std/memory.inc
    libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp
    libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
    libcxx/utils/generate_feature_test_macro_components.py

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 53cfc3739d2be..b548322b1b2b8 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -346,7 +346,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_optional``                                     ``202110L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_out_ptr``                                      *unimplemented*
+    ``__cpp_lib_out_ptr``                                      ``202106L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_print``                                        ``202207L``
     ---------------------------------------------------------- -----------------
@@ -450,7 +450,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_optional_range_support``                       *unimplemented*
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_out_ptr``                                      *unimplemented*
+    ``__cpp_lib_out_ptr``                                      ``202311L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_philox_engine``                                *unimplemented*
     ---------------------------------------------------------- -----------------

diff  --git a/libcxx/docs/ReleaseNotes/19.rst b/libcxx/docs/ReleaseNotes/19.rst
index ccafa1a1456e0..784934eafe9f7 100644
--- a/libcxx/docs/ReleaseNotes/19.rst
+++ b/libcxx/docs/ReleaseNotes/19.rst
@@ -38,6 +38,7 @@ What's New in Libc++ 19.0.0?
 Implemented Papers
 ------------------
 
+- P1132R8 - ``out_ptr`` - a scalable output pointer abstraction
 - P2637R3 - Member ``visit``
 - P2652R2 - Disallow User Specialization of ``allocator_traits``
 - P2819R2 - Add ``tuple`` protocol to ``complex``

diff  --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index cc601b3cd3c96..547d38c25c1da 100644
--- a/libcxx/docs/Status/Cxx23Issues.csv
+++ b/libcxx/docs/Status/Cxx23Issues.csv
@@ -192,7 +192,7 @@
 "`3515 <https://wg21.link/LWG3515>`__","§[stacktrace.basic.nonmem]: ``operator<<`` should be less templatized", "November 2022","","",""
 "`3545 <https://wg21.link/LWG3545>`__","``std::pointer_traits`` should be SFINAE-friendly", "November 2022","|Complete|","18.0",""
 "`3569 <https://wg21.link/LWG3569>`__","``join_view`` fails to support ranges of ranges with non-default_initializable iterators", "November 2022","","","|ranges|"
-"`3594 <https://wg21.link/LWG3594>`__","``inout_ptr`` — inconsistent ``release()`` in destructor", "November 2022","","",""
+"`3594 <https://wg21.link/LWG3594>`__","``inout_ptr`` — inconsistent ``release()`` in destructor", "November 2022","|Complete|","19.0",""
 "`3597 <https://wg21.link/LWG3597>`__","Unsigned integer types don't model advanceable", "November 2022","","","|ranges|"
 "`3600 <https://wg21.link/LWG3600>`__","Making ``istream_iterator`` copy constructor trivial is an ABI break", "November 2022","","",""
 "`3629 <https://wg21.link/LWG3629>`__","``make_error_code`` and ``make_error_condition`` are customization points","November 2022","|Complete|","16.0",""
@@ -282,7 +282,7 @@
 "`3645 <https://wg21.link/LWG3645>`__","``resize_and_overwrite`` is overspecified to call its callback with lvalues","February 2023","|Complete|","14.0",""
 "`3655 <https://wg21.link/LWG3655>`__","The ``INVOKE`` operation and union types","February 2023","|Complete|","18.0",""
 "`3723 <https://wg21.link/LWG3723>`__","``priority_queue::push_range`` needs to ``append_range``","February 2023","","","|ranges|"
-"`3734 <https://wg21.link/LWG3734>`__","Inconsistency in ``inout_ptr`` and ``out_ptr`` for empty case","February 2023","","",""
+"`3734 <https://wg21.link/LWG3734>`__","Inconsistency in ``inout_ptr`` and ``out_ptr`` for empty case","February 2023","|Complete|","19.0",""
 "`3772 <https://wg21.link/LWG3772>`__","``repeat_view``'s ``piecewise`` constructor is missing Postconditions","February 2023","|Complete|","17.0","|ranges|"
 "`3786 <https://wg21.link/LWG3786>`__","Flat maps' deduction guide needs to default ``Allocator`` to be useful","February 2023","","",""
 "`3803 <https://wg21.link/LWG3803>`__","``flat_foo`` constructors taking ``KeyContainer`` lack ``KeyCompare`` parameter","February 2023","","",""

diff  --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 4f589cd938d7c..6dd6bdad07658 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -13,7 +13,7 @@
 "","","","","","",""
 "`P0401R6 <https://wg21.link/P0401R6>`__","LWG","Providing size feedback in the Allocator interface","June 2021","|Complete|","15.0"
 "`P0448R4 <https://wg21.link/P0448R4>`__","LWG","A strstream replacement using span<charT> as buffer","June 2021","",""
-"`P1132R8 <https://wg21.link/P1132R8>`__","LWG","out_ptr - a scalable output pointer abstraction","June 2021","",""
+"`P1132R8 <https://wg21.link/P1132R8>`__","LWG","out_ptr - a scalable output pointer abstraction","June 2021","|Complete|","19.0"
 "`P1328R1 <https://wg21.link/P1328R1>`__","LWG","Making std::type_info::operator== constexpr","June 2021","|Complete|","17.0"
 "`P1425R4 <https://wg21.link/P1425R4>`__","LWG","Iterators pair constructors for stack and queue","June 2021","|Complete|","14.0","|ranges|"
 "`P1518R2 <https://wg21.link/P1518R2>`__","LWG","Stop overconstraining allocators in container deduction guides","June 2021","|Complete|","13.0"

diff  --git a/libcxx/docs/Status/Cxx2cIssues.csv b/libcxx/docs/Status/Cxx2cIssues.csv
index b5732ee981ffb..dec9af1c12656 100644
--- a/libcxx/docs/Status/Cxx2cIssues.csv
+++ b/libcxx/docs/Status/Cxx2cIssues.csv
@@ -24,7 +24,7 @@
 "`3749 <https://wg21.link/LWG3749>`__","``common_iterator`` should handle integer-class 
diff erence types","Kona November 2023","","",""
 "`3809 <https://wg21.link/LWG3809>`__","Is ``std::subtract_with_carry_engine<uint16_t>`` supposed to work","Kona November 2023","","",""
 "`3892 <https://wg21.link/LWG3892>`__","Incorrect formatting of nested ranges and tuples","Kona November 2023","|Complete|","17.0","|format|"
-"`3897 <https://wg21.link/LWG3897>`__","``inout_ptr`` will not update raw pointer to 0","Kona November 2023","","",""
+"`3897 <https://wg21.link/LWG3897>`__","``inout_ptr`` will not update raw pointer to 0","Kona November 2023","|Complete|","19.0",""
 "`3946 <https://wg21.link/LWG3946>`__","The definition of ``const_iterator_t`` should be reworked","Kona November 2023","","",""
 "`3947 <https://wg21.link/LWG3947>`__","Unexpected constraints on ``adjacent_transform_view::base()``","Kona November 2023","","","|ranges|"
 "`3948 <https://wg21.link/LWG3948>`__","``possibly-const-range and as-const-pointer`` should be ``noexcept``","Kona November 2023","","","|ranges|"

diff  --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 26bad4f656a07..4b036ec639507 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -533,6 +533,8 @@ set(files
   __memory/concepts.h
   __memory/construct_at.h
   __memory/destruct_n.h
+  __memory/inout_ptr.h
+  __memory/out_ptr.h
   __memory/pointer_traits.h
   __memory/ranges_construct_at.h
   __memory/ranges_uninitialized_algorithms.h

diff  --git a/libcxx/include/__memory/allocator_traits.h b/libcxx/include/__memory/allocator_traits.h
index ac564f0e6fa0c..c5fcc89327b8f 100644
--- a/libcxx/include/__memory/allocator_traits.h
+++ b/libcxx/include/__memory/allocator_traits.h
@@ -41,7 +41,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
   struct NAME<_Tp, __void_t<typename _Tp::PROPERTY > > : true_type {}
 
 // __pointer
-_LIBCPP_ALLOCATOR_TRAITS_HAS_XXX(__has_pointer, pointer);
 template <class _Tp,
           class _Alloc,
           class _RawAlloc = __libcpp_remove_reference_t<_Alloc>,

diff  --git a/libcxx/include/__memory/inout_ptr.h b/libcxx/include/__memory/inout_ptr.h
new file mode 100644
index 0000000000000..72e1a21ad6867
--- /dev/null
+++ b/libcxx/include/__memory/inout_ptr.h
@@ -0,0 +1,109 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___INOUT_PTR_H
+#define _LIBCPP___INOUT_PTR_H
+
+#include <__config>
+#include <__memory/addressof.h>
+#include <__memory/pointer_traits.h>
+#include <__memory/shared_ptr.h>
+#include <__memory/unique_ptr.h>
+#include <__type_traits/is_same.h>
+#include <__type_traits/is_specialization.h>
+#include <__type_traits/is_void.h>
+#include <__utility/forward.h>
+#include <__utility/move.h>
+#include <tuple>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+template <class _Smart, class _Pointer, class... _Args>
+class _LIBCPP_TEMPLATE_VIS inout_ptr_t {
+  static_assert(!__is_specialization_v<_Smart, shared_ptr>, "std::shared_ptr<> is not supported with std::inout_ptr.");
+
+public:
+  _LIBCPP_HIDE_FROM_ABI explicit inout_ptr_t(_Smart& __smart, _Args... __args)
+      : __s_(__smart), __a_(std::forward<_Args>(__args)...), __p_([&__smart] {
+          if constexpr (is_pointer_v<_Smart>) {
+            return __smart;
+          } else {
+            return __smart.get();
+          }
+        }()) {
+    if constexpr (requires { __s_.release(); }) {
+      __s_.release();
+    } else {
+      __s_ = _Smart();
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI inout_ptr_t(const inout_ptr_t&) = delete;
+
+  _LIBCPP_HIDE_FROM_ABI ~inout_ptr_t() {
+    // LWG-3897 inout_ptr will not update raw pointer to null
+    if constexpr (!is_pointer_v<_Smart>) {
+      if (!__p_) {
+        return;
+      }
+    }
+
+    using _SP = __pointer_of_or_t<_Smart, _Pointer>;
+    if constexpr (is_pointer_v<_Smart>) {
+      std::apply([&](auto&&... __args) { __s_ = _Smart(static_cast<_SP>(__p_), std::forward<_Args>(__args)...); },
+                 std::move(__a_));
+    } else if constexpr (__resettable_smart_pointer_with_args<_Smart, _Pointer, _Args...>) {
+      std::apply([&](auto&&... __args) { __s_.reset(static_cast<_SP>(__p_), std::forward<_Args>(__args)...); },
+                 std::move(__a_));
+    } else {
+      static_assert(is_constructible_v<_Smart, _SP, _Args...>,
+                    "The smart pointer must be constructible from arguments of types _Smart, _Pointer, _Args...");
+      std::apply([&](auto&&... __args) { __s_ = _Smart(static_cast<_SP>(__p_), std::forward<_Args>(__args)...); },
+                 std::move(__a_));
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI operator _Pointer*() const noexcept { return std::addressof(const_cast<_Pointer&>(__p_)); }
+
+  _LIBCPP_HIDE_FROM_ABI operator void**() const noexcept
+    requires(!is_same_v<_Pointer, void*>)
+  {
+    static_assert(is_pointer_v<_Pointer>, "The conversion to void** requires _Pointer to be a raw pointer.");
+
+    return reinterpret_cast<void**>(static_cast<_Pointer*>(*this));
+  }
+
+private:
+  _Smart& __s_;
+  tuple<_Args...> __a_;
+  _Pointer __p_;
+};
+
+template <class _Pointer = void, class _Smart, class... _Args>
+_LIBCPP_HIDE_FROM_ABI auto inout_ptr(_Smart& __s, _Args&&... __args) {
+  using _Ptr = conditional_t<is_void_v<_Pointer>, __pointer_of_t<_Smart>, _Pointer>;
+  return std::inout_ptr_t<_Smart, _Ptr, _Args&&...>(__s, std::forward<_Args>(__args)...);
+}
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___INOUT_PTR_H

diff  --git a/libcxx/include/__memory/out_ptr.h b/libcxx/include/__memory/out_ptr.h
new file mode 100644
index 0000000000000..95aa2029c9231
--- /dev/null
+++ b/libcxx/include/__memory/out_ptr.h
@@ -0,0 +1,101 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___OUT_PTR_H
+#define _LIBCPP___OUT_PTR_H
+
+#include <__config>
+#include <__memory/addressof.h>
+#include <__memory/pointer_traits.h>
+#include <__memory/shared_ptr.h>
+#include <__memory/unique_ptr.h>
+#include <__type_traits/is_specialization.h>
+#include <__type_traits/is_void.h>
+#include <__utility/forward.h>
+#include <__utility/move.h>
+#include <tuple>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+template <class _Smart, class _Pointer, class... _Args>
+class _LIBCPP_TEMPLATE_VIS out_ptr_t {
+  static_assert(!__is_specialization_v<_Smart, shared_ptr> || sizeof...(_Args) > 0,
+                "Using std::shared_ptr<> without a deleter in std::out_ptr is not supported.");
+
+public:
+  _LIBCPP_HIDE_FROM_ABI explicit out_ptr_t(_Smart& __smart, _Args... __args)
+      : __s_(__smart), __a_(std::forward<_Args>(__args)...), __p_() {
+    using _Ptr = decltype(__smart);
+    if constexpr (__resettable_smart_pointer<_Ptr>) {
+      __s_.reset();
+    } else if constexpr (is_constructible_v<_Smart>) {
+      __s_ = _Smart();
+    } else {
+      static_assert(__resettable_smart_pointer<_Ptr> || is_constructible_v<_Smart>,
+                    "The adapted pointer type must have a reset() member function or be default constructible.");
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI out_ptr_t(const out_ptr_t&) = delete;
+
+  _LIBCPP_HIDE_FROM_ABI ~out_ptr_t() {
+    if (!__p_) {
+      return;
+    }
+
+    using _SP = __pointer_of_or_t<_Smart, _Pointer>;
+    if constexpr (__resettable_smart_pointer_with_args<_Smart, _Pointer, _Args...>) {
+      std::apply([&](auto&&... __args) { __s_.reset(static_cast<_SP>(__p_), std::forward<_Args>(__args)...); },
+                 std::move(__a_));
+    } else {
+      static_assert(is_constructible_v<_Smart, _SP, _Args...>,
+                    "The smart pointer must be constructible from arguments of types _Smart, _Pointer, _Args...");
+      std::apply([&](auto&&... __args) { __s_ = _Smart(static_cast<_SP>(__p_), std::forward<_Args>(__args)...); },
+                 std::move(__a_));
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI operator _Pointer*() const noexcept { return std::addressof(const_cast<_Pointer&>(__p_)); }
+
+  _LIBCPP_HIDE_FROM_ABI operator void**() const noexcept
+    requires(!is_same_v<_Pointer, void*>)
+  {
+    static_assert(is_pointer_v<_Pointer>, "The conversion to void** requires _Pointer to be a raw pointer.");
+
+    return reinterpret_cast<void**>(static_cast<_Pointer*>(*this));
+  }
+
+private:
+  _Smart& __s_;
+  tuple<_Args...> __a_;
+  _Pointer __p_ = _Pointer();
+};
+
+template <class _Pointer = void, class _Smart, class... _Args>
+_LIBCPP_HIDE_FROM_ABI auto out_ptr(_Smart& __s, _Args&&... __args) {
+  using _Ptr = conditional_t<is_void_v<_Pointer>, __pointer_of_t<_Smart>, _Pointer>;
+  return std::out_ptr_t<_Smart, _Ptr, _Args&&...>(__s, std::forward<_Args>(__args)...);
+}
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___OUT_PTR_H

diff  --git a/libcxx/include/__memory/pointer_traits.h b/libcxx/include/__memory/pointer_traits.h
index fcd2c3edd9f6a..0914aceb318b7 100644
--- a/libcxx/include/__memory/pointer_traits.h
+++ b/libcxx/include/__memory/pointer_traits.h
@@ -20,19 +20,28 @@
 #include <__type_traits/is_void.h>
 #include <__type_traits/void_t.h>
 #include <__utility/declval.h>
+#include <__utility/forward.h>
 #include <cstddef>
 
 #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
 #  pragma GCC system_header
 #endif
 
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
 _LIBCPP_BEGIN_NAMESPACE_STD
 
-template <class _Tp, class = void>
-struct __has_element_type : false_type {};
+// clang-format off
+#define _LIBCPP_CLASS_TRAITS_HAS_XXX(NAME, PROPERTY)                                                                   \
+  template <class _Tp, class = void>                                                                                   \
+  struct NAME : false_type {};                                                                                         \
+  template <class _Tp>                                                                                                 \
+  struct NAME<_Tp, __void_t<typename _Tp::PROPERTY> > : true_type {}
+// clang-format on
 
-template <class _Tp>
-struct __has_element_type<_Tp, __void_t<typename _Tp::element_type> > : true_type {};
+_LIBCPP_CLASS_TRAITS_HAS_XXX(__has_pointer, pointer);
+_LIBCPP_CLASS_TRAITS_HAS_XXX(__has_element_type, element_type);
 
 template <class _Ptr, bool = __has_element_type<_Ptr>::value>
 struct __pointer_traits_element_type {};
@@ -240,6 +249,59 @@ to_address(const _Pointer& __p) noexcept -> decltype(std::__to_address(__p)) {
 }
 #endif
 
+#if _LIBCPP_STD_VER >= 23
+
+template <class _Tp>
+struct __pointer_of {};
+
+template <class _Tp>
+  requires(__has_pointer<_Tp>::value)
+struct __pointer_of<_Tp> {
+  using type = typename _Tp::pointer;
+};
+
+template <class _Tp>
+  requires(!__has_pointer<_Tp>::value && __has_element_type<_Tp>::value)
+struct __pointer_of<_Tp> {
+  using type = typename _Tp::element_type*;
+};
+
+template <class _Tp>
+  requires(!__has_pointer<_Tp>::value && !__has_element_type<_Tp>::value &&
+           __has_element_type<pointer_traits<_Tp>>::value)
+struct __pointer_of<_Tp> {
+  using type = typename pointer_traits<_Tp>::element_type*;
+};
+
+template <typename _Tp>
+using __pointer_of_t = typename __pointer_of<_Tp>::type;
+
+template <class _Tp, class _Up>
+struct __pointer_of_or {
+  using type _LIBCPP_NODEBUG = _Up;
+};
+
+template <class _Tp, class _Up>
+  requires requires { typename __pointer_of_t<_Tp>; }
+struct __pointer_of_or<_Tp, _Up> {
+  using type _LIBCPP_NODEBUG = __pointer_of_t<_Tp>;
+};
+
+template <typename _Tp, typename _Up>
+using __pointer_of_or_t = typename __pointer_of_or<_Tp, _Up>::type;
+
+template <class _Smart>
+concept __resettable_smart_pointer = requires(_Smart __s) { __s.reset(); };
+
+template <class _Smart, class _Pointer, class... _Args>
+concept __resettable_smart_pointer_with_args = requires(_Smart __s, _Pointer __p, _Args... __args) {
+  __s.reset(static_cast<__pointer_of_or_t<_Smart, _Pointer>>(__p), std::forward<_Args>(__args)...);
+};
+
+#endif
+
 _LIBCPP_END_NAMESPACE_STD
 
+_LIBCPP_POP_MACROS
+
 #endif // _LIBCPP___MEMORY_POINTER_TRAITS_H

diff  --git a/libcxx/include/memory b/libcxx/include/memory
index d52ee7b4c8eee..e1b8462191f84 100644
--- a/libcxx/include/memory
+++ b/libcxx/include/memory
@@ -911,6 +911,22 @@ void* align(size_t alignment, size_t size, void*& ptr, size_t& space);
 template<size_t N, class T>
 [[nodiscard]] constexpr T* assume_aligned(T* ptr); // since C++20
 
+// [out.ptr.t], class template out_ptr_t
+template<class Smart, class Pointer, class... Args>
+  class out_ptr_t;                                          // since c++23
+
+// [out.ptr], function template out_ptr
+template<class Pointer = void, class Smart, class... Args>
+  auto out_ptr(Smart& s, Args&&... args);                   // since c++23
+
+// [inout.ptr.t], class template inout_ptr_t
+template<class Smart, class Pointer, class... Args>
+  class inout_ptr_t;                                        // since c++23
+
+// [inout.ptr], function template inout_ptr
+template<class Pointer = void, class Smart, class... Args>
+  auto inout_ptr(Smart& s, Args&&... args);                 // since c++23
+
 }  // std
 
 */
@@ -924,6 +940,8 @@ template<size_t N, class T>
 #include <__memory/allocator_arg_t.h>
 #include <__memory/allocator_traits.h>
 #include <__memory/auto_ptr.h>
+#include <__memory/inout_ptr.h>
+#include <__memory/out_ptr.h>
 #include <__memory/pointer_traits.h>
 #include <__memory/raw_storage_iterator.h>
 #include <__memory/shared_ptr.h>

diff  --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 5ed284d80f35e..43ab9c6987c84 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -1521,6 +1521,8 @@ module std_private_memory_concepts                        [system] {
 module std_private_memory_construct_at                    [system] { header "__memory/construct_at.h" }
 module std_private_memory_destruct_n                      [system] { header "__memory/destruct_n.h" }
 module std_private_memory_fwd                             [system] { header "__fwd/memory.h" }
+module std_private_memory_inout_ptr                       [system] { header "__memory/inout_ptr.h" }
+module std_private_memory_out_ptr                         [system] { header "__memory/out_ptr.h" }
 module std_private_memory_pointer_traits                  [system] { header "__memory/pointer_traits.h" }
 module std_private_memory_ranges_construct_at             [system] { header "__memory/ranges_construct_at.h" }
 module std_private_memory_ranges_uninitialized_algorithms [system] {

diff  --git a/libcxx/include/version b/libcxx/include/version
index 7d9fad1cb1eb2..68aa88a48fa8f 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -477,7 +477,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 // # define __cpp_lib_move_only_function                   202110L
 # undef  __cpp_lib_optional
 # define __cpp_lib_optional                             202110L
-// # define __cpp_lib_out_ptr                              202106L
+# define __cpp_lib_out_ptr                              202106L
 # define __cpp_lib_print                                202207L
 // # define __cpp_lib_ranges_as_const                      202207L
 # define __cpp_lib_ranges_as_rvalue                     202207L
@@ -536,7 +536,7 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_mdspan                               202406L
 // # define __cpp_lib_optional_range_support               202406L
 # undef  __cpp_lib_out_ptr
-// # define __cpp_lib_out_ptr                              202311L
+# define __cpp_lib_out_ptr                              202311L
 // # define __cpp_lib_philox_engine                        202406L
 // # define __cpp_lib_ranges_concat                        202403L
 # define __cpp_lib_ratio                                202306L

diff  --git a/libcxx/modules/std/memory.inc b/libcxx/modules/std/memory.inc
index 56c621c0cf17f..c15f37a21239c 100644
--- a/libcxx/modules/std/memory.inc
+++ b/libcxx/modules/std/memory.inc
@@ -177,17 +177,19 @@ export namespace std {
   // [util.smartptr.atomic], atomic smart pointers
   // using std::atomic;
 
+#if _LIBCPP_STD_VER >= 23
   // [out.ptr.t], class template out_ptr_t
-  //  using std::out_ptr_t;
+  using std::out_ptr_t;
 
   // [out.ptr], function template out_ptr
-  //  using std::out_ptr;
+  using std::out_ptr;
 
   // [inout.ptr.t], class template inout_ptr_t
-  //  using std::inout_ptr_t;
+  using std::inout_ptr_t;
 
   // [inout.ptr], function template inout_ptr
-  //  using std::inout_ptr;
+  using std::inout_ptr;
+#endif // _LIBCPP_STD_VER >= 23
 
 #ifndef _LIBCPP_HAS_NO_THREADS
   // [depr.util.smartptr.shared.atomic]

diff  --git a/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp
index 45d9271faa578..aa1170658b103 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp
@@ -485,17 +485,11 @@
 #   error "__cpp_lib_make_unique should have the value 201304L in c++23"
 # endif
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_out_ptr
-#     error "__cpp_lib_out_ptr should be defined in c++23"
-#   endif
-#   if __cpp_lib_out_ptr != 202106L
-#     error "__cpp_lib_out_ptr should have the value 202106L in c++23"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_out_ptr
-#     error "__cpp_lib_out_ptr should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_out_ptr
+#   error "__cpp_lib_out_ptr should be defined in c++23"
+# endif
+# if __cpp_lib_out_ptr != 202106L
+#   error "__cpp_lib_out_ptr should have the value 202106L in c++23"
 # endif
 
 # ifndef __cpp_lib_ranges
@@ -622,17 +616,11 @@
 #   error "__cpp_lib_make_unique should have the value 201304L in c++26"
 # endif
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_out_ptr
-#     error "__cpp_lib_out_ptr should be defined in c++26"
-#   endif
-#   if __cpp_lib_out_ptr != 202311L
-#     error "__cpp_lib_out_ptr should have the value 202311L in c++26"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_out_ptr
-#     error "__cpp_lib_out_ptr should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_out_ptr
+#   error "__cpp_lib_out_ptr should be defined in c++26"
+# endif
+# if __cpp_lib_out_ptr != 202311L
+#   error "__cpp_lib_out_ptr should have the value 202311L in c++26"
 # endif
 
 # ifndef __cpp_lib_ranges

diff  --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index e1af3061725df..945de95df67e1 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -5542,17 +5542,11 @@
 #   error "__cpp_lib_optional_range_support should not be defined before c++26"
 # endif
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_out_ptr
-#     error "__cpp_lib_out_ptr should be defined in c++23"
-#   endif
-#   if __cpp_lib_out_ptr != 202106L
-#     error "__cpp_lib_out_ptr should have the value 202106L in c++23"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_out_ptr
-#     error "__cpp_lib_out_ptr should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_out_ptr
+#   error "__cpp_lib_out_ptr should be defined in c++23"
+# endif
+# if __cpp_lib_out_ptr != 202106L
+#   error "__cpp_lib_out_ptr should have the value 202106L in c++23"
 # endif
 
 # if !defined(_LIBCPP_VERSION)
@@ -7383,17 +7377,11 @@
 #   endif
 # endif
 
-# if !defined(_LIBCPP_VERSION)
-#   ifndef __cpp_lib_out_ptr
-#     error "__cpp_lib_out_ptr should be defined in c++26"
-#   endif
-#   if __cpp_lib_out_ptr != 202311L
-#     error "__cpp_lib_out_ptr should have the value 202311L in c++26"
-#   endif
-# else // _LIBCPP_VERSION
-#   ifdef __cpp_lib_out_ptr
-#     error "__cpp_lib_out_ptr should not be defined because it is unimplemented in libc++!"
-#   endif
+# ifndef __cpp_lib_out_ptr
+#   error "__cpp_lib_out_ptr should be defined in c++26"
+# endif
+# if __cpp_lib_out_ptr != 202311L
+#   error "__cpp_lib_out_ptr should have the value 202311L in c++26"
 # endif
 
 # if !defined(_LIBCPP_VERSION)

diff  --git a/libcxx/test/std/utilities/smartptr/adapt/inout_ptr/inout_ptr.general.pass.cpp b/libcxx/test/std/utilities/smartptr/adapt/inout_ptr/inout_ptr.general.pass.cpp
new file mode 100644
index 0000000000000..d55a0c2abd464
--- /dev/null
+++ b/libcxx/test/std/utilities/smartptr/adapt/inout_ptr/inout_ptr.general.pass.cpp
@@ -0,0 +1,206 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <memory>
+
+// [inout.ptr], function template inout_ptr
+// template<class Pointer = void, class Smart, class... Args>
+//   auto inout_ptr(Smart& s, Args&&... args);                 // since c++23
+
+#include <cassert>
+#include <memory>
+#include <utility>
+
+#include "../types.h"
+
+// Test updating the ownership of an `inout_ptr_t`-managed pointer for an API with a non-void pointer type.
+// The API returns a new valid object.
+void test_replace_int_p() {
+  auto replace_int_p = [](int** pp) {
+    assert(**pp == 90);
+
+    delete *pp;
+    *pp = new int{84};
+  };
+
+  // raw pointer
+  {
+    auto rPtr = new int{90};
+
+    replace_int_p(std::inout_ptr<int*>(rPtr));
+    assert(*rPtr == 84);
+
+    delete rPtr;
+  }
+
+  // std::unique_ptr
+  {
+    auto uPtr = std::make_unique<int>(90);
+
+    replace_int_p(std::inout_ptr(uPtr));
+    assert(*uPtr == 84);
+  }
+
+  {
+    MoveOnlyDeleter<int> del;
+    std::unique_ptr<int, MoveOnlyDeleter<int>> uPtr{new int{90}};
+
+    replace_int_p(std::inout_ptr(uPtr, std::move(del)));
+    assert(*uPtr == 84);
+    assert(uPtr.get_deleter().wasMoveInitilized == true);
+  }
+
+  // pointer-like ConstructiblePtr
+  {
+    ConstructiblePtr<int> cPtr(new int{90});
+
+    replace_int_p(std::inout_ptr(cPtr));
+    assert(cPtr == 84);
+  }
+
+  // pointer-like ResettablePtr
+  {
+    ResettablePtr<int> rPtr(new int{90});
+
+    replace_int_p(std::inout_ptr(rPtr));
+    assert(rPtr == 84);
+  }
+
+  // pointer-like NonConstructiblePtr
+  {
+    NonConstructiblePtr<int> nPtr;
+    nPtr.reset(new int{90});
+
+    replace_int_p(std::inout_ptr(nPtr));
+    assert(nPtr == 84);
+  }
+}
+
+// Test updating the ownership of an `inout_ptr_t`-managed pointer for an API with a non-void pointer type.
+// The API returns `nullptr`.
+void test_replace_int_p_with_nullptr() {
+  auto replace_int_p_with_nullptr = [](int** pp) -> void {
+    assert(**pp == 90);
+
+    delete *pp;
+    *pp = nullptr;
+  };
+
+  // raw pointer
+  {
+    // LWG-3897 inout_ptr will not update raw pointer to null
+    auto rPtr = new int{90};
+
+    replace_int_p_with_nullptr(std::inout_ptr<int*>(rPtr));
+    assert(rPtr == nullptr);
+  }
+
+  // std::unique_ptr
+  {
+    auto uPtr = std::make_unique<int>(90);
+
+    replace_int_p_with_nullptr(std::inout_ptr(uPtr));
+    assert(uPtr == nullptr);
+  }
+}
+
+// Test updating the ownership of an `inout_ptr_t`-managed pointer for an API with a void pointer type.
+// The API returns a new valid object.
+void test_replace_int_void_p() {
+  auto replace_int_void_p = [](void** pp) {
+    assert(*(static_cast<int*>(*pp)) == 90);
+
+    delete static_cast<int*>(*pp);
+    *pp = new int{84};
+  };
+
+  // raw pointer
+  {
+    auto rPtr = new int{90};
+
+    replace_int_void_p(std::inout_ptr<int*>(rPtr));
+    assert(*rPtr == 84);
+
+    delete rPtr;
+  }
+
+  // std::unique_ptr
+  {
+    auto uPtr = std::make_unique<int>(90);
+
+    replace_int_void_p(std::inout_ptr(uPtr));
+    assert(*uPtr == 84);
+  }
+}
+
+// Test updating the ownership of an `inout_ptr_t`-managed pointer for an API with a non-void pointer type.
+// The API returns `nullptr`.
+void test_replace_int_void_p_with_nullptr() {
+  auto replace_int_void_p_with_nullptr = [](void** pp) {
+    assert(*(static_cast<int*>(*pp)) == 90);
+
+    delete static_cast<int*>(*pp);
+    *pp = nullptr;
+  };
+
+  // raw pointer
+  {
+    auto rPtr = new int{90};
+
+    replace_int_void_p_with_nullptr(std::inout_ptr<int*>(rPtr));
+    assert(rPtr == nullptr);
+  }
+
+  // std::unique_ptr
+  {
+    auto uPtr = std::make_unique<int>(90);
+
+    replace_int_void_p_with_nullptr(std::inout_ptr(uPtr));
+    assert(uPtr == nullptr);
+  }
+}
+
+// Test updating the ownership of an `inout_ptr_t`-managed pointer for an API with a void pointer type.
+// The API returns a new valid object.
+void test_replace_nullptr_with_int_p() {
+  auto replace_nullptr_with_int_p = [](int** pp) {
+    assert(*pp == nullptr);
+
+    *pp = new int{84};
+  };
+
+  // raw pointer
+  {
+    int* rPtr = nullptr;
+
+    replace_nullptr_with_int_p(std::inout_ptr<int*>(rPtr));
+    assert(*rPtr == 84);
+
+    delete rPtr;
+  }
+
+  // std::unique_ptr
+  {
+    std::unique_ptr<int> uPtr;
+
+    replace_nullptr_with_int_p(std::inout_ptr(uPtr));
+    assert(*uPtr == 84);
+  }
+}
+
+int main(int, char**) {
+  test_replace_int_p();
+  test_replace_int_p_with_nullptr();
+  test_replace_int_void_p();
+  test_replace_int_void_p_with_nullptr();
+  test_replace_nullptr_with_int_p();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/utilities/smartptr/adapt/inout_ptr/inout_ptr.verify.cpp b/libcxx/test/std/utilities/smartptr/adapt/inout_ptr/inout_ptr.verify.cpp
new file mode 100644
index 0000000000000..d6d70782c5eb5
--- /dev/null
+++ b/libcxx/test/std/utilities/smartptr/adapt/inout_ptr/inout_ptr.verify.cpp
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <memory>
+
+// [inout.ptr], function template inout_ptr
+// template<class Pointer = void, class Smart, class... Args>
+//   auto inout_ptr(Smart& s, Args&&... args);                 // since c++23
+
+#include <memory>
+#include <tuple>
+
+#include "../types.h"
+
+int main(int, char**) {
+  // `std::inout_ptr<>` does not support `std::shared_ptr<>`.
+  {
+    std::shared_ptr<int> sPtr;
+
+    // expected-error-re@*:* {{static assertion failed due to requirement {{.*}}std::shared_ptr<> is not supported}}
+    std::ignore = std::inout_ptr(sPtr);
+    // expected-error@*:* {{no matching conversion for functional-style cast from 'std::shared_ptr<int>' to 'std::inout_ptr_t<shared_ptr<int>, _Ptr>' (aka 'inout_ptr_t<std::shared_ptr<int>, int *>'}}
+    std::ignore = std::inout_ptr<int*>(sPtr);
+  }
+
+  return 0;
+}

diff  --git a/libcxx/test/std/utilities/smartptr/adapt/inout_ptr/inout_ptr_t.convert.pass.cpp b/libcxx/test/std/utilities/smartptr/adapt/inout_ptr/inout_ptr_t.convert.pass.cpp
new file mode 100644
index 0000000000000..230402af07cfe
--- /dev/null
+++ b/libcxx/test/std/utilities/smartptr/adapt/inout_ptr/inout_ptr_t.convert.pass.cpp
@@ -0,0 +1,61 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <memory>
+
+// [inout.ptr.t], class template inout_ptr_t
+// template<class Smart, class Pointer, class... Args>
+//   class inout_ptr_t;                                        // since c++23
+
+// operator Pointer*() const noexcept;
+// operator void**() const noexcept;
+
+#include <cassert>
+#include <concepts>
+#include <memory>
+
+int main(int, char**) {
+  // operator Pointer*()
+  {
+    std::unique_ptr<int> uPtr = std::make_unique<int>(84);
+
+    const std::inout_ptr_t<std::unique_ptr<int>, int*> ioPtr{uPtr};
+
+    static_assert(noexcept(ioPtr.operator int**()));
+    std::same_as<int**> decltype(auto) pPtr = ioPtr.operator int**();
+
+    assert(**pPtr == 84);
+  }
+
+  {
+    std::unique_ptr<int, std::default_delete<int>> uPtr = std::make_unique<int>(84);
+
+    const std::inout_ptr_t<decltype(uPtr), int*, std::default_delete<int>> ioPtr{uPtr, std::default_delete<int>{}};
+
+    static_assert(noexcept(ioPtr.operator int**()));
+    std::same_as<int**> decltype(auto) pPtr = ioPtr.operator int**();
+
+    assert(**pPtr == 84);
+  }
+
+  // operator void**()
+  {
+    std::unique_ptr<int> uPtr = std::make_unique<int>(84);
+
+    const std::inout_ptr_t<std::unique_ptr<int>, void*> ioPtr{uPtr};
+
+    static_assert(noexcept(ioPtr.operator void**()));
+    std::same_as<void**> decltype(auto) pPtr = ioPtr.operator void**();
+
+    assert(**reinterpret_cast<int**>(pPtr) == 84);
+  }
+
+  return 0;
+}

diff  --git a/libcxx/test/std/utilities/smartptr/adapt/inout_ptr/inout_ptr_t.ctor.pass.cpp b/libcxx/test/std/utilities/smartptr/adapt/inout_ptr/inout_ptr_t.ctor.pass.cpp
new file mode 100644
index 0000000000000..c7e173a76e728
--- /dev/null
+++ b/libcxx/test/std/utilities/smartptr/adapt/inout_ptr/inout_ptr_t.ctor.pass.cpp
@@ -0,0 +1,61 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <memory>
+
+// [inout.ptr.t], class template inout_ptr_t
+// template<class Smart, class Pointer, class... Args>
+//   class inout_ptr_t;                                        // since c++23
+
+// explicit inout_ptr_t(Smart&, Args...);
+
+#include <cassert>
+#include <memory>
+
+#include "test_convertible.h"
+#include "../types.h"
+
+int main(int, char**) {
+  {
+    std::unique_ptr<int> uPtr;
+
+    std::inout_ptr_t<std::unique_ptr<int>, int*>{uPtr};
+
+    static_assert(
+        !test_convertible<std::inout_ptr_t<std::unique_ptr<int>, int*>>(), "This constructor must be explicit");
+
+    // Test the state of the pointer after construction. Complete tests are available in inout_ptr.general.pass.cpp.
+    assert(uPtr == nullptr);
+  }
+
+  {
+    auto deleter = [](auto* p) { delete p; };
+    std::unique_ptr<int, decltype(deleter)> uPtr;
+
+    std::inout_ptr_t<std::unique_ptr<int, decltype(deleter)>, int*>{uPtr};
+
+    static_assert(!test_convertible<std::inout_ptr_t<std::unique_ptr<int, decltype(deleter)>, int*>>(),
+                  "This constructor must be explicit");
+
+    // Test the state of the pointer after construction. Complete tests are available in inout_ptr.general.pass.cpp.
+    assert(uPtr == nullptr);
+  }
+
+  {
+    std::unique_ptr<int, MoveOnlyDeleter<int>> uPtr;
+
+    std::inout_ptr_t<decltype(uPtr), int*, MoveOnlyDeleter<int>>{uPtr, MoveOnlyDeleter<int>{}};
+
+    // Test the state of the pointer after construction. Complete tests are available in inout_ptr.general.pass.cpp.
+    assert(uPtr == nullptr);
+  }
+
+  return 0;
+}

diff  --git a/libcxx/test/std/utilities/smartptr/adapt/inout_ptr/inout_ptr_t.verify.cpp b/libcxx/test/std/utilities/smartptr/adapt/inout_ptr/inout_ptr_t.verify.cpp
new file mode 100644
index 0000000000000..2270c4c3fc785
--- /dev/null
+++ b/libcxx/test/std/utilities/smartptr/adapt/inout_ptr/inout_ptr_t.verify.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <memory>
+
+// [inout.ptr.t], class template inout_ptr_t
+// template<class Smart, class Pointer, class... Args>
+//   class inout_ptr_t;                                        // since c++23
+
+#include <memory>
+
+int main(int, char**) {
+  // `std::inout_ptr<>` does not support `std::shared_ptr<>`.
+  {
+    std::shared_ptr<int> sPtr;
+
+    // expected-error-re@*:* {{static assertion failed due to requirement {{.*}}std::shared_ptr<> is not supported with std::inout_ptr.}}
+    std::inout_ptr_t<std::shared_ptr<int>, int*>{sPtr};
+  }
+
+  return 0;
+}

diff  --git a/libcxx/test/std/utilities/smartptr/adapt/out_ptr/out_ptr.general.pass.cpp b/libcxx/test/std/utilities/smartptr/adapt/out_ptr/out_ptr.general.pass.cpp
new file mode 100644
index 0000000000000..a78e22f69abc0
--- /dev/null
+++ b/libcxx/test/std/utilities/smartptr/adapt/out_ptr/out_ptr.general.pass.cpp
@@ -0,0 +1,230 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <memory>
+
+// [out.ptr], function template out_ptr
+// template<class Pointer = void, class Smart, class... Args>
+//   auto out_ptr(Smart& s, Args&&... args);                   // since c++23
+
+#include <cassert>
+#include <memory>
+#include <utility>
+
+#include "../types.h"
+
+// Test updating an `out_ptr_t`-managed pointer for an API with a non-void pointer type.
+// The API returns a new valid object.
+void test_get_int_p() {
+  auto get_int_p = [](int** pp) { *pp = new int{84}; };
+
+  // raw pointer
+  {
+    int* rPtr;
+
+    get_int_p(std::out_ptr<int*>(rPtr));
+    assert(*rPtr == 84);
+
+    delete rPtr;
+  }
+
+  // std::unique_ptr
+  {
+    std::unique_ptr<int> uPtr;
+
+    get_int_p(std::out_ptr(uPtr));
+    assert(*uPtr == 84);
+  }
+
+  {
+    MoveOnlyDeleter<int> del;
+    std::unique_ptr<int, MoveOnlyDeleter<int>> uPtr;
+
+    get_int_p(std::out_ptr(uPtr, std::move(del)));
+    assert(*uPtr == 84);
+    assert(uPtr.get_deleter().wasMoveInitilized == true);
+  }
+
+  // std::shared_ptr
+  {
+    std::shared_ptr<int> sPtr;
+
+    get_int_p(std::out_ptr(sPtr, [](auto* p) {
+      assert(*p == 84);
+
+      delete p;
+    }));
+    assert(*sPtr == 84);
+  }
+
+  // pointer-like ConstructiblePtr
+  {
+    ConstructiblePtr<int> cPtr;
+
+    get_int_p(std::out_ptr(cPtr));
+    assert(cPtr == 84);
+  }
+
+  // pointer-like ResettablePtr
+  {
+    ResettablePtr<int> rPtr{nullptr};
+
+    get_int_p(std::out_ptr(rPtr));
+    assert(rPtr == 84);
+  }
+
+  // NonConstructiblePtr
+  {
+    NonConstructiblePtr<int> nPtr;
+
+    get_int_p(std::out_ptr(nPtr));
+    assert(nPtr == 84);
+  }
+}
+
+// Test updating an `out_ptr_t`-managed pointer for an API with a non-void pointer type.
+// The API returns `nullptr`.
+void test_get_int_p_nullptr() {
+  auto get_int_p_nullptr = [](int** pp) { *pp = nullptr; };
+  // raw pointer
+  {
+    int* rPtr;
+
+    get_int_p_nullptr(std::out_ptr<int*>(rPtr));
+    assert(rPtr == nullptr);
+
+    delete rPtr;
+  }
+
+  // std::unique_ptr
+  {
+    std::unique_ptr<int> uPtr;
+
+    get_int_p_nullptr(std::out_ptr(uPtr));
+    assert(uPtr == nullptr);
+  }
+
+  // std::shared_ptr
+  {
+    std::shared_ptr<int> sPtr;
+
+    get_int_p_nullptr(std::out_ptr(sPtr, [](auto* p) {
+      assert(p == nullptr);
+
+      delete p;
+    }));
+    assert(sPtr == nullptr);
+  }
+}
+
+// Test updating an `out_ptr_t`-managed pointer for an API with a void pointer type.
+// The API returns a new valid object.
+void test_get_int_void_p() {
+  auto get_int_void_p = [](void** pp) { *(reinterpret_cast<int**>(pp)) = new int{84}; };
+
+  // raw pointer
+  {
+    int* rPtr;
+
+    get_int_void_p(std::out_ptr(rPtr));
+    assert(*rPtr == 84);
+
+    delete rPtr;
+  }
+
+  // std::unique_ptr
+  {
+    std::unique_ptr<int> uPtr;
+
+    get_int_void_p(std::out_ptr(uPtr));
+    assert(*uPtr == 84);
+  }
+
+  // std::shared_ptr
+  {
+    std::shared_ptr<int> sPtr;
+
+    get_int_void_p(std::out_ptr(sPtr, [](auto* p) {
+      assert(*p == 84);
+
+      delete p;
+    }));
+    assert(*sPtr == 84);
+  }
+
+  // pointer-like ConstructiblePtr
+  {
+    ConstructiblePtr<int> cPtr;
+
+    get_int_void_p(std::out_ptr(cPtr));
+    assert(cPtr == 84);
+  }
+
+  // pointer-like ResettablePtr
+  {
+    ResettablePtr<int> rPtr{nullptr};
+
+    get_int_void_p(std::out_ptr(rPtr));
+    assert(rPtr == 84);
+  }
+
+  // NonConstructiblePtr
+  {
+    NonConstructiblePtr<int> nPtr;
+
+    get_int_void_p(std::out_ptr(nPtr));
+    assert(nPtr == 84);
+  }
+}
+
+// Test updating an `out_ptr_t`-managed pointer for an API with a void pointer type.
+// The API returns `nullptr`.
+void test_get_int_void_p_nullptr() {
+  auto get_int_void_p_nullptr = [](void** pp) { *pp = nullptr; };
+
+  // raw pointer
+  {
+    int* rPtr;
+
+    get_int_void_p_nullptr(std::out_ptr<int*>(rPtr));
+    assert(rPtr == nullptr);
+
+    delete rPtr;
+  }
+
+  // std::unique_ptr
+  {
+    std::unique_ptr<int> uPtr;
+
+    get_int_void_p_nullptr(std::out_ptr(uPtr));
+    assert(uPtr == nullptr);
+  }
+
+  // std::shared_ptr
+  {
+    std::shared_ptr<int> sPtr;
+
+    get_int_void_p_nullptr(std::out_ptr(sPtr, [](auto* p) {
+      assert(p == nullptr);
+
+      delete p;
+    }));
+    assert(sPtr == nullptr);
+  }
+}
+
+int main(int, char**) {
+  test_get_int_p();
+  test_get_int_p_nullptr();
+  test_get_int_void_p();
+  test_get_int_void_p_nullptr();
+
+  return 0;
+}

diff  --git a/libcxx/test/std/utilities/smartptr/adapt/out_ptr/out_ptr.verify.cpp b/libcxx/test/std/utilities/smartptr/adapt/out_ptr/out_ptr.verify.cpp
new file mode 100644
index 0000000000000..1fe78ecb22789
--- /dev/null
+++ b/libcxx/test/std/utilities/smartptr/adapt/out_ptr/out_ptr.verify.cpp
@@ -0,0 +1,34 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <memory>
+
+// [out.ptr], function template out_ptr
+// template<class Pointer = void, class Smart, class... Args>
+//   auto out_ptr(Smart& s, Args&&... args);                   // since c++23
+
+#include <memory>
+#include <tuple>
+
+#include "../types.h"
+
+int main(int, char**) {
+  // `std::out_ptr<>` requires `std::shared_ptr<>` with a deleter.
+  {
+    std::shared_ptr<int> sPtr;
+
+    // expected-error-re@*:* {{static assertion failed due to requirement {{.*}}Using std::shared_ptr<> without a deleter in std::out_ptr is not supported.}}
+    std::ignore = std::out_ptr(sPtr);
+    // expected-error@*:* {{no matching conversion for functional-style cast from 'std::shared_ptr<int>' to 'std::out_ptr_t<shared_ptr<int>, _Ptr>' (aka 'out_ptr_t<std::shared_ptr<int>, int *>')}}
+    std::ignore = std::out_ptr<int*>(sPtr);
+  }
+
+  return 0;
+}

diff  --git a/libcxx/test/std/utilities/smartptr/adapt/out_ptr/out_ptr_t.convert.pass.cpp b/libcxx/test/std/utilities/smartptr/adapt/out_ptr/out_ptr_t.convert.pass.cpp
new file mode 100644
index 0000000000000..828a5d0ba31ea
--- /dev/null
+++ b/libcxx/test/std/utilities/smartptr/adapt/out_ptr/out_ptr_t.convert.pass.cpp
@@ -0,0 +1,61 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <memory>
+
+// [out.ptr.t], class template out_ptr_t
+// template<class Smart, class Pointer, class... Args>
+//   class out_ptr_t;                                          // since c++23
+
+// operator Pointer*() const noexcept;
+// operator void**() const noexcept;
+
+#include <cassert>
+#include <concepts>
+#include <memory>
+
+int main(int, char**) {
+  // operator Pointer*()
+  {
+    std::unique_ptr<int> uPtr;
+
+    const std::out_ptr_t<std::unique_ptr<int>, int*> oPtr{uPtr};
+
+    static_assert(noexcept(oPtr.operator int**()));
+    std::same_as<int**> decltype(auto) pPtr = oPtr.operator int**();
+
+    assert(*pPtr == nullptr);
+  }
+
+  {
+    std::unique_ptr<int, std::default_delete<int>> uPtr;
+
+    const std::out_ptr_t<decltype(uPtr), int*, std::default_delete<int>> oPtr{uPtr, std::default_delete<int>{}};
+
+    static_assert(noexcept(oPtr.operator int**()));
+    std::same_as<int**> decltype(auto) pPtr = oPtr.operator int**();
+
+    assert(*pPtr == nullptr);
+  }
+
+  // operator void**()
+  {
+    std::unique_ptr<int> uPtr;
+
+    const std::out_ptr_t<std::unique_ptr<int>, void*> oPtr{uPtr};
+
+    static_assert(noexcept(oPtr.operator void**()));
+    std::same_as<void**> decltype(auto) pPtr = oPtr.operator void**();
+
+    assert(*pPtr == nullptr);
+  }
+
+  return 0;
+}

diff  --git a/libcxx/test/std/utilities/smartptr/adapt/out_ptr/out_ptr_t.ctor.pass.cpp b/libcxx/test/std/utilities/smartptr/adapt/out_ptr/out_ptr_t.ctor.pass.cpp
new file mode 100644
index 0000000000000..29e1258845bc9
--- /dev/null
+++ b/libcxx/test/std/utilities/smartptr/adapt/out_ptr/out_ptr_t.ctor.pass.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <memory>
+
+// [out.ptr.t], class template out_ptr_t
+// template<class Smart, class Pointer, class... Args>
+//   class out_ptr_t;                                          // since c++23
+
+// explicit out_ptr_t(Smart&, Args...);
+
+#include <cassert>
+#include <memory>
+
+#include "test_convertible.h"
+#include "../types.h"
+
+int main(int, char**) {
+  {
+    std::unique_ptr<int> uPtr;
+
+    std::out_ptr_t<std::unique_ptr<int>, int*>{uPtr};
+
+    static_assert(!test_convertible<std::out_ptr_t<std::unique_ptr<int>, int*>>(), "This constructor must be explicit");
+
+    // Test the state of the pointer after construction. Complete tests are available in out_ptr.general.pass.cpp
+    assert(uPtr == nullptr);
+  }
+
+  {
+    std::unique_ptr<int, std::default_delete<int>> uPtr;
+
+    std::out_ptr_t<decltype(uPtr), int*, std::default_delete<int>>{uPtr, std::default_delete<int>{}};
+
+    static_assert(!test_convertible<std::out_ptr_t<decltype(uPtr), int*, std::default_delete<int>>>(),
+                  "This constructor must be explicit");
+
+    // Test the state of the pointer after construction. Complete tests are available in out_ptr.general.pass.cpp
+    assert(uPtr == nullptr);
+  }
+
+  {
+    std::unique_ptr<int, MoveOnlyDeleter<int>> uPtr;
+
+    std::out_ptr_t<decltype(uPtr), int*, MoveOnlyDeleter<int>>{uPtr, MoveOnlyDeleter<int>{}};
+
+    // Test the state of the pointer after construction. Complete tests are available in out_ptr.general.pass.cpp
+    assert(uPtr == nullptr);
+  }
+
+  return 0;
+}

diff  --git a/libcxx/test/std/utilities/smartptr/adapt/out_ptr/out_ptr_t.verify.cpp b/libcxx/test/std/utilities/smartptr/adapt/out_ptr/out_ptr_t.verify.cpp
new file mode 100644
index 0000000000000..cbe96e752ac92
--- /dev/null
+++ b/libcxx/test/std/utilities/smartptr/adapt/out_ptr/out_ptr_t.verify.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <memory>
+
+// [out.ptr.t], class template out_ptr_t
+// template<class Smart, class Pointer, class... Args>
+//   class out_ptr_t;                                          // since c++23
+
+#include <memory>
+
+int main(int, char**) {
+  // `std::out_ptr_t<>` requires `std::shared_ptr<>` with a deleter.
+  {
+    std::shared_ptr<int> sPtr;
+
+    // expected-error-re@*:* {{static assertion failed due to requirement {{.*}}Using std::shared_ptr<> without a deleter in std::out_ptr is not supported.}}
+    std::out_ptr_t<std::shared_ptr<int>, int*>{sPtr};
+  }
+
+  return 0;
+}

diff  --git a/libcxx/test/std/utilities/smartptr/adapt/types.h b/libcxx/test/std/utilities/smartptr/adapt/types.h
new file mode 100644
index 0000000000000..0da6007ba394f
--- /dev/null
+++ b/libcxx/test/std/utilities/smartptr/adapt/types.h
@@ -0,0 +1,86 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef TEST_LIBCXX_UTILITIES_SMARTPTR_ADAPT_TYPES_H
+#define TEST_LIBCXX_UTILITIES_SMARTPTR_ADAPT_TYPES_H
+
+#include <type_traits>
+#include <memory>
+
+#include "test_macros.h"
+
+// Custom deleters.
+
+template <typename T>
+struct MoveOnlyDeleter {
+  MoveOnlyDeleter()                                  = default;
+  MoveOnlyDeleter(const MoveOnlyDeleter&)            = delete;
+  MoveOnlyDeleter& operator=(const MoveOnlyDeleter&) = delete;
+  MoveOnlyDeleter(MoveOnlyDeleter&&) : wasMoveInitilized{true} {}
+  MoveOnlyDeleter& operator=(MoveOnlyDeleter&&) = default;
+
+  void operator()(T* p) const { delete p; }
+
+  bool wasMoveInitilized = false;
+};
+
+// Custom pointer types.
+
+template <typename T>
+struct ConstructiblePtr {
+  using pointer = T*;
+  std::unique_ptr<T> ptr;
+
+  ConstructiblePtr() = default;
+  explicit ConstructiblePtr(T* p) : ptr{p} {}
+
+  auto operator==(T val) { return *ptr == val; }
+
+  auto* get() const { return ptr.get(); }
+
+  void release() { ptr.release(); }
+};
+
+LIBCPP_STATIC_ASSERT(std::is_same_v<std::__pointer_of_t< ConstructiblePtr<int>>, int* >);
+static_assert(std::is_constructible_v< ConstructiblePtr<int>, int* >);
+
+struct ResetArg {};
+
+template <typename T>
+struct ResettablePtr {
+  using element_type = T;
+  std::unique_ptr<T> ptr;
+
+  explicit ResettablePtr(T* p) : ptr{p} {}
+
+  auto operator*() const { return *ptr; }
+
+  auto operator==(T val) { return *ptr == val; }
+
+  void reset() { ptr.reset(); }
+  void reset(T* p, ResetArg) { ptr.reset(p); }
+
+  auto* get() const { return ptr.get(); }
+
+  void release() { ptr.release(); }
+};
+
+LIBCPP_STATIC_ASSERT(std::is_same_v<std::__pointer_of_t< ResettablePtr<int>>, int* >);
+static_assert(std::is_constructible_v< ResettablePtr<int>, int* >);
+
+template <typename T>
+struct NonConstructiblePtr : public ResettablePtr<T> {
+  NonConstructiblePtr() : NonConstructiblePtr::ResettablePtr(nullptr) {};
+
+  void reset(T* p) { ResettablePtr<T>::ptr.reset(p); }
+};
+
+LIBCPP_STATIC_ASSERT(std::is_same_v<std::__pointer_of_t< NonConstructiblePtr<int>>, int* >);
+static_assert(!std::is_constructible_v< NonConstructiblePtr<int>, int* >);
+
+#endif // TEST_LIBCXX_UTILITIES_SMARTPTR_ADAPT_TYPES_H

diff  --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 65ba2f8455f1b..a09beb29565ad 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -949,7 +949,6 @@ def add_version_header(tc):
                 "c++26": 202311,  # P2833R2 Freestanding Library: inout expected span
             },
             "headers": ["memory"],
-            "unimplemented": True,
         },
         {
             "name": "__cpp_lib_parallel_algorithm",


        


More information about the libcxx-commits mailing list