[libc-commits] [libc] Reland "[libc] Implement cpp::unique_ptr utility" (PR #233800)
Pavel Labath via libc-commits
libc-commits at lists.llvm.org
Tue Oct 20 06:48:59 PDT 2026
https://github.com/labath updated https://github.com/llvm/llvm-project/pull/233800
>From 433c21d82900c76d480673377ccbc76fe46fe9f3 Mon Sep 17 00:00:00 2001
From: Jeff Bailey <jbailey at raspberryginger.com>
Date: Tue, 28 Jul 2026 21:03:40 +0100
Subject: [PATCH 1/4] [libc] Implement cpp::unique_ptr utility (#206701)
[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.
* Applied LIBC_TRIVIAL_ABI to unique_ptr and LIBC_NO_UNIQUE_ADDRESS to
the
deleter to enable empty member optimization and register-passing ABI.
* Defined portable LIBC_NO_UNIQUE_ADDRESS and LIBC_TRIVIAL_ABI macros in
attributes.h.
* Included is_assignable.h in type_traits.h to support conversion
assignment constraints.
* Modernized template constraints using enable_if_t and
is_convertible_v.
* Added comprehensive unit tests in
libc/test/src/__support/CPP/unique_ptr_test.cpp, including coverage for
reset sequencing, custom deleters, and conversion constructors.
This is a simplified implementation with the following limitations:
* No support for custom pointer types (hardcoded to T*).
* No support for reference deleter types or array conversion.
Adding this to LLVM libc because some things (like regex) require
dynamic
memory allocation, and this will help with correct memory management.
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 | 207 +++++++++++++++++
libc/src/__support/macros/attributes.h | 16 ++
libc/test/src/__support/CPP/CMakeLists.txt | 11 +
.../src/__support/CPP/unique_ptr_test.cpp | 214 ++++++++++++++++++
6 files changed, 467 insertions(+), 1 deletion(-)
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 6777c6a27c0cc..3b2899d0cdc6d 100644
--- a/libc/src/__support/CPP/CMakeLists.txt
+++ b/libc/src/__support/CPP/CMakeLists.txt
@@ -212,6 +212,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..cdb44cdf12c96
--- /dev/null
+++ b/libc/src/__support/CPP/unique_ptr.h
@@ -0,0 +1,207 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 = 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 {
+ 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 = enable_if_t<is_convertible_v<U (*)[], T (*)[]>>>
+ LIBC_INLINE constexpr default_delete(const default_delete<U[]> &) {}
+
+ 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;
+ }
+};
+
+/// A smart pointer that owns and manages another object through a pointer.
+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;
+ 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 = 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())) {}
+
+ // Conversion move assignment
+ template <typename U, typename E,
+ 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());
+ 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 LIBC_TRIVIAL_ABI unique_ptr<T[], Deleter> {
+ T *ptr = nullptr;
+ LIBC_NO_UNIQUE_ADDRESS Deleter deleter;
+
+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/src/__support/macros/attributes.h b/libc/src/__support/macros/attributes.h
index e251f917d9850..9f6da6f91cb9c 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__
@@ -164,6 +168,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/CMakeLists.txt b/libc/test/src/__support/CPP/CMakeLists.txt
index e962487a87a43..5e2321a2ae7cf 100644
--- a/libc/test/src/__support/CPP/CMakeLists.txt
+++ b/libc/test/src/__support/CPP/CMakeLists.txt
@@ -155,6 +155,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
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..d0cbab04414f9
--- /dev/null
+++ b/libc/test/src/__support/CPP/unique_ptr_test.cpp
@@ -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
+/// 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);
+}
+
+struct CustomArrayDeleter {
+ int *count;
+ void operator()(int *p) const {
+ if (count)
+ (*count)++;
+ delete[] p;
+ }
+};
+
+// Test support for custom deleters on array unique_ptr.
+TEST(LlvmLibcUniquePtrTest, CustomArrayDeleter) {
+ int deleter_count = 0;
+ {
+ CustomArrayDeleter d{&deleter_count};
+ unique_ptr<int[], CustomArrayDeleter> ptr(new int[3], d);
+ ASSERT_EQ(deleter_count, 0);
+ ASSERT_EQ(ptr.get_deleter().count, d.count);
+ }
+ 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);
+}
>From 2c80591232750146d96392f1a4d2cab6ecb6afcb Mon Sep 17 00:00:00 2001
From: Pavel Labath <pavel at labath.sk>
Date: Tue, 20 Oct 2026 11:12:23 +0000
Subject: [PATCH 2/4] fix asan
---
.../src/__support/CPP/unique_ptr_test.cpp | 56 ++++++++++++++-----
1 file changed, 42 insertions(+), 14 deletions(-)
diff --git a/libc/test/src/__support/CPP/unique_ptr_test.cpp b/libc/test/src/__support/CPP/unique_ptr_test.cpp
index d0cbab04414f9..9bae256eaa6f4 100644
--- a/libc/test/src/__support/CPP/unique_ptr_test.cpp
+++ b/libc/test/src/__support/CPP/unique_ptr_test.cpp
@@ -13,8 +13,10 @@
#include "src/__support/CPP/unique_ptr.h"
#include "src/__support/CPP/utility.h"
+#include "src/__support/alloc-checker.h"
#include "test/UnitTest/Test.h"
+using LIBC_NAMESPACE::AllocChecker;
using LIBC_NAMESPACE::cpp::unique_ptr;
struct DestructTracker {
@@ -31,7 +33,9 @@ struct DestructTracker {
TEST(LlvmLibcUniquePtrTest, Basic) {
int destruct_count = 0;
{
- unique_ptr<DestructTracker> ptr(new DestructTracker(&destruct_count));
+ AllocChecker ac;
+ unique_ptr<DestructTracker> ptr(new (ac) DestructTracker(&destruct_count));
+ ASSERT_TRUE(static_cast<bool>(ac));
ASSERT_TRUE(static_cast<bool>(ptr));
ASSERT_EQ(destruct_count, 0);
}
@@ -49,7 +53,9 @@ TEST(LlvmLibcUniquePtrTest, Nullptr) {
TEST(LlvmLibcUniquePtrTest, Move) {
int destruct_count = 0;
{
- unique_ptr<DestructTracker> ptr1(new DestructTracker(&destruct_count));
+ AllocChecker ac;
+ unique_ptr<DestructTracker> ptr1(new (ac) DestructTracker(&destruct_count));
+ ASSERT_TRUE(static_cast<bool>(ac));
unique_ptr<DestructTracker> ptr2(LIBC_NAMESPACE::cpp::move(ptr1));
ASSERT_FALSE(static_cast<bool>(ptr1));
ASSERT_TRUE(static_cast<bool>(ptr2));
@@ -63,8 +69,11 @@ 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));
+ AllocChecker ac;
+ unique_ptr<DestructTracker> ptr1(new (ac) DestructTracker(&destruct_count1));
+ ASSERT_TRUE(static_cast<bool>(ac));
+ unique_ptr<DestructTracker> ptr2(new (ac) DestructTracker(&destruct_count2));
+ ASSERT_TRUE(static_cast<bool>(ac));
ptr2 = LIBC_NAMESPACE::cpp::move(ptr1);
ASSERT_FALSE(static_cast<bool>(ptr1));
ASSERT_TRUE(static_cast<bool>(ptr2));
@@ -79,7 +88,9 @@ TEST(LlvmLibcUniquePtrTest, Release) {
int destruct_count = 0;
DestructTracker *raw_ptr = nullptr;
{
- unique_ptr<DestructTracker> ptr(new DestructTracker(&destruct_count));
+ AllocChecker ac;
+ unique_ptr<DestructTracker> ptr(new (ac) DestructTracker(&destruct_count));
+ ASSERT_TRUE(static_cast<bool>(ac));
raw_ptr = ptr.release();
ASSERT_FALSE(static_cast<bool>(ptr));
ASSERT_EQ(destruct_count, 0);
@@ -94,8 +105,11 @@ 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));
+ AllocChecker ac;
+ unique_ptr<DestructTracker> ptr(new (ac) DestructTracker(&destruct_count1));
+ ASSERT_TRUE(static_cast<bool>(ac));
+ ptr.reset(new (ac) DestructTracker(&destruct_count2));
+ ASSERT_TRUE(static_cast<bool>(ac));
ASSERT_EQ(destruct_count1, 1);
ASSERT_EQ(destruct_count2, 0);
}
@@ -107,7 +121,9 @@ TEST(LlvmLibcUniquePtrTest, Dereference) {
struct Foo {
int val;
};
- unique_ptr<Foo> ptr(new Foo{42});
+ AllocChecker ac;
+ unique_ptr<Foo> ptr(new (ac) Foo{42});
+ ASSERT_TRUE(static_cast<bool>(ac));
ASSERT_EQ((*ptr).val, 42);
ASSERT_EQ(ptr->val, 42);
}
@@ -116,7 +132,9 @@ TEST(LlvmLibcUniquePtrTest, Dereference) {
TEST(LlvmLibcUniquePtrTest, Array) {
int destruct_count = 0;
{
- unique_ptr<DestructTracker[]> ptr(new DestructTracker[3]);
+ AllocChecker ac;
+ unique_ptr<DestructTracker[]> ptr(new (ac) DestructTracker[3]);
+ ASSERT_TRUE(static_cast<bool>(ac));
ptr[0].destruct_count = &destruct_count;
ptr[1].destruct_count = &destruct_count;
ptr[2].destruct_count = &destruct_count;
@@ -138,8 +156,10 @@ struct CustomDeleter {
TEST(LlvmLibcUniquePtrTest, CustomDeleter) {
int deleter_count = 0;
{
- unique_ptr<int, CustomDeleter> ptr(new int(42),
+ AllocChecker ac;
+ unique_ptr<int, CustomDeleter> ptr(new (ac) int(42),
CustomDeleter{&deleter_count});
+ ASSERT_TRUE(static_cast<bool>(ac));
ASSERT_EQ(deleter_count, 0);
}
ASSERT_EQ(deleter_count, 1);
@@ -158,8 +178,10 @@ struct CustomArrayDeleter {
TEST(LlvmLibcUniquePtrTest, CustomArrayDeleter) {
int deleter_count = 0;
{
+ AllocChecker ac;
CustomArrayDeleter d{&deleter_count};
- unique_ptr<int[], CustomArrayDeleter> ptr(new int[3], d);
+ unique_ptr<int[], CustomArrayDeleter> ptr(new (ac) int[3], d);
+ ASSERT_TRUE(static_cast<bool>(ac));
ASSERT_EQ(deleter_count, 0);
ASSERT_EQ(ptr.get_deleter().count, d.count);
}
@@ -181,14 +203,18 @@ struct ReentrancyTracker {
TEST(LlvmLibcUniquePtrTest, ResetSequencing) {
bool checked = false;
unique_ptr<ReentrancyTracker> ptr;
- ptr.reset(new ReentrancyTracker(&ptr, &checked));
+ AllocChecker ac;
+ ptr.reset(new (ac) ReentrancyTracker(&ptr, &checked));
+ ASSERT_TRUE(static_cast<bool>(ac));
ptr.reset(); // trigger destructor
ASSERT_TRUE(checked);
}
// Test conversion constructor for const types.
TEST(LlvmLibcUniquePtrTest, ConstConversion) {
- unique_ptr<int> ptr1(new int(42));
+ AllocChecker ac;
+ unique_ptr<int> ptr1(new (ac) int(42));
+ ASSERT_TRUE(static_cast<bool>(ac));
unique_ptr<const int> ptr2(LIBC_NAMESPACE::cpp::move(ptr1));
ASSERT_FALSE(static_cast<bool>(ptr1));
ASSERT_TRUE(static_cast<bool>(ptr2));
@@ -206,7 +232,9 @@ struct Derived : public Base {
// Test conversion constructor for base/derived types.
TEST(LlvmLibcUniquePtrTest, BaseDerivedConversion) {
- unique_ptr<Derived> derived_ptr(new Derived(100));
+ AllocChecker ac;
+ unique_ptr<Derived> derived_ptr(new (ac) Derived(100));
+ ASSERT_TRUE(static_cast<bool>(ac));
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));
>From b83d9024498c909e1f15d784657799b672d5ba17 Mon Sep 17 00:00:00 2001
From: Pavel Labath <pavel at labath.sk>
Date: Tue, 20 Oct 2026 12:28:21 +0000
Subject: [PATCH 3/4] format
---
libc/test/src/__support/CPP/unique_ptr_test.cpp | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/libc/test/src/__support/CPP/unique_ptr_test.cpp b/libc/test/src/__support/CPP/unique_ptr_test.cpp
index 9bae256eaa6f4..ec483f95a613d 100644
--- a/libc/test/src/__support/CPP/unique_ptr_test.cpp
+++ b/libc/test/src/__support/CPP/unique_ptr_test.cpp
@@ -70,9 +70,11 @@ TEST(LlvmLibcUniquePtrTest, MoveAssignment) {
int destruct_count2 = 0;
{
AllocChecker ac;
- unique_ptr<DestructTracker> ptr1(new (ac) DestructTracker(&destruct_count1));
+ unique_ptr<DestructTracker> ptr1(new (ac)
+ DestructTracker(&destruct_count1));
ASSERT_TRUE(static_cast<bool>(ac));
- unique_ptr<DestructTracker> ptr2(new (ac) DestructTracker(&destruct_count2));
+ unique_ptr<DestructTracker> ptr2(new (ac)
+ DestructTracker(&destruct_count2));
ASSERT_TRUE(static_cast<bool>(ac));
ptr2 = LIBC_NAMESPACE::cpp::move(ptr1);
ASSERT_FALSE(static_cast<bool>(ptr1));
>From 38d84411365308402d726f7168b61895c47a0ccc Mon Sep 17 00:00:00 2001
From: Pavel Labath <pavel at labath.sk>
Date: Tue, 20 Oct 2026 13:48:34 +0000
Subject: [PATCH 4/4] fixes
---
libc/src/__support/CPP/CMakeLists.txt | 1 +
libc/src/__support/CPP/unique_ptr.h | 1 +
libc/test/src/__support/CPP/unique_ptr_test.cpp | 2 +-
3 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/libc/src/__support/CPP/CMakeLists.txt b/libc/src/__support/CPP/CMakeLists.txt
index 3b2899d0cdc6d..89e35062909e5 100644
--- a/libc/src/__support/CPP/CMakeLists.txt
+++ b/libc/src/__support/CPP/CMakeLists.txt
@@ -220,6 +220,7 @@ add_header_library(
.new
.type_traits
.utility
+ libc.hdr.types.size_t
libc.src.__support.macros.attributes
libc.src.__support.macros.config
)
diff --git a/libc/src/__support/CPP/unique_ptr.h b/libc/src/__support/CPP/unique_ptr.h
index cdb44cdf12c96..ebba21299f4b8 100644
--- a/libc/src/__support/CPP/unique_ptr.h
+++ b/libc/src/__support/CPP/unique_ptr.h
@@ -14,6 +14,7 @@
#ifndef LLVM_LIBC_SRC___SUPPORT_CPP_UNIQUE_PTR_H
#define LLVM_LIBC_SRC___SUPPORT_CPP_UNIQUE_PTR_H
+#include "hdr/types/size_t.h"
#include "src/__support/CPP/new.h"
#include "src/__support/CPP/type_traits.h"
#include "src/__support/CPP/utility.h"
diff --git a/libc/test/src/__support/CPP/unique_ptr_test.cpp b/libc/test/src/__support/CPP/unique_ptr_test.cpp
index ec483f95a613d..1064ae36de827 100644
--- a/libc/test/src/__support/CPP/unique_ptr_test.cpp
+++ b/libc/test/src/__support/CPP/unique_ptr_test.cpp
@@ -46,7 +46,7 @@ TEST(LlvmLibcUniquePtrTest, Basic) {
TEST(LlvmLibcUniquePtrTest, Nullptr) {
unique_ptr<int> ptr(nullptr);
ASSERT_FALSE(static_cast<bool>(ptr));
- ASSERT_EQ(ptr.get(), static_cast<int *>(nullptr));
+ ASSERT_EQ(ptr.get(), nullptr);
}
// Test move construction transferring ownership.
More information about the libc-commits
mailing list