Skip to content

Commit 522dfe8

Browse files
committed
[libc++] Add tombstone traits to use in optional, variant
1 parent f43ef53 commit 522dfe8

File tree

15 files changed

+436
-3
lines changed

15 files changed

+436
-3
lines changed

libcxx/include/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,7 @@ set(files
559559
__memory/swap_allocator.h
560560
__memory/temp_value.h
561561
__memory/temporary_buffer.h
562+
__memory/tombstone_traits.h
562563
__memory/uninitialized_algorithms.h
563564
__memory/unique_ptr.h
564565
__memory/unique_temporary_buffer.h

libcxx/include/__expected/expected.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <__functional/invoke.h>
1818
#include <__memory/addressof.h>
1919
#include <__memory/construct_at.h>
20+
#include <__memory/tombstone_traits.h>
2021
#include <__type_traits/conditional.h>
2122
#include <__type_traits/conjunction.h>
2223
#include <__type_traits/disjunction.h>

libcxx/include/__functional/reference_wrapper.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@
1313
#include <__compare/synth_three_way.h>
1414
#include <__concepts/boolean_testable.h>
1515
#include <__config>
16+
#include <__cstddef/size_t.h>
1617
#include <__functional/weak_result_type.h>
1718
#include <__memory/addressof.h>
19+
#include <__memory/tombstone_traits.h>
1820
#include <__type_traits/enable_if.h>
1921
#include <__type_traits/invoke.h>
2022
#include <__type_traits/is_const.h>
@@ -122,6 +124,12 @@ template <class _Tp>
122124
reference_wrapper(_Tp&) -> reference_wrapper<_Tp>;
123125
#endif
124126

127+
template <class _Tp>
128+
struct __tombstone_traits<reference_wrapper<_Tp>> {
129+
static constexpr uintptr_t __disengaged_value_ = 0;
130+
static constexpr size_t __is_disengaged_offset_ = 0;
131+
};
132+
125133
template <class _Tp>
126134
inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 reference_wrapper<_Tp> ref(_Tp& __t) _NOEXCEPT {
127135
return reference_wrapper<_Tp>(__t);

libcxx/include/__locale

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <__config>
1414
#include <__locale_dir/locale_base_api.h>
1515
#include <__memory/shared_ptr.h> // __shared_count
16+
#include <__memory/tombstone_traits.h>
1617
#include <__mutex/once_flag.h>
1718
#include <__type_traits/make_unsigned.h>
1819
#include <__utility/no_destroy.h>
@@ -114,6 +115,9 @@ private:
114115
friend const _Facet& use_facet(const locale&);
115116
};
116117

118+
template <>
119+
struct __tombstone_traits<locale> : __tombstone_traits_assume_aligned_pointer {};
120+
117121
class _LIBCPP_EXPORTED_FROM_ABI locale::facet : public __shared_count {
118122
protected:
119123
_LIBCPP_HIDE_FROM_ABI explicit facet(size_t __refs = 0) : __shared_count(static_cast<long>(__refs) - 1) {}

libcxx/include/__memory/shared_ptr.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -833,6 +833,13 @@ template <class _Tp, class _Dp>
833833
shared_ptr(unique_ptr<_Tp, _Dp>) -> shared_ptr<_Tp>;
834834
#endif
835835

836+
template <class _Tp>
837+
struct __tombstone_traits<shared_ptr<_Tp>> {
838+
static constexpr auto __disengaged_value_ = __tombstone_traits_assume_aligned_pointer::__disengaged_value_;
839+
static constexpr size_t __is_disengaged_offset_ =
840+
sizeof(void*) + __tombstone_traits_assume_aligned_pointer::__is_disengaged_offset_;
841+
};
842+
836843
//
837844
// std::allocate_shared and std::make_shared
838845
//
@@ -1383,6 +1390,13 @@ template <class _Tp>
13831390
weak_ptr(shared_ptr<_Tp>) -> weak_ptr<_Tp>;
13841391
#endif
13851392

1393+
template <class _Tp>
1394+
struct __tombstone_traits<weak_ptr<_Tp>> {
1395+
static constexpr auto __disengaged_value_ = __tombstone_traits_assume_aligned_pointer::__disengaged_value_;
1396+
static constexpr size_t __is_disengaged_offset_ =
1397+
sizeof(void*) + __tombstone_traits_assume_aligned_pointer::__is_disengaged_offset_;
1398+
};
1399+
13861400
template <class _Tp>
13871401
inline _LIBCPP_CONSTEXPR weak_ptr<_Tp>::weak_ptr() _NOEXCEPT : __ptr_(nullptr), __cntrl_(nullptr) {}
13881402

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef _LIBCPP___TYPE_TRAITS_DISENGAGED_TRAITS_H
10+
#define _LIBCPP___TYPE_TRAITS_DISENGAGED_TRAITS_H
11+
12+
#include <__assert>
13+
#include <__config>
14+
#include <__memory/construct_at.h>
15+
#include <__type_traits/datasizeof.h>
16+
#include <__type_traits/enable_if.h>
17+
#include <__type_traits/is_constant_evaluated.h>
18+
#include <__type_traits/is_fundamental.h>
19+
#include <__type_traits/is_integral.h>
20+
#include <__type_traits/is_trivial.h>
21+
#include <__type_traits/is_trivially_destructible.h>
22+
#include <__type_traits/remove_cv.h>
23+
#include <__type_traits/void_t.h>
24+
#include <__utility/forward.h>
25+
#include <__utility/in_place.h>
26+
#include <__utility/piecewise_construct.h>
27+
#include <cstdint>
28+
29+
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
30+
# pragma GCC system_header
31+
#endif
32+
33+
_LIBCPP_BEGIN_NAMESPACE_STD
34+
35+
template <class>
36+
struct __tombstone_traits;
37+
38+
// bools have always exactly one bit set. If there is more than one set it's disengaged.
39+
template <>
40+
struct __tombstone_traits<bool> {
41+
static constexpr uint8_t __disengaged_value_ = 3;
42+
static constexpr size_t __is_disengaged_offset_ = 0;
43+
};
44+
45+
struct __tombstone_traits_assume_aligned_pointer {
46+
static constexpr uint8_t __disengaged_value_ = 1;
47+
#ifdef _LIBCPP_LITTLE_ENDIAN
48+
static constexpr size_t __is_disengaged_offset_ = 0;
49+
#else
50+
static constexpr size_t __is_disengaged_offset_ = sizeof(void*) - 1;
51+
#endif
52+
};
53+
54+
// TODO: Look into
55+
// - filesystem::directory_iterator
56+
// - vector<T> with alignof(T) == 1
57+
// - string_view (basic_string_view<T> works with alignof(T) >= 2)
58+
59+
// This is constrained on fundamental types because we might not always know the alignment of a user-defined type.
60+
// For example, in one TU there may only be a forward declaration and in another there is already the definition
61+
// available. If we made this optimization conditional on the completeness of the type this would result in a non-benign
62+
// ODR violation.
63+
template <class _Tp>
64+
struct __tombstone_traits<__enable_specialization_if<is_fundamental_v<_Tp> && alignof(_Tp) >= 2, _Tp*>>
65+
: __tombstone_traits_assume_aligned_pointer {};
66+
67+
template <class _Tp>
68+
struct __tombstone_traits<_Tp**> : __tombstone_traits_assume_aligned_pointer {
69+
static_assert(alignof(_Tp*) >= 2, "alignment of a pointer isn't at least 2!?");
70+
};
71+
72+
inline constexpr struct __init_engaged_t {
73+
} __init_engaged;
74+
inline constexpr struct __init_disengaged_t {
75+
} __init_disengaged;
76+
77+
template <class _Tp, class _Payload>
78+
struct __tombstone_is_disengaged {
79+
using _TombstoneLayout = __tombstone_traits<_Tp>;
80+
using _IsDisengagedT = remove_cv_t<decltype(_TombstoneLayout::__disengaged_value_)>;
81+
82+
char __padding_[_TombstoneLayout::__is_disengaged_offset_];
83+
_IsDisengagedT __is_disengaged_;
84+
};
85+
86+
template <class _Tp, class _Payload>
87+
requires(__tombstone_traits<_Tp>::__is_disengaged_offset_ == 0)
88+
struct __tombstone_is_disengaged<_Tp, _Payload> {
89+
using _TombstoneLayout = __tombstone_traits<_Tp>;
90+
using _IsDisengagedT = remove_cv_t<decltype(_TombstoneLayout::__disengaged_value_)>;
91+
92+
_IsDisengagedT __is_disengaged_;
93+
};
94+
95+
template <class _Tp, class _Payload>
96+
struct __tombstone_data {
97+
using _TombstoneLayout = __tombstone_traits<_Tp>;
98+
using _IsDisengagedT = remove_cv_t<decltype(_TombstoneLayout::__disengaged_value_)>;
99+
100+
static_assert(is_trivial<_IsDisengagedT>::value, "disengaged type has to be trivial!");
101+
static_assert(_TombstoneLayout::__is_disengaged_offset_ >= __datasizeof_v<_Payload>);
102+
103+
_LIBCPP_NO_UNIQUE_ADDRESS _Payload __payload_;
104+
char __padding_[_TombstoneLayout::__is_disengaged_offset_ - __datasizeof_v<_Payload>];
105+
_IsDisengagedT __is_disengaged_;
106+
107+
template <class... _Args>
108+
_LIBCPP_HIDE_FROM_ABI constexpr __tombstone_data(_Args&&... __args)
109+
: __payload_(std::forward<_Args>(__args)...), __is_disengaged_(_TombstoneLayout::__disengaged_value_) {}
110+
};
111+
112+
template <class _Tp, class _Payload>
113+
requires(__tombstone_traits<_Tp>::__is_disengaged_offset_ == 0)
114+
struct __tombstone_data<_Tp, _Payload> {
115+
using _TombstoneLayout = __tombstone_traits<_Tp>;
116+
using _IsDisengagedT = remove_cv_t<decltype(_TombstoneLayout::__disengaged_value_)>;
117+
118+
_IsDisengagedT __is_disengaged_;
119+
_LIBCPP_NO_UNIQUE_ADDRESS _Payload __payload_;
120+
121+
template <class... _Args>
122+
_LIBCPP_HIDE_FROM_ABI constexpr __tombstone_data(_Args&&... __args)
123+
: __is_disengaged_(_TombstoneLayout::__disengaged_value_), __payload_(std::forward<_Args>(__args)...) {}
124+
};
125+
126+
template <class _Tp, class _Payload>
127+
struct __tombstoned_value final {
128+
using _TombstoneLayout = __tombstone_traits<_Tp>;
129+
using _TombstoneData = __tombstone_data<_Tp, _Payload>;
130+
131+
union _MaybeTombstone {
132+
_Tp __value_;
133+
_TombstoneData __tombstone_;
134+
135+
template <class... _Args>
136+
constexpr _MaybeTombstone(__init_disengaged_t, _Args&&... __args) : __tombstone_(std::forward<_Args>(__args)...) {}
137+
138+
template <class... _Args>
139+
constexpr _MaybeTombstone(__init_engaged_t, _Args&&... __args) : __value_(std::forward<_Args>(__args)...) {}
140+
141+
_MaybeTombstone(const _MaybeTombstone&) = default;
142+
_MaybeTombstone(_MaybeTombstone&&) = default;
143+
_MaybeTombstone& operator=(const _MaybeTombstone&) = default;
144+
_MaybeTombstone& operator=(_MaybeTombstone&&) = default;
145+
146+
_LIBCPP_HIDE_FROM_ABI constexpr bool __is_engaged() const noexcept {
147+
if (__libcpp_is_constant_evaluated())
148+
return !__builtin_constant_p(__tombstone_.__is_disengaged_ == _TombstoneLayout::__disengaged_value_);
149+
__tombstone_is_disengaged<_Tp, _Payload> __is_disengaged;
150+
static_assert(sizeof(__tombstone_is_disengaged<_Tp, _Payload>) <= sizeof(_MaybeTombstone));
151+
__builtin_memcpy(&__is_disengaged, this, sizeof(__tombstone_is_disengaged<_Tp, _Payload>));
152+
153+
return __is_disengaged.__is_disengaged_ != _TombstoneLayout::__disengaged_value_;
154+
}
155+
156+
_LIBCPP_HIDE_FROM_ABI constexpr ~_MaybeTombstone() {
157+
if (__is_engaged()) {
158+
std::destroy_at(&__value_);
159+
} else {
160+
std::destroy_at(&__tombstone_);
161+
}
162+
}
163+
164+
~_MaybeTombstone()
165+
requires is_trivially_destructible_v<_Tp> && is_trivially_destructible_v<_Payload>
166+
= default;
167+
};
168+
169+
_MaybeTombstone __data_;
170+
171+
static_assert(sizeof(__tombstone_data<_Tp, _Payload>) <= sizeof(_Tp));
172+
static_assert(is_integral_v<decltype(_TombstoneLayout::__disengaged_value_)>);
173+
static_assert(__builtin_offsetof(_TombstoneData, __is_disengaged_) == _TombstoneLayout::__is_disengaged_offset_);
174+
175+
template <class... _Args>
176+
_LIBCPP_HIDE_FROM_ABI constexpr __tombstoned_value(__init_disengaged_t, _Args&&... __args)
177+
: __data_(__init_disengaged, std::forward<_Args>(__args)...) {}
178+
179+
template <class... _Args>
180+
_LIBCPP_HIDE_FROM_ABI constexpr __tombstoned_value(__init_engaged_t, _Args&&... __args)
181+
: __data_(__init_engaged, std::forward<_Args>(__args)...) {}
182+
183+
_LIBCPP_HIDE_FROM_ABI constexpr bool __is_engaged() const noexcept { return __data_.__is_engaged(); }
184+
185+
template <class _Class>
186+
_LIBCPP_HIDE_FROM_ABI constexpr auto&& __get_value(this _Class&& __self) noexcept {
187+
_LIBCPP_ASSERT_INTERNAL(__self.__is_engaged(), "Trying to get the value of a disenagaged tombstoned value");
188+
return std::forward<_Class>(__self).__data_.__value_;
189+
}
190+
191+
template <class _Class>
192+
_LIBCPP_HIDE_FROM_ABI constexpr auto&& __get_payload(this _Class&& __self) noexcept {
193+
_LIBCPP_ASSERT_INTERNAL(!__self.__is_engaged(), "Trying to get the payload of an enagaged tombstoned value");
194+
return std::forward<_Class>(__self).__data_.__tombstone_.__payload_;
195+
}
196+
197+
template <class _Class, class... _Args>
198+
_LIBCPP_HIDE_FROM_ABI constexpr void __engage(this _Class& __self, piecewise_construct_t, _Args&&... __args) {
199+
_LIBCPP_ASSERT_INTERNAL(!__self.__is_engaged(), "Trying to enage a already engaged tombstoned value");
200+
std::destroy_at(&__self.__data_.__tombstone_);
201+
std::construct_at(&__self.__data_.__value_, std::forward<_Args>(__args)...);
202+
}
203+
204+
template <class _Class, class... _Args>
205+
_LIBCPP_HIDE_FROM_ABI constexpr void __disengage(this _Class& __self, piecewise_construct_t, _Args&&... __args) {
206+
_LIBCPP_ASSERT_INTERNAL(!__self.__is_engaged(), "Trying to disenage a disengaged tombstoned value");
207+
std::destroy_at(&__self.__data_.__value_);
208+
std::construct_at(&__self.__data_.__payload_, std::forward<_Args>(__args)...);
209+
__self.__data_.__is_disengaged_ = _TombstoneLayout::__disengaged_value_;
210+
}
211+
};
212+
213+
template <class _Tp, class = void>
214+
inline constexpr bool __has_tombstone_v = false;
215+
216+
template <class _Tp>
217+
inline constexpr bool __has_tombstone_v<_Tp, void_t<decltype(sizeof(__tombstone_traits<_Tp>))>> = true;
218+
219+
_LIBCPP_END_NAMESPACE_STD
220+
221+
#endif // _LIBCPP___TYPE_TRAITS_DISENGAGED_TRAITS_H

libcxx/include/__memory/unique_ptr.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <__memory/auto_ptr.h>
2525
#include <__memory/compressed_pair.h>
2626
#include <__memory/pointer_traits.h>
27+
#include <__memory/tombstone_traits.h>
2728
#include <__type_traits/add_lvalue_reference.h>
2829
#include <__type_traits/common_type.h>
2930
#include <__type_traits/conditional.h>
@@ -405,6 +406,14 @@ struct __unique_ptr_array_bounds_stored {
405406
size_t __size_;
406407
};
407408

409+
template <class _Tp>
410+
struct __tombstone_traits<__enable_specialization_if<__has_tombstone_v<_Tp*>, unique_ptr<_Tp>>>
411+
: __tombstone_traits<_Tp*> {};
412+
413+
template <class _Tp>
414+
struct __tombstone_traits<__enable_specialization_if<__has_tombstone_v<_Tp*>, unique_ptr<_Tp[]>>>
415+
: __tombstone_traits<_Tp*> {};
416+
408417
template <class _Tp, class _Dp>
409418
class _LIBCPP_UNIQUE_PTR_TRIVIAL_ABI _LIBCPP_TEMPLATE_VIS unique_ptr<_Tp[], _Dp> {
410419
public:

libcxx/include/__type_traits/enable_if.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ template <bool _Bp, class _Tp = void>
3232
using enable_if_t = typename enable_if<_Bp, _Tp>::type;
3333
#endif
3434

35+
template <bool _Bp, class _Tp, class = enable_if_t<_Bp>>
36+
using __enable_specialization_if = _Tp;
37+
3538
_LIBCPP_END_NAMESPACE_STD
3639

3740
#endif // _LIBCPP___TYPE_TRAITS_ENABLE_IF_H

libcxx/include/__utility/pair.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <__fwd/array.h>
1818
#include <__fwd/pair.h>
1919
#include <__fwd/tuple.h>
20+
#include <__memory/tombstone_traits.h>
2021
#include <__tuple/tuple_indices.h>
2122
#include <__tuple/tuple_like_no_subrange.h>
2223
#include <__tuple/tuple_size.h>
@@ -446,6 +447,18 @@ template <class _T1, class _T2>
446447
pair(_T1, _T2) -> pair<_T1, _T2>;
447448
#endif
448449

450+
template <class _Tp, class _Up>
451+
requires __has_tombstone_v<_Up>
452+
struct __tombstone_traits<pair<_Tp, _Up>> {
453+
static constexpr auto __disengaged_value_ = __tombstone_traits<_Up>::__disengaged_value_;
454+
static constexpr size_t __is_disengaged_offset_ =
455+
sizeof(_Tp) + __tombstone_traits<_Up>::__is_disengaged_offset_;
456+
};
457+
458+
template <class _Tp, class _Up>
459+
requires(!__has_tombstone_v<_Up> && __has_tombstone_v<_Tp>)
460+
struct __tombstone_traits<pair<_Tp, _Up>> : __tombstone_traits<_Tp> {};
461+
449462
// [pairs.spec], specialized algorithms
450463

451464
template <class _T1, class _T2, class _U1, class _U2>

libcxx/include/__vector/vector.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,11 @@ template <ranges::input_range _Range,
808808
vector(from_range_t, _Range&&, _Alloc = _Alloc()) -> vector<ranges::range_value_t<_Range>, _Alloc>;
809809
#endif
810810

811+
template <class _Tp, class _Allocator>
812+
struct __tombstone_traits<
813+
__enable_specialization_if<__has_tombstone_v<typename vector<_Tp, _Allocator>::pointer>, vector<_Tp, _Allocator>>>
814+
: __tombstone_traits<typename vector<_Tp>::pointer> {};
815+
811816
// __swap_out_circular_buffer relocates the objects in [__begin_, __end_) into the front of __v and swaps the buffers of
812817
// *this and __v. It is assumed that __v provides space for exactly (__end_ - __begin_) objects in the front. This
813818
// function has a strong exception guarantee.

0 commit comments

Comments
 (0)