Skip to content

Conversation

@H-G-Hristov
Copy link
Contributor

@H-G-Hristov H-G-Hristov commented Nov 28, 2023

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

Implements:

@H-G-Hristov H-G-Hristov requested a review from a team as a code owner November 28, 2023 07:40
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Nov 28, 2023
@llvmbot
Copy link
Member

llvmbot commented Nov 28, 2023

@llvm/pr-subscribers-libcxx

Author: Hristo Hristov (H-G-Hristov)

Changes

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


Patch is 44.95 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/73618.diff

26 Files Affected:

  • (modified) libcxx/docs/FeatureTestMacroTable.rst (+1-1)
  • (modified) libcxx/docs/Status/Cxx23Issues.csv (+2-2)
  • (modified) libcxx/docs/Status/Cxx23Papers.csv (+1-1)
  • (modified) libcxx/include/CMakeLists.txt (+2)
  • (modified) libcxx/include/__memory/allocator_traits.h (-1)
  • (added) libcxx/include/__memory/inout_ptr.h (+100)
  • (added) libcxx/include/__memory/out_ptr.h (+97)
  • (modified) libcxx/include/__memory/pointer_traits.h (+63-4)
  • (modified) libcxx/include/memory (+18)
  • (modified) libcxx/include/module.modulemap.in (+2)
  • (modified) libcxx/include/version (+1-1)
  • (modified) libcxx/modules/std/memory.inc (+6-4)
  • (modified) libcxx/test/std/language.support/support.limits/support.limits.general/memory.version.compile.pass.cpp (+5-11)
  • (modified) libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp (+5-11)
  • (added) libcxx/test/std/utilities/smartptr/adapt/inout.ptr/inout.ptr.compile.pass.cpp (+35)
  • (added) libcxx/test/std/utilities/smartptr/adapt/inout.ptr/inout.ptr.pass.cpp (+205)
  • (added) libcxx/test/std/utilities/smartptr/adapt/inout.ptr/inout.ptr.t.compile.pass.cpp (+32)
  • (added) libcxx/test/std/utilities/smartptr/adapt/inout.ptr/inout.ptr.t.verify.cpp (+29)
  • (added) libcxx/test/std/utilities/smartptr/adapt/inout.ptr/inout.ptr.verify.cpp (+30)
  • (added) libcxx/test/std/utilities/smartptr/adapt/out.ptr/out.ptr.compile.pass.cpp (+34)
  • (added) libcxx/test/std/utilities/smartptr/adapt/out.ptr/out.ptr.pass.cpp (+163)
  • (added) libcxx/test/std/utilities/smartptr/adapt/out.ptr/out.ptr.t.compile.pass.cpp (+32)
  • (added) libcxx/test/std/utilities/smartptr/adapt/out.ptr/out.ptr.t.verify.cpp (+29)
  • (added) libcxx/test/std/utilities/smartptr/adapt/out.ptr/out.ptr.verify.cpp (+30)
  • (added) libcxx/test/std/utilities/smartptr/adapt/types.h (+77)
  • (modified) libcxx/utils/generate_feature_test_macro_components.py (-1)
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 3b2dff3108b0f60..f33ca7f0cc58fd3 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -342,7 +342,7 @@ Status
     --------------------------------------------------- -----------------
     ``__cpp_lib_optional``                              ``202110L``
     --------------------------------------------------- -----------------
-    ``__cpp_lib_out_ptr``                               *unimplemented*
+    ``__cpp_lib_out_ptr``                               ``202106L``
     --------------------------------------------------- -----------------
     ``__cpp_lib_print``                                 *unimplemented*
     --------------------------------------------------- -----------------
diff --git a/libcxx/docs/Status/Cxx23Issues.csv b/libcxx/docs/Status/Cxx23Issues.csv
index b24ecc5525a1497..e5ad365f9ffb877 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|","17.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|","17.0",""
 "`3772 <https://wg21.link/LWG3772>`__","``repeat_view``'s ``piecewise`` constructor is missing Postconditions","February 2023","","","|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 5cc9e488297b9f7..198b0ab9d390e33 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|","17.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/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b0bc3718576f66b..f2ba3d12a2b3854 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -528,6 +528,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 eea5ee973d37fd7..ed13cf8a3c278c1 100644
