[libcxx-commits] [libcxx] [libc++] Add tombstone traits and use them in optional (PR #98498)

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Fri Nov 7 06:03:46 PST 2025


https://github.com/philnik777 updated https://github.com/llvm/llvm-project/pull/98498

>From 65f0ffa178d5d7b85334d01208b9c355a4cb8b7e Mon Sep 17 00:00:00 2001
From: Nikolas Klauser <nikolasklauser at berlin.de>
Date: Sun, 9 Jun 2024 10:35:22 +0200
Subject: [PATCH] [libc++] Add tombstone traits to use in optional, variant

---
 libcxx/include/CMakeLists.txt                 |   1 +
 libcxx/include/__configuration/abi.h          |   4 +
 .../include/__functional/reference_wrapper.h  |  24 +-
 libcxx/include/__locale                       |  15 +-
 libcxx/include/__memory/shared_ptr.h          |  25 +-
 libcxx/include/__memory/tombstone_traits.h    | 289 ++++++++++++++++++
 libcxx/include/__memory/unique_ptr.h          |  21 ++
 libcxx/include/__type_traits/enable_if.h      |   3 +
 libcxx/include/__type_traits/is_fundamental.h |   1 +
 libcxx/include/__utility/pair.h               |  22 ++
 libcxx/include/__vector/vector.h              |  12 +
 libcxx/include/module.modulemap.in            |   4 +
 libcxx/include/optional                       |  67 +++-
 libcxx/include/string                         |  53 +++-
 libcxx/include/string_view                    |   9 +
 libcxx/src/locale.cpp                         |   2 +
 .../test/libcxx/transitive_includes/cxx03.csv |   3 +
 .../test/libcxx/transitive_includes/cxx11.csv |   4 +
 .../test/libcxx/transitive_includes/cxx14.csv |   4 +
 .../test/libcxx/transitive_includes/cxx17.csv |   4 +
 .../test/libcxx/transitive_includes/cxx20.csv |   6 +
 .../test/libcxx/transitive_includes/cxx23.csv |   4 +
 .../test/libcxx/transitive_includes/cxx26.csv |   4 +
 .../optional.object/optional_size.pass.cpp    |  31 ++
 .../optional/tombstone_types.pass.cpp         |  68 +++++
 25 files changed, 664 insertions(+), 16 deletions(-)
 create mode 100644 libcxx/include/__memory/tombstone_traits.h
 create mode 100644 libcxx/test/std/utilities/optional/tombstone_types.pass.cpp

diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 57032ce26d4fd..3f632647f4653 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -596,6 +596,7 @@ set(files
   __memory/swap_allocator.h
   __memory/temp_value.h
   __memory/temporary_buffer.h
+  __memory/tombstone_traits.h
   __memory/uninitialized_algorithms.h
   __memory/unique_ptr.h
   __memory/unique_temporary_buffer.h
diff --git a/libcxx/include/__configuration/abi.h b/libcxx/include/__configuration/abi.h
index 38b85c6ac70de..0a919287fab0d 100644
--- a/libcxx/include/__configuration/abi.h
+++ b/libcxx/include/__configuration/abi.h
@@ -83,6 +83,7 @@
 #  define _LIBCPP_ABI_USE_WRAP_ITER_IN_STD_ARRAY
 #  define _LIBCPP_ABI_USE_WRAP_ITER_IN_STD_STRING_VIEW
 #  define _LIBCPP_ABI_VARIANT_INDEX_TYPE_OPTIMIZATION
+#  define _LIBCPP_ABI_OPTIONAL_USE_TOMBSTONE_TRAITS
 
 #elif _LIBCPP_ABI_VERSION == 1
 #  if !(defined(_LIBCPP_OBJECT_FORMAT_COFF) || defined(_LIBCPP_OBJECT_FORMAT_XCOFF))
@@ -101,6 +102,9 @@
 #  if defined(__FreeBSD__)
 #    define _LIBCPP_DEPRECATED_ABI_DISABLE_PAIR_TRIVIAL_COPY_CTOR
 #  endif
+
+// TODO: This shouldn't be in the final commit - this just to test the changes across all the different configurations
+#  define _LIBCPP_ABI_OPTIONAL_USE_TOMBSTONE_TRAITS
 #endif
 
 // TODO(LLVM 22): Remove this check
diff --git a/libcxx/include/__functional/reference_wrapper.h b/libcxx/include/__functional/reference_wrapper.h
index 148703b21d84a..c759215b156bd 100644
--- a/libcxx/include/__functional/reference_wrapper.h
+++ b/libcxx/include/__functional/reference_wrapper.h
@@ -13,8 +13,10 @@
 #include <__compare/synth_three_way.h>
 #include <__concepts/convertible_to.h>
 #include <__config>
+#include <__cstddef/size_t.h>
 #include <__functional/weak_result_type.h>
 #include <__memory/addressof.h>
+#include <__memory/tombstone_traits.h>
 #include <__type_traits/common_reference.h>
 #include <__type_traits/desugars_to.h>
 #include <__type_traits/enable_if.h>
@@ -125,7 +127,27 @@ class reference_wrapper : public __weak_result_type<_Tp> {
 #if _LIBCPP_STD_VER >= 17
 template <class _Tp>
 reference_wrapper(_Tp&) -> reference_wrapper<_Tp>;
-#endif
+
+template <class _Tp>
+struct __tombstone_traits<reference_wrapper<_Tp>> {
+  // reference_wrapper<_Tp> is conceptually that same as _Tp& and can only be constructed from a _Tp&. Since references
+  // can never be null, reference_wrapper can also never be null, allowing us to use it as an invalid state.
+
+  template <class _Payload, size_t _Np>
+  struct __representation;
+
+  template <class _Payload>
+  struct __representation<_Payload, 0> {
+    template <class... _Args>
+    constexpr __representation(_Args&&... __args) : __payload_(std::forward<_Args>(__args)...), __value_(nullptr) {}
+
+    bool __is_tombstone() { return __value_ == nullptr; }
+
+    _LIBCPP_NO_UNIQUE_ADDRESS _Payload __payload_;
+    _Tp* __value_;
+  };
+};
+#endif // _LIBCPP_STD_VER >= 17
 
 template <class _Tp>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 reference_wrapper<_Tp> ref(_Tp& __t) _NOEXCEPT {
diff --git a/libcxx/include/__locale b/libcxx/include/__locale
index eb7b7786208e8..4a061cd85ef9c 100644
--- a/libcxx/include/__locale
+++ b/libcxx/include/__locale
@@ -17,6 +17,7 @@
 #  include <__locale_dir/locale_base_api.h>
 #  include <__memory/addressof.h>
 #  include <__memory/shared_count.h>
+#  include <__memory/tombstone_traits.h>
 #  include <__mutex/once_flag.h>
 #  include <__type_traits/make_unsigned.h>
 #  include <__utility/no_destroy.h>
@@ -113,8 +114,9 @@ public:
   static locale global(const locale&);
   static const locale& classic();
 
-private:
   class __imp;
+
+private:
   __imp* __locale_;
 
   template <class>
@@ -130,7 +132,18 @@ private:
   friend bool has_facet(const locale&) _NOEXCEPT;
   template <class _Facet>
   friend const _Facet& use_facet(const locale&);
+
+  friend struct __tombstone_traits<locale>;
+};
+
+#if _LIBCPP_STD_VER >= 17
+// Derived from the void* specialization, since __imp is never defined.
+template <>
+struct __tombstone_traits<locale> : __tombstone_traits_assume_aligned_pointer<void*> {
+  static_assert(__builtin_offsetof(locale, __locale_) == 0);
+  static_assert(sizeof(locale) == sizeof(void*));
 };
+#endif
 
 class _LIBCPP_EXPORTED_FROM_ABI locale::facet : public __shared_count {
 protected:
diff --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h
index e90db587d2836..e531d5769aeeb 100644
--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -32,6 +32,7 @@
 #include <__memory/destroy.h>
 #include <__memory/pointer_traits.h>
 #include <__memory/shared_count.h>
+#include <__memory/tombstone_traits.h>
 #include <__memory/uninitialized_algorithms.h>
 #include <__memory/unique_ptr.h>
 #include <__type_traits/add_reference.h>
@@ -689,14 +690,25 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI shared_ptr {
   friend class shared_ptr;
   template <class _Up>
   friend class weak_ptr;
+
+  friend struct __tombstone_traits<shared_ptr<_Tp> >;
 };
 
-#if _LIBCPP_STD_VER >= 17
+#if _LIBCPP_STD_VER >= 17 && 0
 template <class _Tp>
 shared_ptr(weak_ptr<_Tp>) -> shared_ptr<_Tp>;
 template <class _Tp, class _Dp>
 shared_ptr(unique_ptr<_Tp, _Dp>) -> shared_ptr<_Tp>;
-#endif
+
+template <class _Tp>
+struct __tombstone_traits<shared_ptr<_Tp>> {
+  static constexpr auto __disengaged_value_ =
+      __tombstone_traits_assume_aligned_pointer<__shared_weak_count*>::__disengaged_value_;
+  static constexpr size_t __is_disengaged_offset_ =
+      __builtin_offsetof(shared_ptr<_Tp>, __cntrl_) +
+      __tombstone_traits_assume_aligned_pointer<__shared_weak_count*>::__is_disengaged_offset_;
+};
+#endif // _LIBCPP_STD_VER >= 17
 
 //
 // std::allocate_shared and std::make_shared
@@ -1242,12 +1254,19 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI weak_ptr {
   friend class weak_ptr;
   template <class _Up>
   friend class shared_ptr;
+
+  friend struct __tombstone_traits<weak_ptr<_Tp> >;
 };
 
 #if _LIBCPP_STD_VER >= 17
 template <class _Tp>
 weak_ptr(shared_ptr<_Tp>) -> weak_ptr<_Tp>;
-#endif
+
+template <class _Tp>
+struct __tombstone_traits<weak_ptr<_Tp>> {
+
+};
+#endif // _LIBCPP_STD_VER >= 17
 
 template <class _Tp>
 inline _LIBCPP_CONSTEXPR weak_ptr<_Tp>::weak_ptr() _NOEXCEPT : __ptr_(nullptr), __cntrl_(nullptr) {}
diff --git a/libcxx/include/__memory/tombstone_traits.h b/libcxx/include/__memory/tombstone_traits.h
new file mode 100644
index 0000000000000..f354a25e69df5
--- /dev/null
+++ b/libcxx/include/__memory/tombstone_traits.h
@@ -0,0 +1,289 @@
+//===----------------------------------------------------------------------===//
+//
+// 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___TYPE_TRAITS_TOMBSTONE_TRAITS_H
+#define _LIBCPP___TYPE_TRAITS_TOMBSTONE_TRAITS_H
+
+#include <__assert>
+#include <__config>
+#include <__cstddef/size_t.h>
+#include <__memory/construct_at.h>
+#include <__type_traits/datasizeof.h>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/is_constant_evaluated.h>
+#include <__type_traits/is_fundamental.h>
+#include <__type_traits/is_integral.h>
+#include <__type_traits/is_trivial.h>
+#include <__type_traits/is_trivially_destructible.h>
+#include <__type_traits/remove_cv.h>
+#include <__type_traits/void_t.h>
+#include <__utility/forward.h>
+#include <__utility/in_place.h>
+#include <__utility/move.h>
+#include <cstdint>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <class>
+struct __tombstone_traits;
+
+#if _LIBCPP_STD_VER >= 17
+
+// bools always have exactly one bit set. If there is more than one set it's disengaged.
+template <>
+struct __tombstone_traits<bool> {
+  template <class _Payload, size_t _Np>
+  struct __representation {
+    template <class... _Args>
+    constexpr __representation(_Args&&... __args) : __payload_(std::forward<_Args>(__args)...), __value_(_Np + 2) {}
+
+    bool __is_tombstone() { return __value_ == _Np + 2; }
+
+    _LIBCPP_NO_UNIQUE_ADDRESS _Payload __payload_;
+    unsigned char __value_;
+  };
+
+  static constexpr size_t __max_tombstone = 254;
+};
+
+template <size_t _PaddingSize>
+struct __padding {
+  char __padding_[_PaddingSize];
+};
+
+template <>
+struct __padding<0> {};
+
+// Pointers to a type that has an alignment greater than one always have the lowest bits set to zero. This is a single
+// implementation for all the places where we have an invalid pointer to _Tp as the "invalid state" representation.
+template <class _Tp>
+struct __tombstone_traits_assume_aligned_pointer {
+  template <class _Payload, size_t _Np>
+  struct __representation {
+    template <class... _Args>
+    constexpr __representation(_Args&&... __args) : __payload_(std::forward<_Args>(__args)...), __value_(_Np + 1) {}
+
+    bool __is_tombstone() { return __value_ == _Np + 1; }
+
+    _LIBCPP_NO_UNIQUE_ADDRESS _Payload __payload_;
+    _LIBCPP_NO_UNIQUE_ADDRESS __padding<sizeof(_Tp*) - 1 - __datasizeof_v<_Payload>> __padding_;
+    unsigned char __value_;
+  };
+
+  static constexpr size_t __max_tombstone = 0; // TODO: Calculate this correctly
+};
+
+// TODO: Look into
+// - filesystem::directory_iterator
+// - vector<T> with alignof(T) == 1
+// - string_view (basic_string_view<T> works with alignof(T) >= 2)
+
+// This is constrained on fundamental types because we might not always know the alignment of a user-defined type.
+// For example, in one TU there may only be a forward declaration and in another there is already the definition
+// available. If we made this optimization conditional on the completeness of the type this would result in a non-benign
+// ODR violation.
+template <class _Tp>
+struct __tombstone_traits<__enable_specialization_if<is_fundamental_v<_Tp> && alignof(_Tp) >= 2, _Tp*>>
+    : __tombstone_traits_assume_aligned_pointer<_Tp> {};
+
+template <class _Tp>
+struct __tombstone_traits<_Tp**> : __tombstone_traits_assume_aligned_pointer<_Tp*> {
+  static_assert(alignof(_Tp*) >= 2, "alignment of a pointer isn't at least 2!?");
+};
+
+inline constexpr struct __init_engaged_t {
+} __init_engaged;
+inline constexpr struct __init_disengaged_t {
+} __init_disengaged;
+
+template <class _Tp, class = void>
+struct alignas(_Tp) __dummy_payload {
+  char __data_[__datasizeof_v<_Tp>];
+
+  static_assert(alignof(__dummy_payload) == alignof(_Tp));
+  static_assert(sizeof(__dummy_payload) == sizeof(_Tp));
+  static_assert(__datasizeof_v<__dummy_payload> == __datasizeof_v<_Tp>);
+};
+
+template <class _Tp>
+struct alignas(_Tp) __dummy_payload<_Tp, __enable_if_t<__datasizeof_v<_Tp> == 0>> {};
+
+template <class _Tp, class _Payload, bool = is_trivially_destructible_v<_Tp> && is_trivially_destructible_v<_Payload>>
+union _MaybeTombstone {
+  using _Tombstone      = __tombstone_traits<_Tp>::template __representation<_Payload, 0>;
+  using _DummyTombstone = __tombstone_traits<_Tp>::template __representation<__dummy_payload<_Payload>, 0>;
+
+  _Tp __value_;
+  _Tombstone __tombstone_;
+
+  template <class... _Args>
+  explicit constexpr _MaybeTombstone(__init_disengaged_t, _Args&&... __args)
+      : __tombstone_(std::forward<_Args>(__args)...) {}
+
+  template <class... _Args>
+  explicit constexpr _MaybeTombstone(__init_engaged_t, _Args&&... __args) : __value_(std::forward<_Args>(__args)...) {}
+
+  _MaybeTombstone(const _MaybeTombstone&)            = default;
+  _MaybeTombstone(_MaybeTombstone&&)                 = default;
+  _MaybeTombstone& operator=(const _MaybeTombstone&) = default;
+  _MaybeTombstone& operator=(_MaybeTombstone&&)      = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr bool __is_engaged() const noexcept {
+    if consteval {
+      return __builtin_is_within_lifetime(&__value_);
+    } else {
+      _DummyTombstone __dummy;
+      static_assert(sizeof(_DummyTombstone) <= sizeof(*this));
+      __builtin_memcpy(&__dummy, this, sizeof(_DummyTombstone));
+
+      return !__dummy.__is_tombstone();
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~_MaybeTombstone() {
+    if (__is_engaged()) {
+      std::destroy_at(&__value_);
+    } else {
+      std::destroy_at(&__tombstone_);
+    }
+  }
+};
+
+template <class _Tp, class _Payload>
+union _MaybeTombstone<_Tp, _Payload, true> {
+  using _Tombstone      = __tombstone_traits<_Tp>::template __representation<_Payload, 0>;
+  using _DummyTombstone = __tombstone_traits<_Tp>::template __representation<__dummy_payload<_Payload>, 0>;
+
+  _Tp __value_;
+  _Tombstone __tombstone_;
+
+  template <class... _Args>
+  explicit constexpr _MaybeTombstone(__init_disengaged_t, _Args&&... __args)
+      : __tombstone_(std::forward<_Args>(__args)...) {}
+
+  template <class... _Args>
+  explicit constexpr _MaybeTombstone(__init_engaged_t, _Args&&... __args) : __value_(std::forward<_Args>(__args)...) {}
+
+  _MaybeTombstone(const _MaybeTombstone&)            = default;
+  _MaybeTombstone(_MaybeTombstone&&)                 = default;
+  _MaybeTombstone& operator=(const _MaybeTombstone&) = default;
+  _MaybeTombstone& operator=(_MaybeTombstone&&)      = default;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr bool __is_engaged() const noexcept {
+    if consteval {
+      return __builtin_is_within_lifetime(&__value_);
+    } else {
+      _DummyTombstone __dummy;
+      static_assert(sizeof(_DummyTombstone) <= sizeof(*this));
+      __builtin_memcpy(&__dummy, this, sizeof(_DummyTombstone));
+
+      return !__dummy.__is_tombstone();
+    }
+  }
+
+  _LIBCPP_CONSTEXPR_SINCE_CXX20 ~_MaybeTombstone() = default;
+};
+
+template <class _Tp, class _Payload>
+struct __tombstoned_value final {
+  using _Tombstone      = __tombstone_traits<_Tp>::template __representation<_Payload, 0>;
+  using _DummyTombstone = __tombstone_traits<_Tp>::template __representation<__dummy_payload<_Payload>, 0>;
+
+  static_assert(__builtin_is_implicit_lifetime(_DummyTombstone));
+  static_assert(sizeof(_Tombstone) == sizeof(_DummyTombstone));
+  static_assert(alignof(_Tombstone) == alignof(_DummyTombstone));
+  static_assert(__datasizeof_v<_Tombstone> == __datasizeof_v<_DummyTombstone>);
+  static_assert(sizeof(_Tombstone) <= sizeof(_Tp), "Trying to use tobstone which can't hold T");
+
+  _MaybeTombstone<_Tp, _Payload> __data_;
+
+  template <class... _Args>
+  explicit _LIBCPP_HIDE_FROM_ABI constexpr __tombstoned_value(__init_disengaged_t, _Args&&... __args)
+      : __data_(__init_disengaged, std::forward<_Args>(__args)...) {}
+
+  template <class... _Args>
+  explicit _LIBCPP_HIDE_FROM_ABI constexpr __tombstoned_value(__init_engaged_t, _Args&&... __args)
+      : __data_(__init_engaged, std::forward<_Args>(__args)...) {}
+
+  _LIBCPP_HIDE_FROM_ABI constexpr bool __is_engaged() const noexcept { return __data_.__is_engaged(); }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto&& __get_value() & noexcept {
+    _LIBCPP_ASSERT_INTERNAL(__is_engaged(), "Trying to get the value of a disenagaged tombstoned value");
+    return __data_.__value_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto&& __get_value() const& noexcept {
+    _LIBCPP_ASSERT_INTERNAL(__is_engaged(), "Trying to get the value of a disenagaged tombstoned value");
+    return __data_.__value_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto&& __get_value() && noexcept {
+    _LIBCPP_ASSERT_INTERNAL(__is_engaged(), "Trying to get the value of a disenagaged tombstoned value");
+    return std::move(__data_.__value_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto&& __get_value() const&& noexcept {
+    _LIBCPP_ASSERT_INTERNAL(__is_engaged(), "Trying to get the value of a disenagaged tombstoned value");
+    return std::move(__data_.__value_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto&& __get_payload() & noexcept {
+    _LIBCPP_ASSERT_INTERNAL(!__is_engaged(), "Trying to get the payload of an enagaged tombstoned value");
+    return __data_.__tombstone_.__payload_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto&& __get_payload() const& noexcept {
+    _LIBCPP_ASSERT_INTERNAL(!__is_engaged(), "Trying to get the payload of an enagaged tombstoned value");
+    return __data_.__tombstone_.__payload_;
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto&& __get_payload() && noexcept {
+    _LIBCPP_ASSERT_INTERNAL(!__is_engaged(), "Trying to get the payload of an enagaged tombstoned value");
+    return std::move(__data_.__tombstone_.__payload_);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr auto&& __get_payload() const&& noexcept {
+    _LIBCPP_ASSERT_INTERNAL(!__is_engaged(), "Trying to get the payload of an enagaged tombstoned value");
+    return std::move(__data_.__tombstone_.__payload_);
+  }
+
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI constexpr void __engage(in_place_t, _Args&&... __args) {
+    _LIBCPP_ASSERT_INTERNAL(!__is_engaged(), "Trying to enage a already engaged tombstoned value");
+    std::destroy_at(&__data_.__tombstone_);
+    std::__construct_at(&__data_.__value_, std::forward<_Args>(__args)...);
+  }
+
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI constexpr void __disengage(in_place_t, _Args&&... __args) {
+    _LIBCPP_ASSERT_INTERNAL(!__is_engaged(), "Trying to disenage a disengaged tombstoned value");
+    std::destroy_at(&__data_.__value_);
+    std::__construct_at(&__data_.__tombstone_, std::forward<_Args>(__args)...);
+  }
+};
+
+template <class _Tp, class = void>
+inline constexpr bool __has_tombstone_v = false;
+
+template <class _Tp>
+inline constexpr bool __has_tombstone_v<_Tp, void_t<decltype(sizeof(__tombstone_traits<_Tp>))>> = true;
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___TYPE_TRAITS_TOMBSTONE_TRAITS_H
diff --git a/libcxx/include/__memory/unique_ptr.h b/libcxx/include/__memory/unique_ptr.h
index eff24546cdc01..f05a98ff9e577 100644
--- a/libcxx/include/__memory/unique_ptr.h
+++ b/libcxx/include/__memory/unique_ptr.h
@@ -24,6 +24,7 @@
 #include <__memory/auto_ptr.h>
 #include <__memory/compressed_pair.h>
 #include <__memory/pointer_traits.h>
+#include <__memory/tombstone_traits.h>
 #include <__type_traits/add_reference.h>
 #include <__type_traits/common_type.h>
 #include <__type_traits/conditional.h>
@@ -295,8 +296,18 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI unique_ptr {
     swap(__ptr_, __u.__ptr_);
     swap(__deleter_, __u.__deleter_);
   }
+
+  friend struct __tombstone_traits<unique_ptr>;
 };
 
+#if _LIBCPP_STD_VER >= 17
+template <class _Tp>
+struct __tombstone_traits<__enable_specialization_if<__has_tombstone_v<_Tp*>, unique_ptr<_Tp>>>
+    : __tombstone_traits<_Tp*> {
+  static_assert(__builtin_offsetof(unique_ptr<_Tp>, __ptr_) == 0, "Unexpected pointer offset");
+};
+#endif // _LIBCPP_STD_VER >= 17
+
 // Bounds checking in unique_ptr<T[]>
 // ==================================
 //
@@ -624,8 +635,18 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI unique_ptr<_Tp[], _Dp> {
     swap(__deleter_, __u.__deleter_);
     swap(__checker_, __u.__checker_);
   }
+
+  friend struct __tombstone_traits<unique_ptr>;
 };
 
+#if _LIBCPP_STD_VER >= 17
+template <class _Tp>
+struct __tombstone_traits<__enable_specialization_if<__has_tombstone_v<_Tp*>, unique_ptr<_Tp[]>>>
+    : __tombstone_traits<_Tp*> {
+  static_assert(__builtin_offsetof(unique_ptr<_Tp[]>, __ptr_) == 0, "Unexpected pointer offset");
+};
+#endif
+
 template <class _Tp, class _Dp, __enable_if_t<__is_swappable_v<_Dp>, int> = 0>
 inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX23 void
 swap(unique_ptr<_Tp, _Dp>& __x, unique_ptr<_Tp, _Dp>& __y) _NOEXCEPT {
diff --git a/libcxx/include/__type_traits/enable_if.h b/libcxx/include/__type_traits/enable_if.h
index ae1af6ebf17d9..b22d4e9dd6837 100644
--- a/libcxx/include/__type_traits/enable_if.h
+++ b/libcxx/include/__type_traits/enable_if.h
@@ -38,6 +38,9 @@ template <bool _Bp, class _Tp = void>
 using enable_if_t = typename enable_if<_Bp, _Tp>::type;
 #endif
 
+template <bool _Bp, class _Tp, class = __enable_if_t<_Bp> >
+using __enable_specialization_if = _Tp;
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP___TYPE_TRAITS_ENABLE_IF_H
diff --git a/libcxx/include/__type_traits/is_fundamental.h b/libcxx/include/__type_traits/is_fundamental.h
index 6236553cfb836..9449289368d75 100644
--- a/libcxx/include/__type_traits/is_fundamental.h
+++ b/libcxx/include/__type_traits/is_fundamental.h
@@ -11,6 +11,7 @@
 
 #include <__config>
 #include <__type_traits/integral_constant.h>
+#include <__type_traits/is_arithmetic.h>
 #include <__type_traits/is_null_pointer.h>
 #include <__type_traits/is_void.h>
 
diff --git a/libcxx/include/__utility/pair.h b/libcxx/include/__utility/pair.h
index 33694c52430f1..7b0a350908417 100644
--- a/libcxx/include/__utility/pair.h
+++ b/libcxx/include/__utility/pair.h
@@ -18,6 +18,7 @@
 #include <__fwd/array.h>
 #include <__fwd/pair.h>
 #include <__fwd/tuple.h>
+#include <__memory/tombstone_traits.h>
 #include <__tuple/tuple_like_no_subrange.h>
 #include <__tuple/tuple_size.h>
 #include <__type_traits/common_reference.h>
@@ -450,6 +451,27 @@ struct pair
 #if _LIBCPP_STD_VER >= 17
 template <class _T1, class _T2>
 pair(_T1, _T2) -> pair<_T1, _T2>;
+#endif // _LIBCPP_STD_VER >= 17
+
+#if 0
+template <class _Tp, class _Up, bool __first_tombstone>
+struct __tombstone_traits_pair;
+
+template <class _Tp, class _Up>
+struct __tombstone_traits_pair<_Tp, _Up, true> : __tombstone_traits<_Tp> {
+  static_assert(__builtin_offsetof(pair<_Tp, _Up>, first) == 0);
+};
+
+template <class _Tp, class _Up>
+struct __tombstone_traits_pair<_Tp, _Up, false> {
+  static constexpr auto __disengaged_value_ = __tombstone_traits<_Up>::__disengaged_value_;
+  static constexpr size_t __is_disengaged_offset_ =
+      __builtin_offsetof(pair<_Tp, _Up>, second) + __tombstone_traits<_Up>::__is_disengaged_offset_;
+};
+
+template <class _Tp, class _Up>
+struct __tombstone_traits<__enable_specialization_if<__has_tombstone_v<_Tp> || __has_tombstone_v<_Up>, pair<_Tp, _Up>>>
+    : __tombstone_traits_pair<_Tp, _Up, __has_tombstone_v<_Tp>> {};
 #endif
 
 // [pairs.spec], specialized algorithms
diff --git a/libcxx/include/__vector/vector.h b/libcxx/include/__vector/vector.h
index 316d3a9d10eff..68e8fba53a32c 100644
--- a/libcxx/include/__vector/vector.h
+++ b/libcxx/include/__vector/vector.h
@@ -40,6 +40,7 @@
 #include <__memory/pointer_traits.h>
 #include <__memory/swap_allocator.h>
 #include <__memory/temp_value.h>
+#include <__memory/tombstone_traits.h>
 #include <__memory/uninitialized_algorithms.h>
 #include <__ranges/access.h>
 #include <__ranges/concepts.h>
@@ -840,6 +841,8 @@ class vector {
     __sb.__set_valid_range(__vector_begin, __vector_sentinel);
     __sb.__set_capacity(__vector_cap);
   }
+
+  friend struct __tombstone_traits<vector<_Tp, _Allocator> >;
 };
 
 #if _LIBCPP_STD_VER >= 17
@@ -863,6 +866,15 @@ template <ranges::input_range _Range,
 vector(from_range_t, _Range&&, _Alloc = _Alloc()) -> vector<ranges::range_value_t<_Range>, _Alloc>;
 #endif
 
+#if _LIBCPP_STD_VER >= 17
+template <class _Tp, class _Allocator>
+struct __tombstone_traits<
+    __enable_specialization_if<__has_tombstone_v<typename vector<_Tp, _Allocator>::pointer>, vector<_Tp, _Allocator>>>
+    : __tombstone_traits<typename vector<_Tp>::pointer> {
+  static_assert(__builtin_offsetof(vector<_Tp, _Allocator>, __begin_) == 0);
+};
+#endif
+
 // __swap_out_circular_buffer relocates the objects in [__begin_, __end_) into the front of __v and swaps the buffers of
 // *this and __v. It is assumed that __v provides space for exactly (__end_ - __begin_) objects in the front. This
 // function has a strong exception guarantee.
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 24a2fe761943a..4bb7dc89ca090 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1674,6 +1674,10 @@ module std [system] {
       header "__memory/temporary_buffer.h"
       export std.utility.pair // return type of std::get_temporary_buffer()
     }
+    module tombstone_traits                   {
+      header "__memory/tombstone_traits.h"
+      export std.utility.piecewise_construct
+    }
     module uninitialized_algorithms {
       header "__memory/uninitialized_algorithms.h"
       export std.utility.pair
diff --git a/libcxx/include/optional b/libcxx/include/optional
index ef1bfd3ec44c0..f38b2c0526aaf 100644
--- a/libcxx/include/optional
+++ b/libcxx/include/optional
@@ -210,6 +210,7 @@ namespace std {
 #  include <__iterator/wrap_iter.h>
 #  include <__memory/addressof.h>
 #  include <__memory/construct_at.h>
+#  include <__memory/tombstone_traits.h>
 #  include <__ranges/enable_view.h>
 #  include <__tuple/sfinae_helpers.h>
 #  include <__type_traits/add_pointer.h>
@@ -372,8 +373,19 @@ struct __optional_destruct_base<_Tp, true> {
   }
 };
 
-template <class _Tp, bool = is_reference<_Tp>::value>
-struct __optional_storage_base : __optional_destruct_base<_Tp> {
+template <class _Tp,
+          bool = is_reference<_Tp>::value,
+          bool =
+#  ifdef _LIBCPP_ABI_OPTIONAL_USE_TOMBSTONE_TRAITS
+              __has_tombstone_v<_Tp>
+#  else
+              false
+#  endif
+          >
+struct __optional_storage_base;
+
+template <class _Tp>
+struct __optional_storage_base<_Tp, false, false> : __optional_destruct_base<_Tp> {
   using __base _LIBCPP_NODEBUG = __optional_destruct_base<_Tp>;
   using value_type             = _Tp;
   using __base::__base;
@@ -416,7 +428,7 @@ struct __optional_storage_base : __optional_destruct_base<_Tp> {
 // be allowed in the future. For this reason, it has already been implemented
 // to ensure we can make the change in an ABI-compatible manner.
 template <class _Tp>
-struct __optional_storage_base<_Tp, true> {
+struct __optional_storage_base<_Tp, true, false> {
   using value_type                 = _Tp;
   using __raw_type _LIBCPP_NODEBUG = remove_reference_t<_Tp>;
   __raw_type* __value_;
@@ -484,6 +496,55 @@ struct __optional_storage_base<_Tp, true> {
   }
 };
 
+template <class _Tp, bool _Bp>
+struct __optional_storage_base<_Tp, _Bp, true> {
+  using value_type = _Tp;
+  __tombstoned_value<_Tp, __empty> __storage_;
+
+  _LIBCPP_HIDE_FROM_ABI constexpr __optional_storage_base() noexcept : __storage_(__init_disengaged) {}
+
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit __optional_storage_base(in_place_t, _Args&&... __args)
+      : __storage_(__init_engaged, std::forward<_Args>(__args)...) {}
+
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void reset() noexcept {
+    if (__storage_.__is_engaged())
+      __storage_.__disengage(in_place);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr bool has_value() const noexcept { return __storage_.__is_engaged(); }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr _Tp& __get() & { return __storage_.__get_value(); }
+  _LIBCPP_HIDE_FROM_ABI constexpr _Tp&& __get() && { return std::move(__storage_.__get_value()); }
+  _LIBCPP_HIDE_FROM_ABI constexpr const _Tp& __get() const& { return __storage_.__get_value(); }
+  _LIBCPP_HIDE_FROM_ABI constexpr const _Tp&& __get() const&& { return std::move(__storage_.__get_value()); }
+
+  template <class... _Args>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct(_Args&&... __args) {
+    _LIBCPP_ASSERT_INTERNAL(!has_value(), "__construct called for engaged __optional_storage_base");
+    __storage_.__engage(in_place, std::forward<_Args>(__args)...);
+  }
+
+  template <class _Other>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __construct_from(_Other&& __opt) {
+    if (__opt.has_value())
+      __construct(std::forward<_Other>(__opt).__get());
+  }
+
+  template <class _Other>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void __assign_from(_Other&& __opt) {
+    if (has_value() == __opt.has_value()) {
+      if (has_value())
+        __storage_.__get_value() = std::forward<_Other>(__opt).__get();
+    } else {
+      if (has_value())
+        __storage_.__disengage(in_place);
+      else
+        __construct(std::forward<_Other>(__opt).__get());
+    }
+  }
+};
+
 template <class _Tp, bool = is_trivially_copy_constructible<_Tp>::value>
 struct __optional_copy_base : __optional_storage_base<_Tp> {
   using __optional_storage_base<_Tp>::__optional_storage_base;
diff --git a/libcxx/include/string b/libcxx/include/string
index ede42467b99fe..dafe1dc00cb08 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -617,6 +617,7 @@ basic_string<char32_t> operator""s( const char32_t *str, size_t len );
 #  include <__memory/noexcept_move_assign_container.h>
 #  include <__memory/pointer_traits.h>
 #  include <__memory/swap_allocator.h>
+#  include <__memory/tombstone_traits.h>
 #  include <__memory_resource/polymorphic_allocator.h>
 #  include <__ranges/access.h>
 #  include <__ranges/concepts.h>
@@ -717,14 +718,6 @@ inline const bool __can_be_converted_to_string_view_v =
 struct __uninitialized_size_tag {};
 struct __init_with_sentinel_tag {};
 
-template <size_t _PaddingSize>
-struct __padding {
-  char __padding_[_PaddingSize];
-};
-
-template <>
-struct __padding<0> {};
-
 template <class _CharT, class _Traits, class _Allocator>
 class basic_string {
 public:
@@ -2542,6 +2535,10 @@ private:
 #  ifdef _LIBCPP_BUILDING_LIBRARY
   void __init(const value_type* __s, size_type __sz, size_type __reserve);
 #  endif
+
+#if _LIBCPP_STD_VER >= 17
+  friend __tombstone_traits<basic_string>;
+#endif
 };
 
 // These declarations must appear before any functions are implicitly used
@@ -2622,6 +2619,46 @@ basic_string(from_range_t, _Range&&, _Allocator = _Allocator())
     -> basic_string<ranges::range_value_t<_Range>, char_traits<ranges::range_value_t<_Range>>, _Allocator>;
 #  endif
 
+#  if _LIBCPP_STD_VER >= 17
+template <class _CharT, class _Traits, class _Allocator>
+struct __tombstone_traits<basic_string<_CharT, _Traits, _Allocator>> {
+  // If `string` is in the `__short` representation, the size can never be larger than `__min_cap`. 65 sets the bit
+  // for the small string representation and the size to 32. The maximum size is
+  // `sizeof(size_type) * 2 + sizeof(pointer)`, which is never greater than 24.
+
+  using _String = basic_string<_CharT, _Traits, _Allocator>;
+
+  static_assert(_String::__min_cap <= 32, "__min_cap is too big!");
+
+  template <class _Payload, size_t _Np>
+  struct __representation {
+    _LIBCPP_DIAGNOSTIC_PUSH
+    _LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wreorder-ctor")
+    template <class... _Args>
+    constexpr __representation(_Args&&... __args)
+        : __payload_(std::forward<_Args>(__args)...), __is_long_(0), __size_(_String::__min_cap + _Np) {}
+    _LIBCPP_DIAGNOSTIC_POP
+
+    bool __is_tombstone() { return __size_ == _String::__min_cap + _Np; }
+
+#    ifndef _LIBCPP_ABI_ALTERNATE_STRING_LAYOUT
+    struct _LIBCPP_PACKED {
+      unsigned char __is_long_ : 1;
+      unsigned char __size_    : 7;
+    };
+#    endif
+    _LIBCPP_NO_UNIQUE_ADDRESS _Payload __payload_;
+#    ifdef _LIBCPP_ABI_ALTERNATE_STRING_LAYOUT
+    _LIBCPP_NO_UNIQUE_ADDRESS
+    __padding<sizeof(_String::value_type) * (_String::__min_cap + 1) - 1 - __datasizeof_v<_Payload>>
+        __padding_;
+    unsigned char __size_    : 7;
+    unsigned char __is_long_ : 1;
+#    endif
+  };
+};
+#  endif                       // _LIBCPP_STD_VER >= 17
+
 template <class _CharT, class _Traits, class _Allocator>
 _LIBCPP_CONSTEXPR_SINCE_CXX20 void
 basic_string<_CharT, _Traits, _Allocator>::__init(const value_type* __s, size_type __sz) {
diff --git a/libcxx/include/string_view b/libcxx/include/string_view
index 5ecaa3de7deba..e6165adf2809c 100644
--- a/libcxx/include/string_view
+++ b/libcxx/include/string_view
@@ -227,6 +227,7 @@ namespace std {
 #  include <__iterator/reverse_iterator.h>
 #  include <__iterator/wrap_iter.h>
 #  include <__memory/pointer_traits.h>
+#  include <__memory/tombstone_traits.h>
 #  include <__ranges/concepts.h>
 #  include <__ranges/data.h>
 #  include <__ranges/enable_borrowed_range.h>
@@ -724,6 +725,14 @@ template <class _CharT, class _Traits>
 inline constexpr bool ranges::enable_borrowed_range<basic_string_view<_CharT, _Traits> > = true;
 #  endif // _LIBCPP_STD_VER >= 20
 
+#if _LIBCPP_STD_VER >= 17
+template <class _CharT, class _Traits>
+struct __tombstone_traits<__enable_specialization_if<alignof(_CharT) >= 2, basic_string_view<_CharT, _Traits>>>
+    : __tombstone_traits_assume_aligned_pointer<const _CharT*> {
+  static_assert(__builtin_offsetof(basic_string_view<_CharT, _Traits>, __data_) == 0);
+};
+#endif
+
 // [string.view.deduct]
 
 #  if _LIBCPP_STD_VER >= 20
diff --git a/libcxx/src/locale.cpp b/libcxx/src/locale.cpp
index 0f695d4f1a229..fc9f974cfcfd7 100644
--- a/libcxx/src/locale.cpp
+++ b/libcxx/src/locale.cpp
@@ -152,6 +152,8 @@ class _LIBCPP_HIDDEN locale::__imp : public facet {
   void install_from(const __imp& other);
 };
 
+static_assert(alignof(locale::__imp) == alignof(void*), "__tombstone_traits alignment is wrong!");
+
 locale::__imp::__imp(size_t refs) : facet(refs), facets_(N), name_("C") {
   facets_.clear();
   install(&make<std::collate<char> >(1u));
diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index c0031543e47bc..62a3963b70dce 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -615,8 +615,10 @@ experimental/iterator variant
 experimental/iterator version
 experimental/memory cstddef
 experimental/memory cstdint
+experimental/memory cstdlib
 experimental/memory cstring
 experimental/memory limits
+experimental/memory new
 experimental/memory type_traits
 experimental/memory version
 experimental/propagate_const cstddef
@@ -2459,6 +2461,7 @@ utility cstdlib
 utility initializer_list
 utility iosfwd
 utility limits
+utility new
 utility type_traits
 utility version
 valarray algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index c0031543e47bc..830d89c5486c4 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -615,8 +615,10 @@ experimental/iterator variant
 experimental/iterator version
 experimental/memory cstddef
 experimental/memory cstdint
+experimental/memory cstdlib
 experimental/memory cstring
 experimental/memory limits
+experimental/memory new
 experimental/memory type_traits
 experimental/memory version
 experimental/propagate_const cstddef
@@ -636,6 +638,7 @@ experimental/utility cstdlib
 experimental/utility initializer_list
 experimental/utility iosfwd
 experimental/utility limits
+experimental/utility new
 experimental/utility type_traits
 experimental/utility utility
 experimental/utility version
@@ -2459,6 +2462,7 @@ utility cstdlib
 utility initializer_list
 utility iosfwd
 utility limits
+utility new
 utility type_traits
 utility version
 valarray algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index c2eb5b44e8d7a..fee2e320d50f0 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -627,8 +627,10 @@ experimental/iterator variant
 experimental/iterator version
 experimental/memory cstddef
 experimental/memory cstdint
+experimental/memory cstdlib
 experimental/memory cstring
 experimental/memory limits
+experimental/memory new
 experimental/memory type_traits
 experimental/memory version
 experimental/propagate_const cstddef
@@ -653,6 +655,7 @@ experimental/utility cstdlib
 experimental/utility initializer_list
 experimental/utility iosfwd
 experimental/utility limits
+experimental/utility new
 experimental/utility type_traits
 experimental/utility utility
 experimental/utility version
@@ -2508,6 +2511,7 @@ utility cstdlib
 utility initializer_list
 utility iosfwd
 utility limits
+utility new
 utility type_traits
 utility version
 valarray algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 332cb62f35b5f..23db9585119b2 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -617,8 +617,10 @@ experimental/iterator variant
 experimental/iterator version
 experimental/memory cstddef
 experimental/memory cstdint
+experimental/memory cstdlib
 experimental/memory cstring
 experimental/memory limits
+experimental/memory new
 experimental/memory type_traits
 experimental/memory version
 experimental/propagate_const cstddef
@@ -643,6 +645,7 @@ experimental/utility cstdlib
 experimental/utility initializer_list
 experimental/utility iosfwd
 experimental/utility limits
+experimental/utility new
 experimental/utility type_traits
 experimental/utility utility
 experimental/utility version
@@ -2512,6 +2515,7 @@ utility cstdlib
 utility initializer_list
 utility iosfwd
 utility limits
+utility new
 utility type_traits
 utility version
 valarray algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 55c79acff5a8f..d45c378c80e76 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -445,9 +445,11 @@ coroutine cmath
 coroutine compare
 coroutine cstddef
 coroutine cstdint
+coroutine cstdlib
 coroutine cstring
 coroutine iosfwd
 coroutine limits
+coroutine new
 coroutine type_traits
 coroutine version
 cstddef version
@@ -613,8 +615,10 @@ experimental/iterator variant
 experimental/iterator version
 experimental/memory cstddef
 experimental/memory cstdint
+experimental/memory cstdlib
 experimental/memory cstring
 experimental/memory limits
+experimental/memory new
 experimental/memory type_traits
 experimental/memory version
 experimental/propagate_const cstddef
@@ -639,6 +643,7 @@ experimental/utility cstdlib
 experimental/utility initializer_list
 experimental/utility iosfwd
 experimental/utility limits
+experimental/utility new
 experimental/utility type_traits
 experimental/utility utility
 experimental/utility version
@@ -2540,6 +2545,7 @@ utility cstdlib
 utility initializer_list
 utility iosfwd
 utility limits
+utility new
 utility type_traits
 utility version
 valarray algorithm
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index cb23c7a98de1b..efbe92a10bcaf 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -203,6 +203,7 @@ coroutine compare
 coroutine cstdint
 coroutine cstring
 coroutine limits
+coroutine new
 coroutine version
 cstddef version
 ctgmath bitset
@@ -290,6 +291,7 @@ experimental/iterator variant
 experimental/iterator version
 experimental/memory cstdint
 experimental/memory cstring
+experimental/memory new
 experimental/memory version
 experimental/propagate_const version
 experimental/simd cstdint
@@ -303,6 +305,7 @@ experimental/utility compare
 experimental/utility cstdint
 experimental/utility initializer_list
 experimental/utility limits
+experimental/utility new
 experimental/utility utility
 experimental/utility version
 filesystem cctype
@@ -1130,6 +1133,7 @@ utility compare
 utility cstdint
 utility initializer_list
 utility limits
+utility new
 utility version
 valarray cmath
 valarray cstdint
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index 8c3e1f0a97dfe..5f4941c1326f4 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -198,6 +198,7 @@ coroutine compare
 coroutine cstdint
 coroutine cstring
 coroutine limits
+coroutine new
 coroutine version
 cstddef version
 ctgmath bitset
@@ -282,6 +283,7 @@ experimental/iterator variant
 experimental/iterator version
 experimental/memory cstdint
 experimental/memory cstring
+experimental/memory new
 experimental/memory version
 experimental/propagate_const version
 experimental/simd cstdint
@@ -296,6 +298,7 @@ experimental/utility cstdint
 experimental/utility cstring
 experimental/utility initializer_list
 experimental/utility limits
+experimental/utility new
 experimental/utility utility
 experimental/utility version
 filesystem cctype
@@ -1109,6 +1112,7 @@ utility cstdint
 utility cstring
 utility initializer_list
 utility limits
+utility new
 utility version
 valarray cmath
 valarray cstdint
diff --git a/libcxx/test/libcxx/utilities/optional/optional.object/optional_size.pass.cpp b/libcxx/test/libcxx/utilities/optional/optional.object/optional_size.pass.cpp
index b928ed7432791..57fb26e3cddb2 100644
--- a/libcxx/test/libcxx/utilities/optional/optional.object/optional_size.pass.cpp
+++ b/libcxx/test/libcxx/utilities/optional/optional.object/optional_size.pass.cpp
@@ -12,7 +12,12 @@
 
 // template <class T> class optional;
 
+#include <functional>
+#include <locale>
+#include <memory>
 #include <optional>
+#include <string>
+#include <vector>
 
 template <class T>
 struct type_with_bool {
@@ -20,6 +25,8 @@ struct type_with_bool {
   bool has_value;
 };
 
+struct alignas(void*) aligned_s {};
+
 int main(int, char**) {
   // Test that std::optional achieves the expected size. See https://llvm.org/PR61095.
   static_assert(sizeof(std::optional<char>) == sizeof(type_with_bool<char>));
@@ -27,5 +34,29 @@ int main(int, char**) {
   static_assert(sizeof(std::optional<long>) == sizeof(type_with_bool<long>));
   static_assert(sizeof(std::optional<std::size_t>) == sizeof(type_with_bool<std::size_t>));
 
+  // Check that the optionals using a tombstone have the expected size
+  static_assert(sizeof(std::optional<bool>) == sizeof(bool));
+  static_assert(sizeof(std::optional<std::string>) == sizeof(std::string));
+  static_assert(sizeof(std::optional<int*>) == sizeof(void*));
+  static_assert(sizeof(std::optional<short*>) == sizeof(void*));
+  static_assert(sizeof(std::optional<char*>) == sizeof(void*) * 2);
+  static_assert(sizeof(std::optional<char**>) == sizeof(void*));
+  static_assert(sizeof(std::optional<aligned_s*>) == sizeof(void*) * 2);
+  static_assert(sizeof(std::optional<std::unique_ptr<int>>) == sizeof(void*));
+  static_assert(sizeof(std::optional<std::unique_ptr<int[]>>) == sizeof(void*));
+  static_assert(sizeof(std::optional<std::unique_ptr<aligned_s>>) == sizeof(void*) * 2);
+  static_assert(sizeof(std::optional<std::reference_wrapper<char*>>) == sizeof(void*));
+  static_assert(sizeof(std::optional<std::reference_wrapper<aligned_s>>) == sizeof(void*));
+  static_assert(sizeof(std::optional<std::locale>) == sizeof(void*));
+  static_assert(sizeof(std::optional<std::shared_ptr<char>>) == sizeof(void*) * 2);
+  static_assert(sizeof(std::optional<std::shared_ptr<int>>) == sizeof(void*) * 2);
+  static_assert(sizeof(std::optional<std::weak_ptr<char>>) == sizeof(void*) * 2);
+  static_assert(sizeof(std::optional<std::weak_ptr<int>>) == sizeof(void*) * 2);
+  static_assert(sizeof(std::optional<std::pair<int, int>>) == sizeof(int) * 3);
+  static_assert(sizeof(std::optional<std::pair<std::string, char*>>) == sizeof(void*) * 4);
+  static_assert(sizeof(std::optional<std::pair<char*, std::string>>) == sizeof(void*) * 4);
+  static_assert(sizeof(std::optional<std::vector<int>>) == sizeof(void*) * 3);
+  static_assert(sizeof(std::optional<std::vector<char>>) == sizeof(void*) * 4);
+
   return 0;
 }
diff --git a/libcxx/test/std/utilities/optional/tombstone_types.pass.cpp b/libcxx/test/std/utilities/optional/tombstone_types.pass.cpp
new file mode 100644
index 0000000000000..43cab57d25071
--- /dev/null
+++ b/libcxx/test/std/utilities/optional/tombstone_types.pass.cpp
@@ -0,0 +1,68 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+// <optional>
+
+#include <cassert>
+#include <optional>
+#include <string>
+
+#include "test_macros.h"
+
+TEST_CONSTEXPR_CXX20 bool test() {
+  {
+    std::optional<bool> opt;
+    assert(!opt);
+    opt = true;
+    assert(opt);
+    assert(*opt);
+    opt = false;
+    assert(opt);
+    assert(!*opt);
+    opt.reset();
+    assert(!opt);
+  }
+
+  {
+    std::optional<std::string> opt;
+    assert(!opt);
+    opt = "";
+    assert(opt);
+    assert(*opt == "");
+    opt = "23 letter string to SSO";
+    assert(opt);
+    assert(*opt == "23 letter string to SSO");
+    opt.reset();
+    assert(!opt);
+  }
+
+  {
+    int i;
+    int j;
+    std::optional<int*> opt;
+    assert(!opt);
+    opt = &i;
+    assert(opt);
+    assert(*opt == &i);
+    opt = &j;
+    assert(opt);
+    assert(*opt == &j);
+    opt.reset();
+    assert(!opt);
+  }
+
+  return true;
+}
+
+int main(int, char**) {
+  test();
+#if TEST_STD_VER >= 20
+  static_assert(test());
+#endif
+}



More information about the libcxx-commits mailing list