[libcxx-commits] [libcxx] [libc++] Implement Tombstone Traits (PR #237502)
William Tran-Viet via libcxx-commits
libcxx-commits at lists.llvm.org
Sat Oct 31 13:15:17 PDT 2026
https://github.com/smallp-o-p updated https://github.com/llvm/llvm-project/pull/237502
>From 524cd9f8a1ae830052969b9ebb215d72b042abbb 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 01/12] [libc++] Add tombstone traits to use in optional,
variant
---
libcxx/include/CMakeLists.txt | 1 +
libcxx/include/__configuration/abi.h | 1 +
.../include/__functional/reference_wrapper.h | 24 +-
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/module.modulemap.in | 4 +
libcxx/include/string | 53 +++-
libcxx/include/string_view | 9 +
libcxx/src/locale.cpp | 2 +
.../test/libcxx/transitive_includes/cxx03.csv | 1 +
.../test/libcxx/transitive_includes/cxx11.csv | 1 +
.../test/libcxx/transitive_includes/cxx14.csv | 1 +
.../test/libcxx/transitive_includes/cxx17.csv | 1 +
.../test/libcxx/transitive_includes/cxx20.csv | 2 +
.../test/libcxx/transitive_includes/cxx23.csv | 4 +
.../test/libcxx/transitive_includes/cxx26.csv | 4 +
.../optional/tombstone_types.pass.cpp | 68 +++++
21 files changed, 525 insertions(+), 12 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 fcb66d5fe2f48a..d5ffe939dba3fa 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -636,6 +636,7 @@ set(files
__memory/swap_allocator.h
__memory/temp_value.h
__memory/temporary_buffer.h
+ __memory/tombstone_traits.h
__memory/uninitialized_algorithms.h
__memory/uninitialized_multidimensional_algorithms.h
__memory/unique_ptr.h
diff --git a/libcxx/include/__configuration/abi.h b/libcxx/include/__configuration/abi.h
index 9343eacf393449..e637e217b5b8f7 100644
--- a/libcxx/include/__configuration/abi.h
+++ b/libcxx/include/__configuration/abi.h
@@ -86,6 +86,7 @@
# if defined(__FreeBSD__)
# define _LIBCPP_DEPRECATED_ABI_DISABLE_PAIR_TRIVIAL_COPY_CTOR
# endif
+
#endif
// We had some bugs where we use [[no_unique_address]] together with construct_at,
diff --git a/libcxx/include/__functional/reference_wrapper.h b/libcxx/include/__functional/reference_wrapper.h
index b1efd9f76d877b..d696e7c81e04f4 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>
[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI
diff --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h
index 0db58b37a6288b..39749416787685 100644
--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -33,6 +33,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/uninitialized_multidimensional_algorithms.h>
#include <__memory/unique_ptr.h>
@@ -652,14 +653,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
@@ -1295,12 +1307,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_HIDE_FROM_ABI void swap(weak_ptr<_Tp>& __x, weak_ptr<_Tp>& __y) _NOEXCEPT {
diff --git a/libcxx/include/__memory/tombstone_traits.h b/libcxx/include/__memory/tombstone_traits.h
new file mode 100644
index 00000000000000..f354a25e69df53
--- /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 01820aa52b5335..96e53c80487485 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>
@@ -274,8 +275,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[]>
// ==================================
//
@@ -577,8 +588,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 ae1af6ebf17d90..b22d4e9dd68378 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 6236553cfb836b..9449289368d755 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 a65c178a9f0360..6d5b3ffa274343 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>
@@ -433,6 +434,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/module.modulemap.in b/libcxx/include/module.modulemap.in
index 6ca65ba556fa1c..d031a5dc4f47fd 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1734,6 +1734,10 @@ module std {
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.memory.destroy
diff --git a/libcxx/include/string b/libcxx/include/string
index 33175c115fd10c..a693ddf47d37fa 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -620,6 +620,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>
@@ -724,14 +725,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 _LIBCPP_WARN_UNUSED basic_string {
public:
@@ -2496,6 +2489,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
@@ -2586,6 +2583,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 736ac391a7979b..89f88aabc3dbf2 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>
@@ -735,6 +736,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 7795e9eab2fc7b..495c61696ccfdd 100644
--- a/libcxx/src/locale.cpp
+++ b/libcxx/src/locale.cpp
@@ -142,6 +142,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 201fc2f36bf482..929ba564d4381f 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -234,6 +234,7 @@ experimental/iterator tuple
experimental/iterator typeinfo
experimental/iterator version
experimental/memory cstdint
+experimental/memory cstdlib
experimental/memory cstring
experimental/memory version
experimental/propagate_const version
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index ab8791f9aa0747..adf7ebf2d7c80d 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -234,6 +234,7 @@ experimental/iterator tuple
experimental/iterator typeinfo
experimental/iterator version
experimental/memory cstdint
+experimental/memory cstdlib
experimental/memory cstring
experimental/memory version
experimental/propagate_const version
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index 3d5030b5644e2b..c1274500243f96 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -234,6 +234,7 @@ experimental/iterator tuple
experimental/iterator typeinfo
experimental/iterator version
experimental/memory cstdint
+experimental/memory cstdlib
experimental/memory cstring
experimental/memory version
experimental/propagate_const version
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 8e3b1de5efd7ee..0896b2f1b72673 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -243,6 +243,7 @@ experimental/iterator tuple
experimental/iterator typeinfo
experimental/iterator version
experimental/memory cstdint
+experimental/memory cstdlib
experimental/memory cstring
experimental/memory version
experimental/propagate_const version
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index b9d1dd17aa016f..4ea0167247c080 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -196,6 +196,7 @@ condition_variable typeinfo
condition_variable version
coroutine compare
coroutine cstdint
+coroutine cstdlib
coroutine cstring
coroutine limits
coroutine version
@@ -278,6 +279,7 @@ experimental/iterator typeinfo
experimental/iterator variant
experimental/iterator version
experimental/memory cstdint
+experimental/memory cstdlib
experimental/memory cstring
experimental/memory version
experimental/propagate_const version
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 83125257f35f54..75120224ed18eb 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -199,6 +199,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
@@ -295,6 +297,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
@@ -1097,6 +1100,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 229e85afd106b7..cb99de928efcc3 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -196,6 +196,7 @@ coroutine compare
coroutine cstdint
coroutine cstring
coroutine limits
+coroutine new
coroutine version
cstddef version
ctgmath bitset
@@ -279,6 +280,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
@@ -293,6 +295,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
@@ -1090,6 +1093,7 @@ utility cstdint
utility cstring
utility initializer_list
utility limits
+utility new
utility version
valarray cmath
valarray cstdint
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 00000000000000..43cab57d250713
--- /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
+}
>From 980894cc871f9edba4d2de1d8eda0a35cc6003ef Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Fri, 30 Oct 2026 22:53:21 -0400
Subject: [PATCH 02/12] Fix some typos
---
libcxx/include/__memory/tombstone_traits.h | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/libcxx/include/__memory/tombstone_traits.h b/libcxx/include/__memory/tombstone_traits.h
index f354a25e69df53..9adc4625075334 100644
--- a/libcxx/include/__memory/tombstone_traits.h
+++ b/libcxx/include/__memory/tombstone_traits.h
@@ -100,11 +100,12 @@ struct __tombstone_traits<__enable_specialization_if<is_fundamental_v<_Tp> && al
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!?");
+ 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;
@@ -144,8 +145,9 @@ union _MaybeTombstone {
if consteval {
return __builtin_is_within_lifetime(&__value_);
} else {
- _DummyTombstone __dummy;
static_assert(sizeof(_DummyTombstone) <= sizeof(*this));
+
+ _DummyTombstone __dummy;
__builtin_memcpy(&__dummy, this, sizeof(_DummyTombstone));
return !__dummy.__is_tombstone();
@@ -205,7 +207,7 @@ struct __tombstoned_value final {
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");
+ static_assert(sizeof(_Tombstone) <= sizeof(_Tp), "Trying to use tombstone which can't hold T");
_MaybeTombstone<_Tp, _Payload> __data_;
@@ -250,25 +252,25 @@ struct __tombstoned_value final {
}
_LIBCPP_HIDE_FROM_ABI constexpr auto&& __get_payload() && noexcept {
- _LIBCPP_ASSERT_INTERNAL(!__is_engaged(), "Trying to get the payload of an enagaged tombstoned value");
+ _LIBCPP_ASSERT_INTERNAL(!__is_engaged(), "Trying to get the payload of an engaged 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");
+ _LIBCPP_ASSERT_INTERNAL(!__is_engaged(), "Trying to get the payload of an engaged 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");
+ _LIBCPP_ASSERT_INTERNAL(!__is_engaged(), "Trying to engage 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");
+ _LIBCPP_ASSERT_INTERNAL(__is_engaged(), "Trying to disengage a disengaged tombstoned value");
std::destroy_at(&__data_.__value_);
std::__construct_at(&__data_.__tombstone_, std::forward<_Args>(__args)...);
}
>From b808386775c23b62281d20251df57e6e855c4c12 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Fri, 30 Oct 2026 22:57:14 -0400
Subject: [PATCH 03/12] Rename macro to where it actually lives
---
libcxx/include/__memory/tombstone_traits.h | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/libcxx/include/__memory/tombstone_traits.h b/libcxx/include/__memory/tombstone_traits.h
index 9adc4625075334..4bc52c8a4307d7 100644
--- a/libcxx/include/__memory/tombstone_traits.h
+++ b/libcxx/include/__memory/tombstone_traits.h
@@ -6,8 +6,8 @@
//
//===----------------------------------------------------------------------===//
-#ifndef _LIBCPP___TYPE_TRAITS_TOMBSTONE_TRAITS_H
-#define _LIBCPP___TYPE_TRAITS_TOMBSTONE_TRAITS_H
+#ifndef _LIBCPP___MEMORY_TOMBSTONE_TRAITS_H
+#define _LIBCPP___MEMORY_TOMBSTONE_TRAITS_H
#include <__assert>
#include <__config>
@@ -288,4 +288,4 @@ _LIBCPP_END_NAMESPACE_STD
_LIBCPP_POP_MACROS
-#endif // _LIBCPP___TYPE_TRAITS_TOMBSTONE_TRAITS_H
+#endif // _LIBCPP___MEMORY_TOMBSTONE_TRAITS_H
>From c586b6cad3c08c725a43ee2e7b55037399fce37e Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Sat, 31 Oct 2026 01:04:01 -0400
Subject: [PATCH 04/12] Update Tp* tombstone traits to accommodate little
endian
---
libcxx/include/__memory/tombstone_traits.h | 31 +++++++++++++++++++---
1 file changed, 27 insertions(+), 4 deletions(-)
diff --git a/libcxx/include/__memory/tombstone_traits.h b/libcxx/include/__memory/tombstone_traits.h
index 4bc52c8a4307d7..6c5e769b2581ba 100644
--- a/libcxx/include/__memory/tombstone_traits.h
+++ b/libcxx/include/__memory/tombstone_traits.h
@@ -75,14 +75,30 @@ struct __tombstone_traits_assume_aligned_pointer {
template <class... _Args>
constexpr __representation(_Args&&... __args) : __payload_(std::forward<_Args>(__args)...), __value_(_Np + 1) {}
- bool __is_tombstone() { return __value_ == _Np + 1; }
+ constexpr bool __is_tombstone() { return __value_ == _Np + 1; }
+
+ static_assert(sizeof(_Tp*) - 1 - (alignof(_Payload) - 1) >= sizeof(_Payload));
+
+ // clang-format off
+# ifdef _LIBCPP_LITTLE_ENDIAN
+ // TODO: Figure out a better explanation for the layout
+ // On little-endian, the value is the first byte, and how much we can use the remaining 7 bytes depends on the payload's alignment requirements.
+ // If alignof == 1, we have to pad 0 bytes.
+ // If alignof == 2, we have to pad 1 byte.
+ // If alignof == 4, we have to pad 3 bytes.
+ unsigned char __value_;
+ _LIBCPP_NO_UNIQUE_ADDRESS __padding<alignof(_Payload) - 1> __padding_;
+ _LIBCPP_NO_UNIQUE_ADDRESS _Payload __payload_;
+# else
+ // On big-endian, we actually only need to use the datasize of _Payload because it sits first, which relaxes the requirements for the payload quite a bit
+ // since we can fill all 7 bytes without worrying about alignment. However, to keep things consistent, maintain the same requirements as little-endian.
_LIBCPP_NO_UNIQUE_ADDRESS _Payload __payload_;
- _LIBCPP_NO_UNIQUE_ADDRESS __padding<sizeof(_Tp*) - 1 - __datasizeof_v<_Payload>> __padding_;
+ _LIBCPP_NO_UNIQUE_ADDRESS __padding<alignof(_Payload) - 1> __padding_;
unsigned char __value_;
+# endif
+ // clang-format on
};
-
- static constexpr size_t __max_tombstone = 0; // TODO: Calculate this correctly
};
// TODO: Look into
@@ -198,6 +214,13 @@ union _MaybeTombstone<_Tp, _Payload, true> {
_LIBCPP_CONSTEXPR_SINCE_CXX20 ~_MaybeTombstone() = default;
};
+// For a type to have a tombstone, it must have a specialization of __tombstone_traits.
+// __tombstoned_value that can contain one of either _Tp, or a specialized _Payload class/struct, which must occupy
+// the same memory footprint as _Tp.
+// If _Tp has a valid memory represntation of that type, then __tombstoned_value is engaged and contains that
+// instance of _Tp. Otherwise, it is _Payload, which represents an impossible in-memory value of _Tp, as well as any
+// extra metadata which can fit inside.
+
template <class _Tp, class _Payload>
struct __tombstoned_value final {
using _Tombstone = __tombstone_traits<_Tp>::template __representation<_Payload, 0>;
>From ca55080b0958e402ee0fd19a29c8c6a7e962cb82 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Sat, 31 Oct 2026 01:59:44 -0400
Subject: [PATCH 05/12] Remove shared_ptr specialization (for now)
---
libcxx/include/__memory/shared_ptr.h | 18 ------------------
1 file changed, 18 deletions(-)
diff --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h
index 39749416787685..1d8a62c984a67a 100644
--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -653,26 +653,8 @@ 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 && 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>;
-
-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
//
>From 31283177200064047f09240c475693f1707ebe47 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Sat, 31 Oct 2026 02:00:30 -0400
Subject: [PATCH 06/12] Remove pair tombstone_traits
---
libcxx/include/__utility/pair.h | 22 ----------------------
1 file changed, 22 deletions(-)
diff --git a/libcxx/include/__utility/pair.h b/libcxx/include/__utility/pair.h
index 6d5b3ffa274343..86dc245d91493c 100644
--- a/libcxx/include/__utility/pair.h
+++ b/libcxx/include/__utility/pair.h
@@ -18,7 +18,6 @@
#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>
@@ -436,27 +435,6 @@ 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
template <class _T1, class _T2, class _U1, class _U2>
>From a4f0ecee7824b83ae86d4bec601751aa110e17ed Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Sat, 31 Oct 2026 02:00:56 -0400
Subject: [PATCH 07/12] shared_ptr include
---
libcxx/include/__memory/shared_ptr.h | 1 -
1 file changed, 1 deletion(-)
diff --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h
index 1d8a62c984a67a..21c311640b874c 100644
--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -33,7 +33,6 @@
#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/uninitialized_multidimensional_algorithms.h>
#include <__memory/unique_ptr.h>
>From 02b131039b13c1730961a0e7e5db30e7840eeb26 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Sat, 31 Oct 2026 02:21:39 -0400
Subject: [PATCH 08/12] Bump minimum to C++20 Remove _LIBCPP_HIDE_FROM_ABI Add
diagnostic push/pop Tidy up some usings
---
.../include/__functional/reference_wrapper.h | 4 +-
libcxx/include/__memory/tombstone_traits.h | 57 ++++++++++---------
libcxx/include/__memory/unique_ptr.h | 8 ++-
libcxx/include/string | 8 +--
4 files changed, 42 insertions(+), 35 deletions(-)
diff --git a/libcxx/include/__functional/reference_wrapper.h b/libcxx/include/__functional/reference_wrapper.h
index d696e7c81e04f4..0d16a65bdc14cd 100644
--- a/libcxx/include/__functional/reference_wrapper.h
+++ b/libcxx/include/__functional/reference_wrapper.h
@@ -127,7 +127,9 @@ class reference_wrapper : public __weak_result_type<_Tp> {
#if _LIBCPP_STD_VER >= 17
template <class _Tp>
reference_wrapper(_Tp&) -> reference_wrapper<_Tp>;
+#endif // _LIBCPP_STD_VER >= 17
+#if _LIBCPP_STD_VER >= 20
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
@@ -147,7 +149,7 @@ struct __tombstone_traits<reference_wrapper<_Tp>> {
_Tp* __value_;
};
};
-#endif // _LIBCPP_STD_VER >= 17
+#endif // _LIBCPP_STD_VER >= 20
template <class _Tp>
[[__nodiscard__]] inline _LIBCPP_HIDE_FROM_ABI
diff --git a/libcxx/include/__memory/tombstone_traits.h b/libcxx/include/__memory/tombstone_traits.h
index 6c5e769b2581ba..4f8a15665603f0 100644
--- a/libcxx/include/__memory/tombstone_traits.h
+++ b/libcxx/include/__memory/tombstone_traits.h
@@ -39,7 +39,7 @@ _LIBCPP_BEGIN_NAMESPACE_STD
template <class>
struct __tombstone_traits;
-#if _LIBCPP_STD_VER >= 17
+#if _LIBCPP_STD_VER >= 20
// bools always have exactly one bit set. If there is more than one set it's disengaged.
template <>
@@ -49,7 +49,7 @@ struct __tombstone_traits<bool> {
template <class... _Args>
constexpr __representation(_Args&&... __args) : __payload_(std::forward<_Args>(__args)...), __value_(_Np + 2) {}
- bool __is_tombstone() { return __value_ == _Np + 2; }
+ constexpr bool __is_tombstone() { return __value_ == _Np + 2; }
_LIBCPP_NO_UNIQUE_ADDRESS _Payload __payload_;
unsigned char __value_;
@@ -72,12 +72,16 @@ template <class _Tp>
struct __tombstone_traits_assume_aligned_pointer {
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)...), __value_(_Np + 1) {}
+ _LIBCPP_DIAGNOSTIC_POP
constexpr bool __is_tombstone() { return __value_ == _Np + 1; }
- static_assert(sizeof(_Tp*) - 1 - (alignof(_Payload) - 1) >= sizeof(_Payload));
+ static_assert(sizeof(_Tp*) - 1 - (alignof(_Payload) - 1) >= sizeof(_Payload),
+ "Payload is too big for tombstone of pointer!");
// clang-format off
# ifdef _LIBCPP_LITTLE_ENDIAN
@@ -128,10 +132,6 @@ inline constexpr struct __init_disengaged_t {
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>
@@ -157,7 +157,7 @@ union _MaybeTombstone {
_MaybeTombstone& operator=(const _MaybeTombstone&) = default;
_MaybeTombstone& operator=(_MaybeTombstone&&) = default;
- _LIBCPP_HIDE_FROM_ABI constexpr bool __is_engaged() const noexcept {
+ constexpr bool __is_engaged() const noexcept {
if consteval {
return __builtin_is_within_lifetime(&__value_);
} else {
@@ -170,7 +170,7 @@ union _MaybeTombstone {
}
}
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 ~_MaybeTombstone() {
+ constexpr ~_MaybeTombstone() {
if (__is_engaged()) {
std::destroy_at(&__value_);
} else {
@@ -199,7 +199,7 @@ union _MaybeTombstone<_Tp, _Payload, true> {
_MaybeTombstone& operator=(const _MaybeTombstone&) = default;
_MaybeTombstone& operator=(_MaybeTombstone&&) = default;
- _LIBCPP_HIDE_FROM_ABI constexpr bool __is_engaged() const noexcept {
+ constexpr bool __is_engaged() const noexcept {
if consteval {
return __builtin_is_within_lifetime(&__value_);
} else {
@@ -211,7 +211,7 @@ union _MaybeTombstone<_Tp, _Payload, true> {
}
}
- _LIBCPP_CONSTEXPR_SINCE_CXX20 ~_MaybeTombstone() = default;
+ constexpr ~_MaybeTombstone() = default;
};
// For a type to have a tombstone, it must have a specialization of __tombstone_traits.
@@ -223,8 +223,9 @@ union _MaybeTombstone<_Tp, _Payload, true> {
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>;
+ using _Repr = _MaybeTombstone<_Tp, _Payload>;
+ using _Tombstone = _Repr::_Tombstone;
+ using _DummyTombstone = _Repr::_DummyTombstone;
static_assert(__builtin_is_implicit_lifetime(_DummyTombstone));
static_assert(sizeof(_Tombstone) == sizeof(_DummyTombstone));
@@ -232,67 +233,67 @@ struct __tombstoned_value final {
static_assert(__datasizeof_v<_Tombstone> == __datasizeof_v<_DummyTombstone>);
static_assert(sizeof(_Tombstone) <= sizeof(_Tp), "Trying to use tombstone which can't hold T");
- _MaybeTombstone<_Tp, _Payload> __data_;
+ _Repr __data_;
template <class... _Args>
- explicit _LIBCPP_HIDE_FROM_ABI constexpr __tombstoned_value(__init_disengaged_t, _Args&&... __args)
+ explicit 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)
+ explicit 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(); }
+ constexpr bool __is_engaged() const noexcept { return __data_.__is_engaged(); }
- _LIBCPP_HIDE_FROM_ABI constexpr auto&& __get_value() & noexcept {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ 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 {
+ constexpr auto&& __get_payload() && noexcept {
_LIBCPP_ASSERT_INTERNAL(!__is_engaged(), "Trying to get the payload of an engaged tombstoned value");
return std::move(__data_.__tombstone_.__payload_);
}
- _LIBCPP_HIDE_FROM_ABI constexpr auto&& __get_payload() const&& noexcept {
+ constexpr auto&& __get_payload() const&& noexcept {
_LIBCPP_ASSERT_INTERNAL(!__is_engaged(), "Trying to get the payload of an engaged tombstoned value");
return std::move(__data_.__tombstone_.__payload_);
}
template <class... _Args>
- _LIBCPP_HIDE_FROM_ABI constexpr void __engage(in_place_t, _Args&&... __args) {
+ constexpr void __engage(in_place_t, _Args&&... __args) {
_LIBCPP_ASSERT_INTERNAL(!__is_engaged(), "Trying to engage 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) {
+ constexpr void __disengage(in_place_t, _Args&&... __args) {
_LIBCPP_ASSERT_INTERNAL(__is_engaged(), "Trying to disengage a disengaged tombstoned value");
std::destroy_at(&__data_.__value_);
std::__construct_at(&__data_.__tombstone_, std::forward<_Args>(__args)...);
@@ -305,7 +306,7 @@ 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
+#endif // _LIBCPP_STD_VER >= 20
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__memory/unique_ptr.h b/libcxx/include/__memory/unique_ptr.h
index 96e53c80487485..f04bad1b9434a6 100644
--- a/libcxx/include/__memory/unique_ptr.h
+++ b/libcxx/include/__memory/unique_ptr.h
@@ -276,10 +276,12 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI unique_ptr {
swap(__deleter_, __u.__deleter_);
}
+#if _LIBCPP_STD_VER >= 20
friend struct __tombstone_traits<unique_ptr>;
+#endif
};
-#if _LIBCPP_STD_VER >= 17
+#if _LIBCPP_STD_VER >= 20
template <class _Tp>
struct __tombstone_traits<__enable_specialization_if<__has_tombstone_v<_Tp*>, unique_ptr<_Tp>>>
: __tombstone_traits<_Tp*> {
@@ -589,10 +591,12 @@ class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI unique_ptr<_Tp[], _Dp> {
swap(__checker_, __u.__checker_);
}
+#if _LIBCPP_STD_VER >= 20
friend struct __tombstone_traits<unique_ptr>;
+#endif
};
-#if _LIBCPP_STD_VER >= 17
+#if _LIBCPP_STD_VER >= 20
template <class _Tp>
struct __tombstone_traits<__enable_specialization_if<__has_tombstone_v<_Tp*>, unique_ptr<_Tp[]>>>
: __tombstone_traits<_Tp*> {
diff --git a/libcxx/include/string b/libcxx/include/string
index a693ddf47d37fa..4f35f2a9b7a47c 100644
--- a/libcxx/include/string
+++ b/libcxx/include/string
@@ -2490,9 +2490,9 @@ private:
void __init(const value_type* __s, size_type __sz, size_type __reserve);
# endif
-#if _LIBCPP_STD_VER >= 17
+# if _LIBCPP_STD_VER >= 20
friend __tombstone_traits<basic_string>;
-#endif
+# endif
};
// These declarations must appear before any functions are implicitly used
@@ -2583,7 +2583,7 @@ 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
+# if _LIBCPP_STD_VER >= 20
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
@@ -2621,7 +2621,7 @@ struct __tombstone_traits<basic_string<_CharT, _Traits, _Allocator>> {
# endif
};
};
-# endif // _LIBCPP_STD_VER >= 17
+# endif // _LIBCPP_STD_VER >= 20
template <class _CharT, class _Traits, class _Allocator>
_LIBCPP_CONSTEXPR_SINCE_CXX20 void
>From 9c594b36ae320b432d07a8be1590c4dc3e4539e2 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Sat, 31 Oct 2026 02:28:54 -0400
Subject: [PATCH 09/12] Add tests for each specialized type in this PR
---
.../memory/tombstone_traits/bool.pass.cpp | 42 +++++
.../memory/tombstone_traits/pointer.pass.cpp | 158 ++++++++++++++++++
.../reference_wrapper.pass.cpp | 75 +++++++++
.../memory/tombstone_traits/string.pass.cpp | 100 +++++++++++
.../tombstone_traits/unique_ptr.pass.cpp | 151 +++++++++++++++++
5 files changed, 526 insertions(+)
create mode 100644 libcxx/test/libcxx/memory/tombstone_traits/bool.pass.cpp
create mode 100644 libcxx/test/libcxx/memory/tombstone_traits/pointer.pass.cpp
create mode 100644 libcxx/test/libcxx/memory/tombstone_traits/reference_wrapper.pass.cpp
create mode 100644 libcxx/test/libcxx/memory/tombstone_traits/string.pass.cpp
create mode 100644 libcxx/test/libcxx/memory/tombstone_traits/unique_ptr.pass.cpp
diff --git a/libcxx/test/libcxx/memory/tombstone_traits/bool.pass.cpp b/libcxx/test/libcxx/memory/tombstone_traits/bool.pass.cpp
new file mode 100644
index 00000000000000..a541570e5f8dba
--- /dev/null
+++ b/libcxx/test/libcxx/memory/tombstone_traits/bool.pass.cpp
@@ -0,0 +1,42 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// tombstone_traits<bool>
+
+#include <__memory/tombstone_traits.h>
+#include <cassert>
+#include <utility>
+
+struct Empty {};
+
+constexpr bool test() {
+ std::__tombstoned_value<bool, Empty> tombstone(std::__init_disengaged);
+
+ assert(!tombstone.__is_engaged());
+
+ tombstone.__engage(std::in_place, true);
+
+ assert(tombstone.__get_value());
+ assert(std::as_const(tombstone.__get_value()));
+ assert(std::move(tombstone.__get_value()));
+ assert(std::move(std::as_const(tombstone.__get_value())));
+
+ tombstone.__disengage(std::in_place);
+ assert(!tombstone.__is_engaged());
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx/memory/tombstone_traits/pointer.pass.cpp b/libcxx/test/libcxx/memory/tombstone_traits/pointer.pass.cpp
new file mode 100644
index 00000000000000..5951ec60769fb1
--- /dev/null
+++ b/libcxx/test/libcxx/memory/tombstone_traits/pointer.pass.cpp
@@ -0,0 +1,158 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRED: std-at-least-c++20
+
+// UNSUPPORTED: 32-bit-pointer
+// TODO: Expand tests to accommodate 32-bit pointers
+
+#include <__memory/tombstone_traits.h>
+#include <cassert>
+#include <string_view>
+#include <utility>
+
+// Payloads that can fit into the available bytes given their padding
+
+struct Size7Align1 {
+ char payload[7] = {
+ 'h',
+ 'e',
+ 'l',
+ 'l',
+ 'o',
+ '1',
+ '\0',
+ };
+
+ constexpr std::string_view getPayload() & { return "ref"; }
+ constexpr std::string_view getPayload() && { return "move"; }
+ constexpr std::string_view getPayload() const& { return "const ref"; }
+ constexpr std::string_view getPayload() const&& { return "const move"; }
+};
+
+struct Size6Align2 {
+ short a = 1, b = 2, c = 3;
+};
+
+struct Size4Align4 {
+ int a = 1234;
+};
+
+constexpr bool test() {
+ // start engaged, disengage
+ {
+ int my_int = 1;
+ std::__tombstoned_value<int*, Size7Align1> tombstone(std::__init_engaged, &my_int);
+ assert(sizeof(tombstone) == sizeof(&my_int));
+
+ assert(tombstone.__is_engaged());
+ assert(tombstone.__get_value() == &my_int);
+ assert(std::as_const(tombstone.__get_value()) == &my_int);
+ assert(std::move(tombstone.__get_value()) == &my_int);
+ assert(std::move(std::as_const(tombstone)).__get_value() == &my_int);
+ assert(*tombstone.__get_value() == my_int);
+
+ tombstone.__disengage(std::in_place, Size7Align1{});
+ assert(!tombstone.__is_engaged());
+
+ assert(std::string_view(tombstone.__get_payload().payload) == "hello1");
+ assert(tombstone.__get_payload().getPayload() == "ref");
+ assert(std::as_const(tombstone.__get_payload()).getPayload() == "const ref");
+ assert(std::move(tombstone.__get_payload()).getPayload() == "move");
+ assert(std::move(std::as_const(tombstone.__get_payload())).getPayload() == "const move");
+ }
+
+ {
+ short my_short = 1;
+ std::__tombstoned_value<short*, Size6Align2> tombstone(std::__init_engaged, &my_short);
+ assert(sizeof(tombstone) == sizeof(&my_short));
+
+ assert(tombstone.__is_engaged());
+ assert(tombstone.__get_value() == &my_short);
+ assert(*tombstone.__get_value() == my_short);
+
+ tombstone.__disengage(std::in_place, Size6Align2{});
+ assert(!tombstone.__is_engaged());
+
+ assert(tombstone.__get_payload().a == 1);
+ assert(tombstone.__get_payload().b == 2);
+ assert(tombstone.__get_payload().c == 3);
+ }
+
+ {
+ long my_long = 3;
+ std::__tombstoned_value<long*, Size4Align4> tombstone(std::__init_engaged, &my_long);
+ assert(sizeof(tombstone) == sizeof(&my_long));
+
+ assert(tombstone.__is_engaged());
+ assert(tombstone.__get_value() == &my_long);
+ assert(*tombstone.__get_value() == my_long);
+
+ tombstone.__disengage(std::in_place, Size4Align4{});
+ assert(!tombstone.__is_engaged());
+
+ assert(tombstone.__get_payload().a == 1234);
+ }
+
+ // start disengaged, then engage
+ {
+ int my_int = 1;
+ std::__tombstoned_value<int*, Size7Align1> tombstone(std::__init_disengaged, Size7Align1{});
+ assert(sizeof(tombstone) == sizeof(&my_int));
+
+ assert(!tombstone.__is_engaged());
+ assert(std::string_view(tombstone.__get_payload().payload) == "hello1");
+
+ tombstone.__engage(std::in_place, &my_int);
+ assert(tombstone.__is_engaged());
+
+ assert(tombstone.__get_value() == &my_int);
+ assert(*tombstone.__get_value() == my_int);
+ }
+
+ {
+ short my_short = 1;
+ std::__tombstoned_value<short*, Size6Align2> tombstone(std::__init_disengaged, Size6Align2{});
+ assert(sizeof(tombstone) == sizeof(&my_short));
+
+ assert(!tombstone.__is_engaged());
+ assert(tombstone.__get_payload().a == 1);
+ assert(tombstone.__get_payload().b == 2);
+ assert(tombstone.__get_payload().c == 3);
+
+ tombstone.__engage(std::in_place, &my_short);
+ assert(tombstone.__is_engaged());
+
+ assert(tombstone.__get_value() == &my_short);
+ assert(*tombstone.__get_value() == my_short);
+ }
+
+ {
+ long my_long = 3;
+ std::__tombstoned_value<long*, Size4Align4> tombstone(std::__init_disengaged, Size4Align4{});
+ assert(sizeof(tombstone) == sizeof(&my_long));
+
+ assert(!tombstone.__is_engaged());
+ assert(tombstone.__get_payload().a == 1234);
+
+ tombstone.__engage(std::in_place, &my_long);
+ assert(tombstone.__is_engaged());
+
+ assert(tombstone.__get_value() == &my_long);
+ assert(*tombstone.__get_value() == my_long);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx/memory/tombstone_traits/reference_wrapper.pass.cpp b/libcxx/test/libcxx/memory/tombstone_traits/reference_wrapper.pass.cpp
new file mode 100644
index 00000000000000..a47c1c35f6dace
--- /dev/null
+++ b/libcxx/test/libcxx/memory/tombstone_traits/reference_wrapper.pass.cpp
@@ -0,0 +1,75 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// tombstone_traits<reference_wrapper>
+
+#include <__memory/tombstone_traits.h>
+#include <cassert>
+#include <functional>
+#include <utility>
+
+struct Empty {};
+
+struct Incomplete;
+static_assert(std::__has_tombstone_v<std::reference_wrapper<Incomplete>>);
+
+template <class T>
+constexpr void test_type(T& value) {
+ // start engaged, disengage
+ {
+ std::__tombstoned_value<std::reference_wrapper<T>, Empty> tombstone(std::__init_engaged, value);
+ assert(sizeof(tombstone) == sizeof(std::reference_wrapper<T>));
+
+ assert(tombstone.__is_engaged());
+ assert(&tombstone.__get_value().get() == &value);
+ assert(&std::as_const(tombstone).__get_value().get() == &value);
+ assert(&std::move(tombstone).__get_value().get() == &value);
+
+ tombstone.__disengage(std::in_place);
+ assert(!tombstone.__is_engaged());
+ }
+
+ // start disengaged, then engage
+ {
+ std::__tombstoned_value<std::reference_wrapper<T>, Empty> tombstone(std::__init_disengaged);
+ assert(sizeof(tombstone) == sizeof(std::reference_wrapper<T>));
+
+ assert(!tombstone.__is_engaged());
+
+ tombstone.__engage(std::in_place, value);
+ assert(tombstone.__is_engaged());
+ assert(&tombstone.__get_value().get() == &value);
+ }
+}
+
+struct UserDefined {
+ int a = 1;
+};
+
+constexpr bool test() {
+ int my_int = 1;
+ test_type<int>(my_int);
+ test_type<const int>(my_int);
+
+ char my_char = 'a';
+ test_type<char>(my_char);
+
+ UserDefined my_struct;
+ test_type<UserDefined>(my_struct);
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx/memory/tombstone_traits/string.pass.cpp b/libcxx/test/libcxx/memory/tombstone_traits/string.pass.cpp
new file mode 100644
index 00000000000000..43889fac50b0d0
--- /dev/null
+++ b/libcxx/test/libcxx/memory/tombstone_traits/string.pass.cpp
@@ -0,0 +1,100 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// UNSUPPORTED: 32-bit-pointer
+// TODO: Expand tests to accommodate 32-bit pointers
+
+// tombstone_traits<basic_string>
+
+#include <__memory/tombstone_traits.h>
+#include <cassert>
+#include <cstddef>
+#include <string>
+#include <utility>
+
+struct Empty {
+ friend constexpr bool operator==(const Empty&, const Empty&) = default;
+};
+
+struct Size7Align1 {
+ char payload[7] = {'h', 'e', 'l', 'l', 'o', '1', '\0'};
+
+ friend constexpr bool operator==(const Size7Align1&, const Size7Align1&) = default;
+};
+
+struct Size16Align8 {
+ long long a = 1, b = 2;
+
+ friend constexpr bool operator==(const Size16Align8&, const Size16Align8&) = default;
+};
+
+template <class String, class Payload>
+constexpr void test_payload(std::size_t size) {
+ using CharT = typename String::value_type;
+ const String expected(size, CharT('a'));
+
+ // start engaged, disengage
+ {
+ std::__tombstoned_value<String, Payload> tombstone(std::__init_engaged, expected);
+ assert(sizeof(tombstone) == sizeof(String));
+
+ assert(tombstone.__is_engaged());
+ assert(tombstone.__get_value() == expected);
+ assert(std::as_const(tombstone).__get_value() == expected);
+
+ tombstone.__disengage(std::in_place, Payload{});
+ assert(!tombstone.__is_engaged());
+ assert(tombstone.__get_payload() == Payload{});
+ }
+
+ // start disengaged, then engage
+ {
+ std::__tombstoned_value<String, Payload> tombstone(std::__init_disengaged, Payload{});
+ assert(sizeof(tombstone) == sizeof(String));
+
+ assert(!tombstone.__is_engaged());
+ assert(tombstone.__get_payload() == Payload{});
+
+ tombstone.__engage(std::in_place, expected);
+ assert(tombstone.__is_engaged());
+ assert(tombstone.__get_value() == expected);
+ }
+}
+
+template <class String>
+constexpr void test_string() {
+ // Exercises both the short and the long representation
+ for (std::size_t size : {std::size_t(0), std::size_t(1), std::size_t(100)}) {
+ test_payload<String, Empty>(size);
+ test_payload<String, Size7Align1>(size);
+ test_payload<String, Size16Align8>(size);
+ }
+}
+
+constexpr bool test() {
+ test_string<std::string>();
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+ test_string<std::wstring>();
+#endif
+#ifndef TEST_HAS_NO_CHAR8_T
+ test_string<std::u8string>();
+#endif
+ test_string<std::u16string>();
+ test_string<std::u32string>();
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx/memory/tombstone_traits/unique_ptr.pass.cpp b/libcxx/test/libcxx/memory/tombstone_traits/unique_ptr.pass.cpp
new file mode 100644
index 00000000000000..0539e8d81474a0
--- /dev/null
+++ b/libcxx/test/libcxx/memory/tombstone_traits/unique_ptr.pass.cpp
@@ -0,0 +1,151 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// UNSUPPORTED: 32-bit-pointer
+// TODO: Expand tests to accommodate 32-bit pointers
+
+// tombstone_traits<unique_ptr>
+
+#include <__memory/tombstone_traits.h>
+#include <cassert>
+#include <memory>
+#include <utility>
+
+struct UserDefined {};
+
+struct CustomDeleter {
+ constexpr void operator()(int* ptr) const { delete ptr; }
+};
+
+// Only pointers that have a tombstone themselves and the default deleter get a tombstone
+static_assert(std::__has_tombstone_v<std::unique_ptr<int>>);
+static_assert(std::__has_tombstone_v<std::unique_ptr<int[]>>);
+static_assert(std::__has_tombstone_v<std::unique_ptr<int*>>);
+static_assert(!std::__has_tombstone_v<std::unique_ptr<char>>);
+static_assert(!std::__has_tombstone_v<std::unique_ptr<char[]>>);
+static_assert(!std::__has_tombstone_v<std::unique_ptr<UserDefined>>);
+static_assert(!std::__has_tombstone_v<std::unique_ptr<int, CustomDeleter>>);
+
+// Payloads that can fit into the available bytes given their padding
+
+struct Size7Align1 {
+ char payload[7] = {'h', 'e', 'l', 'l', 'o', '1', '\0'};
+
+ friend constexpr bool operator==(const Size7Align1&, const Size7Align1&) = default;
+};
+
+struct Size6Align2 {
+ short a = 1, b = 2, c = 3;
+
+ friend constexpr bool operator==(const Size6Align2&, const Size6Align2&) = default;
+};
+
+struct Size4Align4 {
+ int a = 1234;
+
+ friend constexpr bool operator==(const Size4Align4&, const Size4Align4&) = default;
+};
+
+template <class Payload>
+constexpr void test_single_object() {
+ // start engaged, disengage
+ {
+ std::__tombstoned_value<std::unique_ptr<int>, Payload> tombstone(std::__init_engaged, new int(3));
+ assert(sizeof(tombstone) == sizeof(std::unique_ptr<int>));
+
+ assert(tombstone.__is_engaged());
+ assert(*tombstone.__get_value() == 3);
+ assert(*std::as_const(tombstone).__get_value() == 3);
+
+ tombstone.__disengage(std::in_place, Payload{});
+ assert(!tombstone.__is_engaged());
+ assert(tombstone.__get_payload() == Payload{});
+ }
+
+ // start disengaged, then engage
+ {
+ std::__tombstoned_value<std::unique_ptr<int>, Payload> tombstone(std::__init_disengaged, Payload{});
+ assert(sizeof(tombstone) == sizeof(std::unique_ptr<int>));
+
+ assert(!tombstone.__is_engaged());
+ assert(tombstone.__get_payload() == Payload{});
+
+ tombstone.__engage(std::in_place, new int(3));
+ assert(tombstone.__is_engaged());
+ assert(*tombstone.__get_value() == 3);
+ }
+
+ // an empty unique_ptr is still engaged
+ {
+ std::__tombstoned_value<std::unique_ptr<int>, Payload> tombstone(std::__init_engaged);
+ assert(tombstone.__is_engaged());
+ assert(tombstone.__get_value() == nullptr);
+ }
+
+ // moving the value out leaves it engaged with an empty unique_ptr
+ {
+ std::__tombstoned_value<std::unique_ptr<int>, Payload> tombstone(std::__init_engaged, new int(3));
+ std::unique_ptr<int> moved = std::move(tombstone).__get_value();
+ assert(*moved == 3);
+
+ assert(tombstone.__is_engaged());
+ assert(tombstone.__get_value() == nullptr);
+ }
+}
+
+template <class Payload>
+constexpr void test_array() {
+ // start engaged, disengage
+ {
+ std::__tombstoned_value<std::unique_ptr<int[]>, Payload> tombstone(std::__init_engaged, new int[2]{3, 4});
+ assert(sizeof(tombstone) == sizeof(std::unique_ptr<int[]>));
+
+ assert(tombstone.__is_engaged());
+ assert(tombstone.__get_value()[0] == 3);
+ assert(tombstone.__get_value()[1] == 4);
+
+ tombstone.__disengage(std::in_place, Payload{});
+ assert(!tombstone.__is_engaged());
+ assert(tombstone.__get_payload() == Payload{});
+ }
+
+ // start disengaged, then engage
+ {
+ std::__tombstoned_value<std::unique_ptr<int[]>, Payload> tombstone(std::__init_disengaged, Payload{});
+ assert(sizeof(tombstone) == sizeof(std::unique_ptr<int[]>));
+
+ assert(!tombstone.__is_engaged());
+ assert(tombstone.__get_payload() == Payload{});
+
+ tombstone.__engage(std::in_place, new int[2]{3, 4});
+ assert(tombstone.__is_engaged());
+ assert(tombstone.__get_value()[0] == 3);
+ assert(tombstone.__get_value()[1] == 4);
+ }
+}
+
+constexpr bool test() {
+ test_single_object<Size7Align1>();
+ test_single_object<Size6Align2>();
+ test_single_object<Size4Align4>();
+
+ test_array<Size7Align1>();
+ test_array<Size6Align2>();
+ test_array<Size4Align4>();
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
>From 708ec9be99b16b6a9c2b4c365b905faefecf57b4 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Sat, 31 Oct 2026 02:32:42 -0400
Subject: [PATCH 10/12] Revert some changes
---
libcxx/include/__utility/pair.h | 2 +-
libcxx/src/locale.cpp | 2 -
.../test/libcxx/transitive_includes/cxx03.csv | 1 -
.../test/libcxx/transitive_includes/cxx11.csv | 1 -
.../test/libcxx/transitive_includes/cxx14.csv | 1 -
.../test/libcxx/transitive_includes/cxx17.csv | 1 -
.../test/libcxx/transitive_includes/cxx20.csv | 2 -
.../test/libcxx/transitive_includes/cxx23.csv | 4 --
.../test/libcxx/transitive_includes/cxx26.csv | 4 --
.../optional/tombstone_types.pass.cpp | 68 -------------------
10 files changed, 1 insertion(+), 85 deletions(-)
delete mode 100644 libcxx/test/std/utilities/optional/tombstone_types.pass.cpp
diff --git a/libcxx/include/__utility/pair.h b/libcxx/include/__utility/pair.h
index 86dc245d91493c..a65c178a9f0360 100644
--- a/libcxx/include/__utility/pair.h
+++ b/libcxx/include/__utility/pair.h
@@ -433,7 +433,7 @@ struct pair
#if _LIBCPP_STD_VER >= 17
template <class _T1, class _T2>
pair(_T1, _T2) -> pair<_T1, _T2>;
-#endif // _LIBCPP_STD_VER >= 17
+#endif
// [pairs.spec], specialized algorithms
diff --git a/libcxx/src/locale.cpp b/libcxx/src/locale.cpp
index 495c61696ccfdd..7795e9eab2fc7b 100644
--- a/libcxx/src/locale.cpp
+++ b/libcxx/src/locale.cpp
@@ -142,8 +142,6 @@ 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 929ba564d4381f..201fc2f36bf482 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -234,7 +234,6 @@ experimental/iterator tuple
experimental/iterator typeinfo
experimental/iterator version
experimental/memory cstdint
-experimental/memory cstdlib
experimental/memory cstring
experimental/memory version
experimental/propagate_const version
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index adf7ebf2d7c80d..ab8791f9aa0747 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -234,7 +234,6 @@ experimental/iterator tuple
experimental/iterator typeinfo
experimental/iterator version
experimental/memory cstdint
-experimental/memory cstdlib
experimental/memory cstring
experimental/memory version
experimental/propagate_const version
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index c1274500243f96..3d5030b5644e2b 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -234,7 +234,6 @@ experimental/iterator tuple
experimental/iterator typeinfo
experimental/iterator version
experimental/memory cstdint
-experimental/memory cstdlib
experimental/memory cstring
experimental/memory version
experimental/propagate_const version
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 0896b2f1b72673..8e3b1de5efd7ee 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -243,7 +243,6 @@ experimental/iterator tuple
experimental/iterator typeinfo
experimental/iterator version
experimental/memory cstdint
-experimental/memory cstdlib
experimental/memory cstring
experimental/memory version
experimental/propagate_const version
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 4ea0167247c080..b9d1dd17aa016f 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -196,7 +196,6 @@ condition_variable typeinfo
condition_variable version
coroutine compare
coroutine cstdint
-coroutine cstdlib
coroutine cstring
coroutine limits
coroutine version
@@ -279,7 +278,6 @@ experimental/iterator typeinfo
experimental/iterator variant
experimental/iterator version
experimental/memory cstdint
-experimental/memory cstdlib
experimental/memory cstring
experimental/memory version
experimental/propagate_const version
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index 75120224ed18eb..83125257f35f54 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -199,7 +199,6 @@ coroutine compare
coroutine cstdint
coroutine cstring
coroutine limits
-coroutine new
coroutine version
cstddef version
ctgmath bitset
@@ -283,7 +282,6 @@ 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
@@ -297,7 +295,6 @@ experimental/utility compare
experimental/utility cstdint
experimental/utility initializer_list
experimental/utility limits
-experimental/utility new
experimental/utility utility
experimental/utility version
filesystem cctype
@@ -1100,7 +1097,6 @@ 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 cb99de928efcc3..229e85afd106b7 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -196,7 +196,6 @@ coroutine compare
coroutine cstdint
coroutine cstring
coroutine limits
-coroutine new
coroutine version
cstddef version
ctgmath bitset
@@ -280,7 +279,6 @@ 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
@@ -295,7 +293,6 @@ experimental/utility cstdint
experimental/utility cstring
experimental/utility initializer_list
experimental/utility limits
-experimental/utility new
experimental/utility utility
experimental/utility version
filesystem cctype
@@ -1093,7 +1090,6 @@ utility cstdint
utility cstring
utility initializer_list
utility limits
-utility new
utility version
valarray cmath
valarray cstdint
diff --git a/libcxx/test/std/utilities/optional/tombstone_types.pass.cpp b/libcxx/test/std/utilities/optional/tombstone_types.pass.cpp
deleted file mode 100644
index 43cab57d250713..00000000000000
--- a/libcxx/test/std/utilities/optional/tombstone_types.pass.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// 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
-}
>From bf1f0465560444688b3357bfdf50e11b350c3000 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Sat, 31 Oct 2026 02:35:19 -0400
Subject: [PATCH 11/12] Update string_view
---
libcxx/include/string_view | 8 +-
.../tombstone_traits/string_view.pass.cpp | 122 ++++++++++++++++++
2 files changed, 127 insertions(+), 3 deletions(-)
create mode 100644 libcxx/test/libcxx/memory/tombstone_traits/string_view.pass.cpp
diff --git a/libcxx/include/string_view b/libcxx/include/string_view
index 89f88aabc3dbf2..812448a6a54ac5 100644
--- a/libcxx/include/string_view
+++ b/libcxx/include/string_view
@@ -725,6 +725,10 @@ private:
template <class, class, class>
friend class basic_string;
+
+# if _LIBCPP_STD_VER >= 20
+ friend struct __tombstone_traits<basic_string_view>;
+# endif
};
_LIBCPP_CTAD_SUPPORTED_FOR_TYPE(basic_string_view);
@@ -734,15 +738,13 @@ inline constexpr bool ranges::enable_view<basic_string_view<_CharT, _Traits>> =
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
+# endif // _LIBCPP_STD_VER >= 20
// [string.view.deduct]
diff --git a/libcxx/test/libcxx/memory/tombstone_traits/string_view.pass.cpp b/libcxx/test/libcxx/memory/tombstone_traits/string_view.pass.cpp
new file mode 100644
index 00000000000000..00d9f91b057fa4
--- /dev/null
+++ b/libcxx/test/libcxx/memory/tombstone_traits/string_view.pass.cpp
@@ -0,0 +1,122 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++20
+
+// UNSUPPORTED: 32-bit-pointer
+// TODO: Expand tests to accommodate 32-bit pointers
+
+// tombstone_traits<basic_string_view>
+
+#include <__memory/tombstone_traits.h>
+#include <cassert>
+#include <string_view>
+#include <utility>
+
+#include "test_macros.h"
+
+// The tombstone relies on the low bit of the data pointer being unset, so characters must have an alignment of at
+// least 2.
+static_assert(!std::__has_tombstone_v<std::string_view>);
+#ifndef TEST_HAS_NO_CHAR8_T
+static_assert(!std::__has_tombstone_v<std::u8string_view>);
+#endif
+static_assert(std::__has_tombstone_v<std::u16string_view>);
+static_assert(std::__has_tombstone_v<std::u32string_view>);
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+static_assert(std::__has_tombstone_v<std::wstring_view>);
+#endif
+
+struct Empty {
+ friend constexpr bool operator==(const Empty&, const Empty&) = default;
+};
+
+// Payloads that can fit into the available bytes given their padding
+
+struct Size7Align1 {
+ char payload[7] = {'h', 'e', 'l', 'l', 'o', '1', '\0'};
+
+ friend constexpr bool operator==(const Size7Align1&, const Size7Align1&) = default;
+};
+
+struct Size6Align2 {
+ short a = 1, b = 2, c = 3;
+
+ friend constexpr bool operator==(const Size6Align2&, const Size6Align2&) = default;
+};
+
+struct Size4Align4 {
+ int a = 1234;
+
+ friend constexpr bool operator==(const Size4Align4&, const Size4Align4&) = default;
+};
+
+template <class View, class Payload>
+constexpr void test_payload(View expected) {
+ // start engaged, disengage
+ {
+ std::__tombstoned_value<View, Payload> tombstone(std::__init_engaged, expected);
+ assert(sizeof(tombstone) == sizeof(View));
+
+ assert(tombstone.__is_engaged());
+ assert(tombstone.__get_value().data() == expected.data());
+ assert(tombstone.__get_value().size() == expected.size());
+ assert(std::as_const(tombstone).__get_value() == expected);
+
+ tombstone.__disengage(std::in_place, Payload{});
+ assert(!tombstone.__is_engaged());
+ assert(tombstone.__get_payload() == Payload{});
+ }
+
+ // start disengaged, then engage
+ {
+ std::__tombstoned_value<View, Payload> tombstone(std::__init_disengaged, Payload{});
+ assert(sizeof(tombstone) == sizeof(View));
+
+ assert(!tombstone.__is_engaged());
+ assert(tombstone.__get_payload() == Payload{});
+
+ tombstone.__engage(std::in_place, expected.data(), expected.size());
+ assert(tombstone.__is_engaged());
+ assert(tombstone.__get_value().data() == expected.data());
+ assert(tombstone.__get_value().size() == expected.size());
+ }
+
+ // a default constructed string_view has a null data pointer, but it's still engaged
+ {
+ std::__tombstoned_value<View, Payload> tombstone(std::__init_engaged);
+ assert(tombstone.__is_engaged());
+ assert(tombstone.__get_value().data() == nullptr);
+ assert(tombstone.__get_value().empty());
+ }
+}
+
+template <class View>
+constexpr void test_view(View expected) {
+ test_payload<View, Empty>(expected);
+ test_payload<View, Size7Align1>(expected);
+ test_payload<View, Size6Align2>(expected);
+ test_payload<View, Size4Align4>(expected);
+}
+
+constexpr bool test() {
+ test_view<std::u16string_view>(u"hello");
+ test_view<std::u32string_view>(U"hello");
+#ifndef TEST_HAS_NO_WIDE_CHARACTERS
+ test_view<std::wstring_view>(L"hello");
+#endif
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ return 0;
+}
>From af411986a2ad1eccb062e8a9c62e2d8f212fa9a0 Mon Sep 17 00:00:00 2001
From: William Tran-Viet <wtranviet at proton.me>
Date: Sat, 31 Oct 2026 02:37:13 -0400
Subject: [PATCH 12/12] revert more things
---
libcxx/include/__configuration/abi.h | 1 -
libcxx/include/__memory/shared_ptr.h | 16 ++++++++--------
2 files changed, 8 insertions(+), 9 deletions(-)
diff --git a/libcxx/include/__configuration/abi.h b/libcxx/include/__configuration/abi.h
index e637e217b5b8f7..9343eacf393449 100644
--- a/libcxx/include/__configuration/abi.h
+++ b/libcxx/include/__configuration/abi.h
@@ -86,7 +86,6 @@
# if defined(__FreeBSD__)
# define _LIBCPP_DEPRECATED_ABI_DISABLE_PAIR_TRIVIAL_COPY_CTOR
# endif
-
#endif
// We had some bugs where we use [[no_unique_address]] together with construct_at,
diff --git a/libcxx/include/__memory/shared_ptr.h b/libcxx/include/__memory/shared_ptr.h
index 21c311640b874c..0db58b37a6288b 100644
--- a/libcxx/include/__memory/shared_ptr.h
+++ b/libcxx/include/__memory/shared_ptr.h
@@ -654,6 +654,13 @@ class _LIBCPP_SHARED_PTR_TRIVIAL_ABI shared_ptr {
friend class weak_ptr;
};
+#if _LIBCPP_STD_VER >= 17
+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
+
//
// std::allocate_shared and std::make_shared
//
@@ -1288,19 +1295,12 @@ 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>;
-
-template <class _Tp>
-struct __tombstone_traits<weak_ptr<_Tp>> {
-
-};
-#endif // _LIBCPP_STD_VER >= 17
+#endif
template <class _Tp>
inline _LIBCPP_HIDE_FROM_ABI void swap(weak_ptr<_Tp>& __x, weak_ptr<_Tp>& __y) _NOEXCEPT {
More information about the libcxx-commits
mailing list