--- a/libcxx/include/__memory/allocator_traits.h
+++ b/libcxx/include/__memory/allocator_traits.h
@@ -38,7 +38,6 @@ _LIBCPP_BEGIN_NAMESPACE_STD
     template <class _Tp>               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>,
           bool = __has_pointer<_RawAlloc>::value>
diff --git a/libcxx/include/__memory/inout_ptr.h b/libcxx/include/__memory/inout_ptr.h
new file mode 100644
index 000000000000000..c2044a4642cf4ee
--- /dev/null
+++ b/libcxx/include/__memory/inout_ptr.h
@@ -0,0 +1,100 @@
+// -*- 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 <tuple>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_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");
+
+public:
+  _LIBCPP_HIDE_FROM_ABI explicit inout_ptr_t(_Smart& __s, _Args... __args)
+      : __s_(__s), __a_(std::forward<_Args>(__args)...), __p_([&__s] {
+          if constexpr (is_pointer_v<_Smart>) {
+            return __s;
+          } else {
+            return __s.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() {
+    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 if constexpr (is_constructible_v<_Smart, _SP, _Args...>) {
+      std::apply([&](auto&&... __args) { __s_ = _Smart(static_cast<_SP>(__p_), std::forward<_Args>(__args)...); },
+                 std::move(__a_));
+    } else {
+      static_assert(is_pointer_v<_Smart> || __resettable_smart_pointer_with_args<_Smart, _Pointer, _Args...> ||
+                    is_constructible_v<_Smart, _SP, _Args...>);
+    }
+  }
+
+  _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>);
+
+    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
+
+#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 000000000000000..09c77cbafe69e01
--- /dev/null
+++ b/libcxx/include/__memory/out_ptr.h
@@ -0,0 +1,97 @@
+// -*- 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 <tuple>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+template <class _Tp>
+concept __resettable_adapted_ptr = requires(_Tp __ptr) { __ptr().reset(); };
+
+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,
+                "Specialization of std::shared_ptr<> requires a deleter.");
+
+public:
+  _LIBCPP_HIDE_FROM_ABI explicit out_ptr_t(_Smart& __s, _Args... __args)
+      : __s_(__s), __a_(std::forward<_Args>(__args)...), __p_() {
+    using _Ptr = decltype(__s);
+    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>);
+    }
+  }
+
+  _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 if constexpr (is_constructible_v<_Smart, _SP, _Args...>) {
+      std::apply([&](auto&&... __args) { __s_ = _Smart(static_cast<_SP>(__p_), std::forward<_Args>(__args)...); },
+                 std::move(__a_));
+    } else {
+      static_assert(__resettable_smart_pointer_with_args<_Smart, _Pointer, _Args...> ||
+                    is_constructible_v<_Smart, _SP, _Args...>);
+    }
+  }
+
+  _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>);
+
+    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
+
+#endif // _LIBCPP___OUT_PTR_H
diff --git a/libcxx/include/__memory/pointer_traits.h b/libcxx/include/__memory/pointer_traits.h
index 7617948ed76bd66..2be0deb50cb8ed1 100644
--- a/libcxx/include/__memory/pointer_traits.h
+++ b/libcxx/include/__memory/pointer_traits.h
@@ -15,11 +15,14 @@
 #include <__type_traits/conditional.h>
 #include <__type_traits/conjunction.h>
 #include <__type_traits/decay.h>
+#include <__type_traits/enable_if.h>
 #include <__type_traits/is_class.h>
 #include <__type_traits/is_function.h>
 #include <__type_traits/is_void.h>
+#include <__type_traits/negation.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)
@@ -28,11 +31,16 @@
 
 _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 {};
@@ -242,6 +250,57 @@ auto 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 = void, 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
 
 #endif // _LIBCPP___MEMORY_POINTER_TRAITS_H
diff --git a/libcxx/include/memory b/libcxx/include/memory
index 24ba82f43ddd309..d3ab5f73bf00aae 100644
--- a/libcxx/include/memory
+++ b/libcxx/include/memory
@@ -910,6 +910,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
 
 */
