[libc-commits] [libc] 1bb06d2 - Reland "[libc] Implement cpp::unique_ptr utility" (#233800)

via libc-commits libc-commits at lists.llvm.org
Wed Oct 21 02:20:20 PDT 2026


Author: Pavel Labath
Date: 2026-10-21T11:20:15+02:00
New Revision: 1bb06d275e73291c19117e03e3406508fed88b7a

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

LOG: Reland "[libc] Implement cpp::unique_ptr utility" (#233800)

This patch reapplies #206701 (reverted in #212644 due to failures on the
asan bots). The asan failures (allocation mismatches) are fixed by
changing the test to use new(AllocChecker) for allocating memory.

The original commit message was:

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.

---------

Co-authored-by: Jeff Bailey <jbailey at raspberryginger.com>

Added: 
    libc/src/__support/CPP/unique_ptr.h
    libc/test/src/__support/CPP/unique_ptr_test.cpp

Modified: 
    libc/src/__support/CPP/CMakeLists.txt
    libc/src/__support/CPP/type_traits.h
    libc/src/__support/macros/attributes.h
    libc/test/src/__support/CPP/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/libc/src/__support/CPP/CMakeLists.txt b/libc/src/__support/CPP/CMakeLists.txt
index 6777c6a27c0ccd..89e35062909e57 100644
--- a/libc/src/__support/CPP/CMakeLists.txt
+++ b/libc/src/__support/CPP/CMakeLists.txt
@@ -212,6 +212,19 @@ add_header_library(
     expected.h
 )
 
+add_header_library(
+  unique_ptr
+  HDRS
+    unique_ptr.h
+  DEPENDS
+    .new
+    .type_traits
+    .utility
+    libc.hdr.types.size_t
+    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 d48ee23aeae070..4bf5acdccd927d 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 00000000000000..ebba21299f4b80
--- /dev/null
+++ b/libc/src/__support/CPP/unique_ptr.h
@@ -0,0 +1,208 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "hdr/types/size_t.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 e251f917d9850b..9f6da6f91cb9c0 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 e962487a87a43a..5e2321a2ae7cf2 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 00000000000000..1064ae36de8272
--- /dev/null
+++ b/libc/test/src/__support/CPP/unique_ptr_test.cpp
@@ -0,0 +1,244 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "src/__support/alloc-checker.h"
+#include "test/UnitTest/Test.h"
+
+using LIBC_NAMESPACE::AllocChecker;
+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;
+  {
+    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);
+  }
+  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(), nullptr);
+}
+
+// Test move construction transferring ownership.
+TEST(LlvmLibcUniquePtrTest, Move) {
+  int destruct_count = 0;
+  {
+    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));
+    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;
+  {
+    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));
+    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;
+  {
+    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);
+  }
+  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;
+  {
+    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);
+  }
+  ASSERT_EQ(destruct_count2, 1);
+}
+
+// Test dereference operators (operator* and operator->).
+TEST(LlvmLibcUniquePtrTest, Dereference) {
+  struct Foo {
+    int val;
+  };
+  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);
+}
+
+// Test array specialization behavior and destruction.
+TEST(LlvmLibcUniquePtrTest, Array) {
+  int destruct_count = 0;
+  {
+    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;
+    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;
+  {
+    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);
+}
+
+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;
+  {
+    AllocChecker ac;
+    CustomArrayDeleter d{&deleter_count};
+    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);
+  }
+  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;
+  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) {
+  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));
+  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) {
+  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));
+  ASSERT_EQ(base_ptr->val, 100);
+}


        


More information about the libc-commits mailing list