[libc-commits] [libc] [libc] Implement cpp::unique_ptr utility (PR #206701)

Jeff Bailey via libc-commits libc-commits at lists.llvm.org
Wed Jul 15 07:30:34 PDT 2026


https://github.com/kaladron updated https://github.com/llvm/llvm-project/pull/206701

>From b7df37837d53ad87ef72c760c229664642a9f9cb Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jbailey at raspberryginger.com>
Date: Tue, 30 Jun 2026 09:56:26 +0100
Subject: [PATCH 1/2] [libc] Implement cpp::unique_ptr utility

Implemented cpp::unique_ptr and cpp::default_delete in
libc/src/__support/CPP/unique_ptr.h for internal use within LLVM-libc.

* Added cpp::default_delete and its array specialization.
* Implemented cpp::unique_ptr with support for custom deleters and
  array specialization.
* Included is_assignable.h in type_traits.h to support conversion
  assignment constraints.
* Added comprehensive unit tests in
  libc/test/src/__support/CPP/unique_ptr_test.cpp.

This is a simplified implementation with the following limitations:
* No empty member optimization (empty deleters still occupy space).
* No support for custom pointer types (hardcoded to T*).
* No support for reference deleter types or array conversion.

Assisted-by: Automated tooling, human reviewed.
---
 libc/src/__support/CPP/CMakeLists.txt         |  12 +
 libc/src/__support/CPP/type_traits.h          |   8 +-
 libc/src/__support/CPP/unique_ptr.h           | 214 ++++++++++++++++++
 libc/test/src/__support/CPP/CMakeLists.txt    |  20 +-
 .../src/__support/CPP/unique_ptr_test.cpp     | 146 ++++++++++++
 5 files changed, 394 insertions(+), 6 deletions(-)
 create mode 100644 libc/src/__support/CPP/unique_ptr.h
 create mode 100644 libc/test/src/__support/CPP/unique_ptr_test.cpp

diff --git a/libc/src/__support/CPP/CMakeLists.txt b/libc/src/__support/CPP/CMakeLists.txt
index b602aa1f79d08..a7f04d8b8e66f 100644
--- a/libc/src/__support/CPP/CMakeLists.txt
+++ b/libc/src/__support/CPP/CMakeLists.txt
@@ -201,6 +201,18 @@ add_header_library(
     expected.h
 )
 
+add_header_library(
+  unique_ptr
+  HDRS
+    unique_ptr.h
+  DEPENDS
+    .new
+    .type_traits
+    .utility
+    libc.src.__support.macros.attributes
+    libc.src.__support.macros.config
+)
+
 add_object_library(
   new
   SRCS
diff --git a/libc/src/__support/CPP/type_traits.h b/libc/src/__support/CPP/type_traits.h
index d48ee23aeae07..4bf5acdccd927 100644
--- a/libc/src/__support/CPP/type_traits.h
+++ b/libc/src/__support/CPP/type_traits.h
@@ -1,10 +1,15 @@
-//===-- Self contained C++ type_traits --------------------------*- 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
 //
 //===----------------------------------------------------------------------===//
+///
+/// \file
+/// Self contained C++ type_traits.
+///
+//===----------------------------------------------------------------------===//
 
 #ifndef LLVM_LIBC_SRC___SUPPORT_CPP_TYPE_TRAITS_H
 #define LLVM_LIBC_SRC___SUPPORT_CPP_TYPE_TRAITS_H
@@ -24,6 +29,7 @@
 #include "src/__support/CPP/type_traits/invoke_result.h"
 #include "src/__support/CPP/type_traits/is_arithmetic.h"
 #include "src/__support/CPP/type_traits/is_array.h"
+#include "src/__support/CPP/type_traits/is_assignable.h"
 #include "src/__support/CPP/type_traits/is_base_of.h"
 #include "src/__support/CPP/type_traits/is_class.h"
 #include "src/__support/CPP/type_traits/is_complex.h"
diff --git a/libc/src/__support/CPP/unique_ptr.h b/libc/src/__support/CPP/unique_ptr.h
new file mode 100644
index 0000000000000..cb1fae0a5e601
--- /dev/null
+++ b/libc/src/__support/CPP/unique_ptr.h
@@ -0,0 +1,214 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Standalone implementation of std::unique_ptr.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_CPP_UNIQUE_PTR_H
+#define LLVM_LIBC_SRC___SUPPORT_CPP_UNIQUE_PTR_H
+
+#include "src/__support/CPP/new.h"
+#include "src/__support/CPP/type_traits.h"
+#include "src/__support/CPP/utility.h"
+#include "src/__support/macros/attributes.h"
+#include "src/__support/macros/config.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace cpp {
+
+/// A wrapper for deleting objects by default.
+template <typename T> struct default_delete {
+  LIBC_INLINE constexpr default_delete() = default;
+
+  template <typename U, typename = typename enable_if<
+                            is_convertible<U *, T *>::value>::type>
+  LIBC_INLINE constexpr default_delete(const default_delete<U> &) {}
+
+  LIBC_INLINE constexpr void operator()(T *ptr) const {
+    static_assert(sizeof(T) > 0, "cannot delete an incomplete type");
+    delete ptr;
+  }
+};
+
+/// A wrapper for deleting array objects by default.
+template <typename T> struct default_delete<T[]> {
+  LIBC_INLINE constexpr default_delete() = default;
+
+  template <typename U, typename = typename enable_if<
+                            is_convertible<U (*)[], T (*)[]>::value>::type>
+  LIBC_INLINE constexpr default_delete(const default_delete<U[]> &) {}
+
+  template <typename U, typename = typename enable_if<
+                            is_convertible<U (*)[], T (*)[]>::value>::type>
+  LIBC_INLINE constexpr void operator()(U *ptr) const {
+    static_assert(sizeof(U) > 0, "cannot delete an incomplete type");
+    delete[] ptr;
+  }
+};
+
+/// A smart pointer that owns and manages another object through a pointer.
+template <typename T, typename Deleter = default_delete<T>> class unique_ptr {
+  T *ptr_ = nullptr;
+  Deleter deleter_;
+
+  template <typename U, typename E> friend class unique_ptr;
+
+public:
+  using element_type = T;
+  using deleter_type = Deleter;
+  using pointer = T *;
+
+  LIBC_INLINE constexpr unique_ptr() = default;
+  LIBC_INLINE constexpr unique_ptr(decltype(nullptr)) : ptr_(nullptr) {}
+  LIBC_INLINE constexpr explicit unique_ptr(pointer p) : ptr_(p) {}
+
+  LIBC_INLINE constexpr unique_ptr(pointer p, const deleter_type &d)
+      : ptr_(p), deleter_(d) {}
+  LIBC_INLINE constexpr unique_ptr(pointer p, deleter_type &&d)
+      : ptr_(p), deleter_(move(d)) {}
+
+  // Move constructor
+  LIBC_INLINE constexpr unique_ptr(unique_ptr &&other)
+      : ptr_(other.release()), deleter_(forward<Deleter>(other.get_deleter())) {
+  }
+
+  // Move assignment
+  LIBC_INLINE constexpr unique_ptr &operator=(unique_ptr &&other) {
+    reset(other.release());
+    deleter_ = forward<Deleter>(other.get_deleter());
+    return *this;
+  }
+
+  // Conversion move constructor
+  template <
+      typename U, typename E,
+      typename = typename enable_if<
+          is_convertible<typename unique_ptr<U, E>::pointer, pointer>::value &&
+          !is_array<U>::value && is_convertible<E, Deleter>::value>::type>
+  LIBC_INLINE constexpr unique_ptr(unique_ptr<U, E> &&other)
+      : ptr_(other.release()), deleter_(forward<E>(other.get_deleter())) {}
+
+  // Conversion move assignment
+  template <
+      typename U, typename E,
+      typename = typename enable_if<
+          is_convertible<typename unique_ptr<U, E>::pointer, pointer>::value &&
+          !is_array<U>::value && is_assignable<Deleter &, E &&>::value>::type>
+  LIBC_INLINE constexpr unique_ptr &operator=(unique_ptr<U, E> &&other) {
+    reset(other.release());
+    deleter_ = forward<E>(other.get_deleter());
+    return *this;
+  }
+
+  // Disable copy
+  unique_ptr(const unique_ptr &) = delete;
+  unique_ptr &operator=(const unique_ptr &) = delete;
+
+  LIBC_INLINE ~unique_ptr() { reset(); }
+
+  LIBC_INLINE constexpr pointer get() const { return ptr_; }
+  LIBC_INLINE constexpr deleter_type &get_deleter() { return deleter_; }
+  LIBC_INLINE constexpr const deleter_type &get_deleter() const {
+    return deleter_;
+  }
+
+  LIBC_INLINE constexpr explicit operator bool() const {
+    return ptr_ != nullptr;
+  }
+
+  LIBC_INLINE constexpr pointer release() {
+    pointer temp = ptr_;
+    ptr_ = nullptr;
+    return temp;
+  }
+
+  LIBC_INLINE constexpr void reset(pointer p = pointer()) {
+    pointer old_ptr = ptr_;
+    ptr_ = p;
+    if (old_ptr)
+      deleter_(old_ptr);
+  }
+
+  LIBC_INLINE constexpr typename add_lvalue_reference<T>::type
+  operator*() const {
+    return *ptr_;
+  }
+  LIBC_INLINE constexpr pointer operator->() const { return ptr_; }
+};
+
+/// A smart pointer that owns and manages an array of objects through a pointer.
+template <typename T, typename Deleter> class unique_ptr<T[], Deleter> {
+  T *ptr_ = nullptr;
+  Deleter deleter_;
+
+  template <typename U, typename E> friend class unique_ptr;
+
+public:
+  using element_type = T;
+  using deleter_type = Deleter;
+  using pointer = T *;
+
+  LIBC_INLINE constexpr unique_ptr() = default;
+  LIBC_INLINE constexpr unique_ptr(decltype(nullptr)) : ptr_(nullptr) {}
+  LIBC_INLINE constexpr explicit unique_ptr(pointer p) : ptr_(p) {}
+
+  LIBC_INLINE constexpr unique_ptr(pointer p, const deleter_type &d)
+      : ptr_(p), deleter_(d) {}
+  LIBC_INLINE constexpr unique_ptr(pointer p, deleter_type &&d)
+      : ptr_(p), deleter_(move(d)) {}
+
+  // Move constructor
+  LIBC_INLINE constexpr unique_ptr(unique_ptr &&other)
+      : ptr_(other.release()), deleter_(forward<Deleter>(other.get_deleter())) {
+  }
+
+  // Move assignment
+  LIBC_INLINE constexpr unique_ptr &operator=(unique_ptr &&other) {
+    reset(other.release());
+    deleter_ = forward<Deleter>(other.get_deleter());
+    return *this;
+  }
+
+  // Disable copy
+  unique_ptr(const unique_ptr &) = delete;
+  unique_ptr &operator=(const unique_ptr &) = delete;
+
+  LIBC_INLINE ~unique_ptr() { reset(); }
+
+  LIBC_INLINE constexpr pointer get() const { return ptr_; }
+  LIBC_INLINE constexpr deleter_type &get_deleter() { return deleter_; }
+  LIBC_INLINE constexpr const deleter_type &get_deleter() const {
+    return deleter_;
+  }
+
+  LIBC_INLINE constexpr explicit operator bool() const {
+    return ptr_ != nullptr;
+  }
+
+  LIBC_INLINE constexpr pointer release() {
+    pointer temp = ptr_;
+    ptr_ = nullptr;
+    return temp;
+  }
+
+  LIBC_INLINE constexpr void reset(pointer p = pointer()) {
+    pointer old_ptr = ptr_;
+    ptr_ = p;
+    if (old_ptr)
+      deleter_(old_ptr);
+  }
+
+  LIBC_INLINE constexpr T &operator[](size_t i) const { return ptr_[i]; }
+};
+
+} // namespace cpp
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_CPP_UNIQUE_PTR_H
diff --git a/libc/test/src/__support/CPP/CMakeLists.txt b/libc/test/src/__support/CPP/CMakeLists.txt
index 9c09684be192d..97171ddf7c996 100644
--- a/libc/test/src/__support/CPP/CMakeLists.txt
+++ b/libc/test/src/__support/CPP/CMakeLists.txt
@@ -97,7 +97,6 @@ add_libc_test(
     libc.src.__support.CPP.utility
 )
 
-
 # This test fails with invalid address space operations on sm_60
 if(NOT LIBC_TARGET_ARCHITECTURE_IS_NVPTX)
   add_libc_test(
@@ -132,6 +131,17 @@ add_libc_test(
     libc.src.__support.CPP.optional
 )
 
+add_libc_test(
+  unique_ptr_test
+  SUITE
+    libc-cpp-utils-tests
+  SRCS
+    unique_ptr_test.cpp
+  DEPENDS
+    libc.src.__support.CPP.unique_ptr
+    libc.src.__support.CPP.utility
+)
+
 add_libc_test(
   tuple_test
   SUITE
@@ -169,8 +179,8 @@ add_libc_test(
   SRCS
     string_test.cpp
   DEPENDS
-  libc.src.__support.CPP.string
-  libc.src.__support.CPP.string_view
+    libc.src.__support.CPP.string
+    libc.src.__support.CPP.string_view
 )
 
 add_libc_test(
@@ -178,9 +188,9 @@ add_libc_test(
   SUITE
     libc-cpp-utils-tests
   SRCS
-  type_traits_test.cpp
+    type_traits_test.cpp
   DEPENDS
-  libc.src.__support.CPP.type_traits
+    libc.src.__support.CPP.type_traits
 )
 
 if(LIBC_COMPILER_HAS_EXT_VECTOR_TYPE)
diff --git a/libc/test/src/__support/CPP/unique_ptr_test.cpp b/libc/test/src/__support/CPP/unique_ptr_test.cpp
new file mode 100644
index 0000000000000..768aa22e07ced
--- /dev/null
+++ b/libc/test/src/__support/CPP/unique_ptr_test.cpp
@@ -0,0 +1,146 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unittests for UniquePtr.
+///
+//===----------------------------------------------------------------------===//
+
+#include "src/__support/CPP/unique_ptr.h"
+#include "src/__support/CPP/utility.h"
+#include "test/UnitTest/Test.h"
+
+using LIBC_NAMESPACE::cpp::unique_ptr;
+
+struct DestructTracker {
+  int *destruct_count = nullptr;
+  DestructTracker() = default;
+  DestructTracker(int *count) : destruct_count(count) {}
+  ~DestructTracker() {
+    if (destruct_count)
+      (*destruct_count)++;
+  }
+};
+
+// Test basic construction, ownership, and destruction.
+TEST(LlvmLibcUniquePtrTest, Basic) {
+  int destruct_count = 0;
+  {
+    unique_ptr<DestructTracker> ptr(new DestructTracker(&destruct_count));
+    ASSERT_TRUE(static_cast<bool>(ptr));
+    ASSERT_EQ(destruct_count, 0);
+  }
+  ASSERT_EQ(destruct_count, 1);
+}
+
+// Test nullptr construction and behavior.
+TEST(LlvmLibcUniquePtrTest, Nullptr) {
+  unique_ptr<int> ptr(nullptr);
+  ASSERT_FALSE(static_cast<bool>(ptr));
+  ASSERT_EQ(ptr.get(), static_cast<int *>(nullptr));
+}
+
+// Test move construction transferring ownership.
+TEST(LlvmLibcUniquePtrTest, Move) {
+  int destruct_count = 0;
+  {
+    unique_ptr<DestructTracker> ptr1(new DestructTracker(&destruct_count));
+    unique_ptr<DestructTracker> ptr2(LIBC_NAMESPACE::cpp::move(ptr1));
+    ASSERT_FALSE(static_cast<bool>(ptr1));
+    ASSERT_TRUE(static_cast<bool>(ptr2));
+    ASSERT_EQ(destruct_count, 0);
+  }
+  ASSERT_EQ(destruct_count, 1);
+}
+
+// Test move assignment transferring ownership and releasing old resource.
+TEST(LlvmLibcUniquePtrTest, MoveAssignment) {
+  int destruct_count1 = 0;
+  int destruct_count2 = 0;
+  {
+    unique_ptr<DestructTracker> ptr1(new DestructTracker(&destruct_count1));
+    unique_ptr<DestructTracker> ptr2(new DestructTracker(&destruct_count2));
+    ptr2 = LIBC_NAMESPACE::cpp::move(ptr1);
+    ASSERT_FALSE(static_cast<bool>(ptr1));
+    ASSERT_TRUE(static_cast<bool>(ptr2));
+    ASSERT_EQ(destruct_count1, 0);
+    ASSERT_EQ(destruct_count2, 1); // ptr2's original object should be destroyed
+  }
+  ASSERT_EQ(destruct_count1, 1);
+}
+
+// Test release of ownership without destroying the object.
+TEST(LlvmLibcUniquePtrTest, Release) {
+  int destruct_count = 0;
+  DestructTracker *raw_ptr = nullptr;
+  {
+    unique_ptr<DestructTracker> ptr(new DestructTracker(&destruct_count));
+    raw_ptr = ptr.release();
+    ASSERT_FALSE(static_cast<bool>(ptr));
+    ASSERT_EQ(destruct_count, 0);
+  }
+  ASSERT_EQ(destruct_count, 0);
+  delete raw_ptr;
+  ASSERT_EQ(destruct_count, 1);
+}
+
+// Test reset replacing the owned object and destroying the old one.
+TEST(LlvmLibcUniquePtrTest, Reset) {
+  int destruct_count1 = 0;
+  int destruct_count2 = 0;
+  {
+    unique_ptr<DestructTracker> ptr(new DestructTracker(&destruct_count1));
+    ptr.reset(new DestructTracker(&destruct_count2));
+    ASSERT_EQ(destruct_count1, 1);
+    ASSERT_EQ(destruct_count2, 0);
+  }
+  ASSERT_EQ(destruct_count2, 1);
+}
+
+// Test dereference operators (operator* and operator->).
+TEST(LlvmLibcUniquePtrTest, Dereference) {
+  struct Foo {
+    int val;
+  };
+  unique_ptr<Foo> ptr(new Foo{42});
+  ASSERT_EQ((*ptr).val, 42);
+  ASSERT_EQ(ptr->val, 42);
+}
+
+// Test array specialization behavior and destruction.
+TEST(LlvmLibcUniquePtrTest, Array) {
+  int destruct_count = 0;
+  {
+    unique_ptr<DestructTracker[]> ptr(new DestructTracker[3]);
+    ptr[0].destruct_count = &destruct_count;
+    ptr[1].destruct_count = &destruct_count;
+    ptr[2].destruct_count = &destruct_count;
+    ASSERT_TRUE(static_cast<bool>(ptr));
+    ASSERT_EQ(destruct_count, 0);
+  }
+  ASSERT_EQ(destruct_count, 3);
+}
+
+struct CustomDeleter {
+  int *count;
+  void operator()(int *p) const {
+    (*count)++;
+    delete p;
+  }
+};
+
+// Test support for custom deleters.
+TEST(LlvmLibcUniquePtrTest, CustomDeleter) {
+  int deleter_count = 0;
+  {
+    unique_ptr<int, CustomDeleter> ptr(new int(42),
+                                       CustomDeleter{&deleter_count});
+    ASSERT_EQ(deleter_count, 0);
+  }
+  ASSERT_EQ(deleter_count, 1);
+}

>From 8e0f5ac71e9c7019766f63974d58945e5a7fb50f Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jbailey at raspberryginger.com>
Date: Wed, 15 Jul 2026 07:57:24 +0100
Subject: [PATCH 2/2] [libc] Address review feedback for cpp::unique_ptr

Addressed review comments from PR 206701:
* Defined LIBC_NO_UNIQUE_ADDRESS and LIBC_TRIVIAL_ABI macros in attributes.h.
* Applied LIBC_TRIVIAL_ABI to unique_ptr and LIBC_NO_UNIQUE_ADDRESS to deleter.
* Renamed member variables to remove trailing underscores (ptr, deleter).
* Removed redundant friend class declaration.
* Modernized type traits usage (enable_if_t, is_convertible_v).
* Added tests for reset sequencing and type conversions.

Assisted-by: Automated tooling, human reviewed.
---
 libc/src/__support/CPP/unique_ptr.h           | 113 +++++++++---------
 libc/src/__support/macros/attributes.h        |  16 +++
 .../src/__support/CPP/unique_ptr_test.cpp     |  47 ++++++++
 3 files changed, 117 insertions(+), 59 deletions(-)

diff --git a/libc/src/__support/CPP/unique_ptr.h b/libc/src/__support/CPP/unique_ptr.h
index cb1fae0a5e601..552f9232f2dcc 100644
--- a/libc/src/__support/CPP/unique_ptr.h
+++ b/libc/src/__support/CPP/unique_ptr.h
@@ -27,8 +27,7 @@ namespace cpp {
 template <typename T> struct default_delete {
   LIBC_INLINE constexpr default_delete() = default;
 
-  template <typename U, typename = typename enable_if<
-                            is_convertible<U *, T *>::value>::type>
+  template <typename U, typename = enable_if_t<is_convertible_v<U *, T *>>>
   LIBC_INLINE constexpr default_delete(const default_delete<U> &) {}
 
   LIBC_INLINE constexpr void operator()(T *ptr) const {
@@ -41,12 +40,12 @@ template <typename T> struct default_delete {
 template <typename T> struct default_delete<T[]> {
   LIBC_INLINE constexpr default_delete() = default;
 
-  template <typename U, typename = typename enable_if<
-                            is_convertible<U (*)[], T (*)[]>::value>::type>
+  template <typename U,
+            typename = enable_if_t<is_convertible_v<U (*)[], T (*)[]>>>
   LIBC_INLINE constexpr default_delete(const default_delete<U[]> &) {}
 
-  template <typename U, typename = typename enable_if<
-                            is_convertible<U (*)[], T (*)[]>::value>::type>
+  template <typename U,
+            typename = enable_if_t<is_convertible_v<U (*)[], T (*)[]>>>
   LIBC_INLINE constexpr void operator()(U *ptr) const {
     static_assert(sizeof(U) > 0, "cannot delete an incomplete type");
     delete[] ptr;
@@ -54,11 +53,10 @@ template <typename T> struct default_delete<T[]> {
 };
 
 /// A smart pointer that owns and manages another object through a pointer.
-template <typename T, typename Deleter = default_delete<T>> class unique_ptr {
-  T *ptr_ = nullptr;
-  Deleter deleter_;
-
-  template <typename U, typename E> friend class unique_ptr;
+template <typename T, typename Deleter = default_delete<T>>
+class LIBC_TRIVIAL_ABI unique_ptr {
+  T *ptr = nullptr;
+  LIBC_NO_UNIQUE_ADDRESS Deleter deleter;
 
 public:
   using element_type = T;
@@ -66,44 +64,43 @@ template <typename T, typename Deleter = default_delete<T>> class unique_ptr {
   using pointer = T *;
 
   LIBC_INLINE constexpr unique_ptr() = default;
-  LIBC_INLINE constexpr unique_ptr(decltype(nullptr)) : ptr_(nullptr) {}
-  LIBC_INLINE constexpr explicit unique_ptr(pointer p) : ptr_(p) {}
+  LIBC_INLINE constexpr unique_ptr(decltype(nullptr)) : ptr(nullptr) {}
+  LIBC_INLINE constexpr explicit unique_ptr(pointer p) : ptr(p) {}
 
   LIBC_INLINE constexpr unique_ptr(pointer p, const deleter_type &d)
-      : ptr_(p), deleter_(d) {}
+      : ptr(p), deleter(d) {}
   LIBC_INLINE constexpr unique_ptr(pointer p, deleter_type &&d)
-      : ptr_(p), deleter_(move(d)) {}
+      : ptr(p), deleter(move(d)) {}
 
   // Move constructor
   LIBC_INLINE constexpr unique_ptr(unique_ptr &&other)
-      : ptr_(other.release()), deleter_(forward<Deleter>(other.get_deleter())) {
-  }
+      : ptr(other.release()), deleter(forward<Deleter>(other.get_deleter())) {}
 
   // Move assignment
   LIBC_INLINE constexpr unique_ptr &operator=(unique_ptr &&other) {
     reset(other.release());
-    deleter_ = forward<Deleter>(other.get_deleter());
+    deleter = forward<Deleter>(other.get_deleter());
     return *this;
   }
 
   // Conversion move constructor
   template <
       typename U, typename E,
-      typename = typename enable_if<
-          is_convertible<typename unique_ptr<U, E>::pointer, pointer>::value &&
-          !is_array<U>::value && is_convertible<E, Deleter>::value>::type>
+      typename = enable_if_t<
+          is_convertible_v<typename unique_ptr<U, E>::pointer, pointer> &&
+          !is_array<U>::value && is_convertible_v<E, Deleter>>>
   LIBC_INLINE constexpr unique_ptr(unique_ptr<U, E> &&other)
-      : ptr_(other.release()), deleter_(forward<E>(other.get_deleter())) {}
+      : ptr(other.release()), deleter(forward<E>(other.get_deleter())) {}
 
   // Conversion move assignment
   template <
       typename U, typename E,
-      typename = typename enable_if<
-          is_convertible<typename unique_ptr<U, E>::pointer, pointer>::value &&
-          !is_array<U>::value && is_assignable<Deleter &, E &&>::value>::type>
+      typename = enable_if_t<
+          is_convertible_v<typename unique_ptr<U, E>::pointer, pointer> &&
+          !is_array<U>::value && is_assignable_v<Deleter &, E &&>>>
   LIBC_INLINE constexpr unique_ptr &operator=(unique_ptr<U, E> &&other) {
     reset(other.release());
-    deleter_ = forward<E>(other.get_deleter());
+    deleter = forward<E>(other.get_deleter());
     return *this;
   }
 
@@ -113,42 +110,41 @@ template <typename T, typename Deleter = default_delete<T>> class unique_ptr {
 
   LIBC_INLINE ~unique_ptr() { reset(); }
 
-  LIBC_INLINE constexpr pointer get() const { return ptr_; }
-  LIBC_INLINE constexpr deleter_type &get_deleter() { return deleter_; }
+  LIBC_INLINE constexpr pointer get() const { return ptr; }
+  LIBC_INLINE constexpr deleter_type &get_deleter() { return deleter; }
   LIBC_INLINE constexpr const deleter_type &get_deleter() const {
-    return deleter_;
+    return deleter;
   }
 
   LIBC_INLINE constexpr explicit operator bool() const {
-    return ptr_ != nullptr;
+    return ptr != nullptr;
   }
 
   LIBC_INLINE constexpr pointer release() {
-    pointer temp = ptr_;
-    ptr_ = nullptr;
+    pointer temp = ptr;
+    ptr = nullptr;
     return temp;
   }
 
   LIBC_INLINE constexpr void reset(pointer p = pointer()) {
-    pointer old_ptr = ptr_;
-    ptr_ = p;
+    pointer old_ptr = ptr;
+    ptr = p;
     if (old_ptr)
-      deleter_(old_ptr);
+      deleter(old_ptr);
   }
 
   LIBC_INLINE constexpr typename add_lvalue_reference<T>::type
   operator*() const {
-    return *ptr_;
+    return *ptr;
   }
-  LIBC_INLINE constexpr pointer operator->() const { return ptr_; }
+  LIBC_INLINE constexpr pointer operator->() const { return ptr; }
 };
 
 /// A smart pointer that owns and manages an array of objects through a pointer.
-template <typename T, typename Deleter> class unique_ptr<T[], Deleter> {
-  T *ptr_ = nullptr;
-  Deleter deleter_;
-
-  template <typename U, typename E> friend class unique_ptr;
+template <typename T, typename Deleter>
+class LIBC_TRIVIAL_ABI unique_ptr<T[], Deleter> {
+  T *ptr = nullptr;
+  LIBC_NO_UNIQUE_ADDRESS Deleter deleter;
 
 public:
   using element_type = T;
@@ -156,23 +152,22 @@ template <typename T, typename Deleter> class unique_ptr<T[], Deleter> {
   using pointer = T *;
 
   LIBC_INLINE constexpr unique_ptr() = default;
-  LIBC_INLINE constexpr unique_ptr(decltype(nullptr)) : ptr_(nullptr) {}
-  LIBC_INLINE constexpr explicit unique_ptr(pointer p) : ptr_(p) {}
+  LIBC_INLINE constexpr unique_ptr(decltype(nullptr)) : ptr(nullptr) {}
+  LIBC_INLINE constexpr explicit unique_ptr(pointer p) : ptr(p) {}
 
   LIBC_INLINE constexpr unique_ptr(pointer p, const deleter_type &d)
-      : ptr_(p), deleter_(d) {}
+      : ptr(p), deleter(d) {}
   LIBC_INLINE constexpr unique_ptr(pointer p, deleter_type &&d)
-      : ptr_(p), deleter_(move(d)) {}
+      : ptr(p), deleter(move(d)) {}
 
   // Move constructor
   LIBC_INLINE constexpr unique_ptr(unique_ptr &&other)
-      : ptr_(other.release()), deleter_(forward<Deleter>(other.get_deleter())) {
-  }
+      : ptr(other.release()), deleter(forward<Deleter>(other.get_deleter())) {}
 
   // Move assignment
   LIBC_INLINE constexpr unique_ptr &operator=(unique_ptr &&other) {
     reset(other.release());
-    deleter_ = forward<Deleter>(other.get_deleter());
+    deleter = forward<Deleter>(other.get_deleter());
     return *this;
   }
 
@@ -182,30 +177,30 @@ template <typename T, typename Deleter> class unique_ptr<T[], Deleter> {
 
   LIBC_INLINE ~unique_ptr() { reset(); }
 
-  LIBC_INLINE constexpr pointer get() const { return ptr_; }
-  LIBC_INLINE constexpr deleter_type &get_deleter() { return deleter_; }
+  LIBC_INLINE constexpr pointer get() const { return ptr; }
+  LIBC_INLINE constexpr deleter_type &get_deleter() { return deleter; }
   LIBC_INLINE constexpr const deleter_type &get_deleter() const {
-    return deleter_;
+    return deleter;
   }
 
   LIBC_INLINE constexpr explicit operator bool() const {
-    return ptr_ != nullptr;
+    return ptr != nullptr;
   }
 
   LIBC_INLINE constexpr pointer release() {
-    pointer temp = ptr_;
-    ptr_ = nullptr;
+    pointer temp = ptr;
+    ptr = nullptr;
     return temp;
   }
 
   LIBC_INLINE constexpr void reset(pointer p = pointer()) {
-    pointer old_ptr = ptr_;
-    ptr_ = p;
+    pointer old_ptr = ptr;
+    ptr = p;
     if (old_ptr)
-      deleter_(old_ptr);
+      deleter(old_ptr);
   }
 
-  LIBC_INLINE constexpr T &operator[](size_t i) const { return ptr_[i]; }
+  LIBC_INLINE constexpr T &operator[](size_t i) const { return ptr[i]; }
 };
 
 } // namespace cpp
diff --git a/libc/src/__support/macros/attributes.h b/libc/src/__support/macros/attributes.h
index cffcabcd29bd3..d04e0ce79529f 100644
--- a/libc/src/__support/macros/attributes.h
+++ b/libc/src/__support/macros/attributes.h
@@ -24,6 +24,10 @@
 #define __has_attribute(x) 0
 #endif
 
+#ifndef __has_cpp_attribute
+#define __has_cpp_attribute(x) 0
+#endif
+
 #define LIBC_INLINE inline
 #define LIBC_INLINE_VAR inline
 #define LIBC_INLINE_ASM __asm__ __volatile__
@@ -148,6 +152,18 @@ LIBC_THREAD_MODE_EXTERNAL.
 #define LIBC_PREFERED_TYPE(TYPE)
 #endif
 
+#if __has_cpp_attribute(no_unique_address)
+#define LIBC_NO_UNIQUE_ADDRESS [[no_unique_address]]
+#else
+#define LIBC_NO_UNIQUE_ADDRESS
+#endif
+
+#if __has_attribute(trivial_abi)
+#define LIBC_TRIVIAL_ABI [[clang::trivial_abi]]
+#else
+#define LIBC_TRIVIAL_ABI
+#endif
+
 #if __has_attribute(ext_vector_type) &&                                        \
     LIBC_HAS_FEATURE(ext_vector_type_boolean)
 #define LIBC_HAS_VECTOR_TYPE 1
diff --git a/libc/test/src/__support/CPP/unique_ptr_test.cpp b/libc/test/src/__support/CPP/unique_ptr_test.cpp
index 768aa22e07ced..4cc475733a959 100644
--- a/libc/test/src/__support/CPP/unique_ptr_test.cpp
+++ b/libc/test/src/__support/CPP/unique_ptr_test.cpp
@@ -144,3 +144,50 @@ TEST(LlvmLibcUniquePtrTest, CustomDeleter) {
   }
   ASSERT_EQ(deleter_count, 1);
 }
+
+struct ReentrancyTracker {
+  unique_ptr<ReentrancyTracker> *owner;
+  bool *checked;
+  ReentrancyTracker(unique_ptr<ReentrancyTracker> *o, bool *c)
+      : owner(o), checked(c) {}
+  ~ReentrancyTracker() {
+    if (owner)
+      *checked = (owner->get() == nullptr);
+  }
+};
+
+// Test that reset() clears the pointer before invoking the deleter.
+TEST(LlvmLibcUniquePtrTest, ResetSequencing) {
+  bool checked = false;
+  unique_ptr<ReentrancyTracker> ptr;
+  ptr.reset(new ReentrancyTracker(&ptr, &checked));
+  ptr.reset(); // trigger destructor
+  ASSERT_TRUE(checked);
+}
+
+// Test conversion constructor for const types.
+TEST(LlvmLibcUniquePtrTest, ConstConversion) {
+  unique_ptr<int> ptr1(new int(42));
+  unique_ptr<const int> ptr2(LIBC_NAMESPACE::cpp::move(ptr1));
+  ASSERT_FALSE(static_cast<bool>(ptr1));
+  ASSERT_TRUE(static_cast<bool>(ptr2));
+  ASSERT_EQ(*ptr2, 42);
+}
+
+struct Base {
+  int val;
+  virtual ~Base() = default;
+  Base(int v) : val(v) {}
+};
+struct Derived : public Base {
+  Derived(int v) : Base(v) {}
+};
+
+// Test conversion constructor for base/derived types.
+TEST(LlvmLibcUniquePtrTest, BaseDerivedConversion) {
+  unique_ptr<Derived> derived_ptr(new Derived(100));
+  unique_ptr<Base> base_ptr(LIBC_NAMESPACE::cpp::move(derived_ptr));
+  ASSERT_FALSE(static_cast<bool>(derived_ptr));
+  ASSERT_TRUE(static_cast<bool>(base_ptr));
+  ASSERT_EQ(base_ptr->val, 100);
+}



More information about the libc-commits mailing list