@@ -928,6 +944,8 @@ template<size_t N, class T>
 #include <__memory/compressed_pair.h>
 #include <__memory/concepts.h>
 #include <__memory/construct_at.h>
+#include <__memory/inout_ptr.h>
+#include <__memory/out_ptr.h>
 #include <__memory/pointer_traits.h>
 #include <__memory/ranges_construct_at.h>
 #include <__memory/ranges_uninitialized_algorithms.h>
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 90381f4f7f720d4..bb9143b3afa4d27 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1518,6 +1518,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_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 ef01af753298222..96a16c8c6381a66 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -455,7 +455,7 @@ __cpp_lib_within_lifetime                               202306L <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
diff --git a/libcxx/modules/std/memory.inc b/libcxx/modules/std/memory.inc
index fba2461732c1b9d..a02fa2b865f57fa 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.c...
[truncated]

Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for re-uploading this patch from Phabricator! I didn't do an in-depth review of the implementation yet but I do have some comments. I'll do another pass once they have been addressed.

@H-G-Hristov H-G-Hristov force-pushed the hgh/libcxx/P1132R8-out_ptr-a-scalable-output-pointer-abstraction branch from 654ef57 to cb71ea2 Compare November 30, 2023 13:05

_LIBCPP_HIDE_FROM_ABI out_ptr_t(const out_ptr_t&) = delete;

_LIBCPP_HIDE_FROM_ABI ~out_ptr_t() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to do something about https://eel.is/c++draft/smartptr#out.ptr.t-7. Otherwise, when we use a shared_ptr here, if we fail to allocate the control block we'll be in trouble, since this is a destructor. So, instead what we could do is allocate a control block in the constructor, then use shared_ptr<T>::__create_with_control_block in the destructor. The simplest way to do this might be to create a specialization for shared_ptr<T>. Do you think that is overkill?

@ldionne ldionne self-assigned this Dec 14, 2023
@github-actions
Copy link

github-actions bot commented Dec 16, 2023

✅ With the latest revision this PR passed the C/C++ code formatter.

Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks mostly good, still a few comments.

@@ -0,0 +1,874 @@
[
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file isn't checked in anymore, it should be removed.

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,
"Specialization of std::shared_ptr<> requires a deleter.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"Specialization of std::shared_ptr<> requires a deleter.");
"Using std::shared_ptr<> without a deleter in std::out_ptr is not supported.");

Or

Suggested change
"Specialization of std::shared_ptr<> requires a deleter.");
"Using std::shared_ptr<> in std::out_ptr requires a deleter.");

} else if constexpr (is_constructible_v<_Smart>) {
__s_ = _Smart();
} else {
static_assert(__resettable_smart_pointer<_Ptr> || is_constructible_v<_Smart>);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An error message would be nice.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DONE. Added error messages.


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");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
static_assert(!__is_specialization_v<_Smart, shared_ptr>, "std::shared_ptr<> is not supported");
static_assert(!__is_specialization_v<_Smart, shared_ptr>, "std::shared_ptr<> is not supported with std::inout_ptr");


// Custom pointer types.

template <typename _Tp>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
template <typename _Tp>
template <typename T>

Here and below. We shouldn't use reserved names in the test suite.

Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM with my final comments applied and CI passing. Thanks!

void release() { ptr.release(); }
};

static_assert(std::is_same_v<std::__pointer_of_t< ConstructiblePtr<int>>, int* >);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and below, we can't use libc++ private helpers cause the test suite needs to work with other implementations as well. I would either remove this static_assert or turn it into LIBCPP_STATIC_ASSERT if it's really really important to check.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I'd like to have that test. I'll convert it to LIBCPP_STATIC_ASSERT. Thanks!

@H-G-Hristov
Copy link
Contributor Author

LGTM with my final comments applied and CI passing. Thanks!

@ldionne Thank you very much!

@Zingam Zingam merged commit e475bb7 into llvm:main Jul 19, 2024
@llvm-ci
Copy link
Collaborator

llvm-ci commented Jul 19, 2024

LLVM Buildbot has detected a new failure on builder sanitizer-x86_64-linux running on sanitizer-buildbot2 while building libcxx at step 2 "annotate".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/66/builds/1820

Here is the relevant piece of the build log for the reference:

Step 2 (annotate) failure: 'python ../sanitizer_buildbot/sanitizers/zorg/buildbot/builders/sanitizers/buildbot_selector.py' (failure)
...
[366/371] Generating MSAN_INST_GTEST.gtest-all.cc.x86_64.o
[367/371] Generating MSAN_INST_TEST_OBJECTS.msan_test.cpp.x86_64-with-call.o
[368/371] Generating Msan-x86_64-with-call-Test
[369/371] Generating MSAN_INST_TEST_OBJECTS.msan_test.cpp.x86_64.o
[370/371] Generating Msan-x86_64-Test
[370/371] Running compiler_rt regression tests
llvm-lit: /b/sanitizer-x86_64-linux/build/llvm-project/llvm/utils/lit/lit/main.py:72: note: The test suite configuration requested an individual test timeout of 0 seconds but a timeout of 900 seconds was requested on the command line. Forcing timeout to be 900 seconds.
-- Testing: 10021 tests, 88 workers --
Testing:  0.. 10.. 20.. 30.. 40.. 50.. 60.. 70.. 80.. 90..
TIMEOUT: SanitizerCommon-hwasan-x86_64-Linux :: Posix/fork_threaded.c (10021 of 10021)
******************** TEST 'SanitizerCommon-hwasan-x86_64-Linux :: Posix/fork_threaded.c' FAILED ********************
Exit Code: -9
Timeout: Reached timeout of 900 seconds

Command Output (stderr):
--
RUN: at line 1: /b/sanitizer-x86_64-linux/build/build_default/./bin/clang  -gline-tables-only -fsanitize=hwaddress -fuse-ld=lld -fsanitize-hwaddress-experimental-aliasing  -m64 -funwind-tables  -I/b/sanitizer-x86_64-linux/build/llvm-project/compiler-rt/test -ldl -O0 /b/sanitizer-x86_64-linux/build/llvm-project/compiler-rt/test/sanitizer_common/TestCases/Posix/fork_threaded.c -o /b/sanitizer-x86_64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/hwasan-x86_64-Linux/Posix/Output/fork_threaded.c.tmp && env HWASAN_OPTIONS=die_after_fork=0  /b/sanitizer-x86_64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/hwasan-x86_64-Linux/Posix/Output/fork_threaded.c.tmp
+ /b/sanitizer-x86_64-linux/build/build_default/./bin/clang -gline-tables-only -fsanitize=hwaddress -fuse-ld=lld -fsanitize-hwaddress-experimental-aliasing -m64 -funwind-tables -I/b/sanitizer-x86_64-linux/build/llvm-project/compiler-rt/test -ldl -O0 /b/sanitizer-x86_64-linux/build/llvm-project/compiler-rt/test/sanitizer_common/TestCases/Posix/fork_threaded.c -o /b/sanitizer-x86_64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/hwasan-x86_64-Linux/Posix/Output/fork_threaded.c.tmp
+ env HWASAN_OPTIONS=die_after_fork=0 /b/sanitizer-x86_64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/hwasan-x86_64-Linux/Posix/Output/fork_threaded.c.tmp

=================================================================
==1153114==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 568 byte(s) in 3 object(s) allocated from:
    #0 0x5c0d6e833528 in malloc /b/sanitizer-x86_64-linux/build/llvm-project/compiler-rt/lib/hwasan/hwasan_allocation_functions.cpp:151:3
    #1 0x5c0d6e98b47e in operator new(unsigned long) llvm-link
    #2 0x5c0d6e829a14 in _start (/b/sanitizer-x86_64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/hwasan-x86_64-Linux/Posix/Output/fork_threaded.c.tmp+0xeea14)

Direct leak of 204 byte(s) in 1 object(s) allocated from:
    #0 0x5c0d6e83306d in calloc /b/sanitizer-x86_64-linux/build/llvm-project/compiler-rt/lib/hwasan/hwasan_allocation_functions.cpp:127:3
    #1 0x5c0d6e913f8d in llvm::safe_calloc(unsigned long, unsigned long) llvm-link
    #2 0x5c0d6e829a14 in _start (/b/sanitizer-x86_64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/hwasan-x86_64-Linux/Posix/Output/fork_threaded.c.tmp+0xeea14)

Indirect leak of 1752 byte(s) in 42 object(s) allocated from:
    #0 0x5c0d6e832a29 in aligned_alloc /b/sanitizer-x86_64-linux/build/llvm-project/compiler-rt/lib/hwasan/hwasan_allocation_functions.cpp:58:3
    #1 0x5c0d6e921897 in llvm::allocate_buffer(unsigned long, unsigned long) llvm-link

Indirect leak of 780 byte(s) in 1 object(s) allocated from:
    #0 0x5c0d6e83306d in calloc /b/sanitizer-x86_64-linux/build/llvm-project/compiler-rt/lib/hwasan/hwasan_allocation_functions.cpp:127:3
    #1 0x5c0d6e913f8d in llvm::safe_calloc(unsigned long, unsigned long) llvm-link

Indirect leak of 160 byte(s) in 1 object(s) allocated from:
    #0 0x5c0d6e833528 in malloc /b/sanitizer-x86_64-linux/build/llvm-project/compiler-rt/lib/hwasan/hwasan_allocation_functions.cpp:151:3
    #1 0x5c0d6e98b47e in operator new(unsigned long) llvm-link
    #2 0x5c0d6e829a14 in _start (/b/sanitizer-x86_64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/hwasan-x86_64-Linux/Posix/Output/fork_threaded.c.tmp+0xeea14)

SUMMARY: HWAddressSanitizer: 3464 byte(s) leaked in 48 allocation(s).
==1153114==ERROR: HWAddressSanitizer: tag-mismatch on address 0x481400005140 at pc 0x5c0d6e83edf7
READ of size 155 at 0x481400005140 tags: 03/00 (ptr/mem) in thread T2
Step 9 (test compiler-rt symbolizer) failure: test compiler-rt symbolizer (failure)
...
[366/371] Generating MSAN_INST_GTEST.gtest-all.cc.x86_64.o
[367/371] Generating MSAN_INST_TEST_OBJECTS.msan_test.cpp.x86_64-with-call.o
[368/371] Generating Msan-x86_64-with-call-Test
[369/371] Generating MSAN_INST_TEST_OBJECTS.msan_test.cpp.x86_64.o
[370/371] Generating Msan-x86_64-Test
[370/371] Running compiler_rt regression tests
llvm-lit: /b/sanitizer-x86_64-linux/build/llvm-project/llvm/utils/lit/lit/main.py:72: note: The test suite configuration requested an individual test timeout of 0 seconds but a timeout of 900 seconds was requested on the command line. Forcing timeout to be 900 seconds.
-- Testing: 10021 tests, 88 workers --
Testing:  0.. 10.. 20.. 30.. 40.. 50.. 60.. 70.. 80.. 90..
TIMEOUT: SanitizerCommon-hwasan-x86_64-Linux :: Posix/fork_threaded.c (10021 of 10021)
******************** TEST 'SanitizerCommon-hwasan-x86_64-Linux :: Posix/fork_threaded.c' FAILED ********************
Exit Code: -9
Timeout: Reached timeout of 900 seconds

Command Output (stderr):
--
RUN: at line 1: /b/sanitizer-x86_64-linux/build/build_default/./bin/clang  -gline-tables-only -fsanitize=hwaddress -fuse-ld=lld -fsanitize-hwaddress-experimental-aliasing  -m64 -funwind-tables  -I/b/sanitizer-x86_64-linux/build/llvm-project/compiler-rt/test -ldl -O0 /b/sanitizer-x86_64-linux/build/llvm-project/compiler-rt/test/sanitizer_common/TestCases/Posix/fork_threaded.c -o /b/sanitizer-x86_64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/hwasan-x86_64-Linux/Posix/Output/fork_threaded.c.tmp && env HWASAN_OPTIONS=die_after_fork=0  /b/sanitizer-x86_64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/hwasan-x86_64-Linux/Posix/Output/fork_threaded.c.tmp
+ /b/sanitizer-x86_64-linux/build/build_default/./bin/clang -gline-tables-only -fsanitize=hwaddress -fuse-ld=lld -fsanitize-hwaddress-experimental-aliasing -m64 -funwind-tables -I/b/sanitizer-x86_64-linux/build/llvm-project/compiler-rt/test -ldl -O0 /b/sanitizer-x86_64-linux/build/llvm-project/compiler-rt/test/sanitizer_common/TestCases/Posix/fork_threaded.c -o /b/sanitizer-x86_64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/hwasan-x86_64-Linux/Posix/Output/fork_threaded.c.tmp
+ env HWASAN_OPTIONS=die_after_fork=0 /b/sanitizer-x86_64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/hwasan-x86_64-Linux/Posix/Output/fork_threaded.c.tmp

=================================================================
==1153114==ERROR: LeakSanitizer: detected memory leaks

Direct leak of 568 byte(s) in 3 object(s) allocated from:
    #0 0x5c0d6e833528 in malloc /b/sanitizer-x86_64-linux/build/llvm-project/compiler-rt/lib/hwasan/hwasan_allocation_functions.cpp:151:3
    #1 0x5c0d6e98b47e in operator new(unsigned long) llvm-link
    #2 0x5c0d6e829a14 in _start (/b/sanitizer-x86_64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/hwasan-x86_64-Linux/Posix/Output/fork_threaded.c.tmp+0xeea14)

Direct leak of 204 byte(s) in 1 object(s) allocated from:
    #0 0x5c0d6e83306d in calloc /b/sanitizer-x86_64-linux/build/llvm-project/compiler-rt/lib/hwasan/hwasan_allocation_functions.cpp:127:3
    #1 0x5c0d6e913f8d in llvm::safe_calloc(unsigned long, unsigned long) llvm-link
    #2 0x5c0d6e829a14 in _start (/b/sanitizer-x86_64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/hwasan-x86_64-Linux/Posix/Output/fork_threaded.c.tmp+0xeea14)

Indirect leak of 1752 byte(s) in 42 object(s) allocated from:
    #0 0x5c0d6e832a29 in aligned_alloc /b/sanitizer-x86_64-linux/build/llvm-project/compiler-rt/lib/hwasan/hwasan_allocation_functions.cpp:58:3
    #1 0x5c0d6e921897 in llvm::allocate_buffer(unsigned long, unsigned long) llvm-link

Indirect leak of 780 byte(s) in 1 object(s) allocated from:
    #0 0x5c0d6e83306d in calloc /b/sanitizer-x86_64-linux/build/llvm-project/compiler-rt/lib/hwasan/hwasan_allocation_functions.cpp:127:3
    #1 0x5c0d6e913f8d in llvm::safe_calloc(unsigned long, unsigned long) llvm-link

Indirect leak of 160 byte(s) in 1 object(s) allocated from:
    #0 0x5c0d6e833528 in malloc /b/sanitizer-x86_64-linux/build/llvm-project/compiler-rt/lib/hwasan/hwasan_allocation_functions.cpp:151:3
    #1 0x5c0d6e98b47e in operator new(unsigned long) llvm-link
    #2 0x5c0d6e829a14 in _start (/b/sanitizer-x86_64-linux/build/build_default/runtimes/runtimes-bins/compiler-rt/test/sanitizer_common/hwasan-x86_64-Linux/Posix/Output/fork_threaded.c.tmp+0xeea14)

SUMMARY: HWAddressSanitizer: 3464 byte(s) leaked in 48 allocation(s).
==1153114==ERROR: HWAddressSanitizer: tag-mismatch on address 0x481400005140 at pc 0x5c0d6e83edf7
READ of size 155 at 0x481400005140 tags: 03/00 (ptr/mem) in thread T2

@H-G-Hristov H-G-Hristov deleted the hgh/libcxx/P1132R8-out_ptr-a-scalable-output-pointer-abstraction branch July 21, 2024 16:02
yuxuanchen1997 pushed a commit that referenced this pull request Jul 25, 2024
…action (#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 <[email protected]>
{
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));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drive-by comment: Is this cast actually strictly legal? See #133445

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.

Projects

None yet

6 participants