Skip to content

Commit 0d8f517

Browse files
Destructor Tombstones (#5318)
1 parent 3b2a52e commit 0d8f517

File tree

21 files changed

+401
-10
lines changed

21 files changed

+401
-10
lines changed

stl/inc/any

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,20 @@ public:
158158

159159
~any() noexcept {
160160
reset();
161+
162+
#if _MSVC_STL_DESTRUCTOR_TOMBSTONES
163+
// The two least significant bits (LSBs) of _Storage._TypeData encode whether we're in _Small, _Big,
164+
// or _Trivial mode; see _Rep(). The rest of the bits encode the type_info pointer that's used
165+
// to remember what type of object we're storing; see _TypeInfo(). So, we'll take the tombstone value,
166+
// clear out the two LSBs with the _Rep_mask, then set the bits for _Small mode.
167+
// _Small mode will prevent the most misuse, as the pseudo-vtable will be used for _Destroy, _Copy, and _Move.
168+
// _Big mode wouldn't use the pseudo-vtable for _Move, and _Trivial mode doesn't have a pseudo-vtable at all.
169+
const uintptr_t _Tombstone_value{_MSVC_STL_UINTPTR_TOMBSTONE_VALUE};
170+
// Set the pseudo-vtable pointer:
171+
_Storage._SmallStorage._RTTI = reinterpret_cast<const _Any_small_RTTI*>(_Tombstone_value);
172+
// Set the combined type_info pointer and representation mode bits:
173+
_Storage._TypeData = (_Tombstone_value & ~_Rep_mask) | static_cast<uintptr_t>(_Any_representation::_Small);
174+
#endif // _MSVC_STL_DESTRUCTOR_TOMBSTONES
161175
}
162176

163177
// Assignment [any.assign]

stl/inc/deque

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -555,6 +555,18 @@ public:
555555

556556
_Deque_val() noexcept : _Map(), _Mapsize(0), _Myoff(0), _Mysize(0) {}
557557

558+
#if _MSVC_STL_DESTRUCTOR_TOMBSTONES
559+
~_Deque_val() noexcept {
560+
if constexpr (is_pointer_v<_Mapptr>) {
561+
const auto _Tombstone{reinterpret_cast<_Mapptr>(_MSVC_STL_UINTPTR_TOMBSTONE_VALUE)};
562+
_Map = _Tombstone;
563+
_Mapsize = 0;
564+
_Myoff = 0;
565+
_Mysize = 0;
566+
}
567+
}
568+
#endif // _MSVC_STL_DESTRUCTOR_TOMBSTONES
569+
558570
_Map_difference_type _Getblock(size_type _Off) const noexcept {
559571
// NB: _Mapsize and _Block_size are guaranteed to be powers of 2
560572
return static_cast<_Map_difference_type>((_Off / _Block_size) & (_Mapsize - 1));

stl/inc/exception

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,12 @@ public:
234234

235235
~exception_ptr() noexcept {
236236
__ExceptionPtrDestroy(this);
237+
238+
#if _MSVC_STL_DESTRUCTOR_TOMBSTONES
239+
const auto _Tombstone{reinterpret_cast<void*>(_MSVC_STL_UINTPTR_TOMBSTONE_VALUE)};
240+
_Data1 = _Tombstone;
241+
_Data2 = _Tombstone;
242+
#endif // _MSVC_STL_DESTRUCTOR_TOMBSTONES
237243
}
238244

239245
exception_ptr(const exception_ptr& _Rhs) noexcept {

stl/inc/forward_list

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,15 @@ public:
277277

278278
_Flist_val() noexcept : _Myhead() {} // initialize data
279279

280+
#if _MSVC_STL_DESTRUCTOR_TOMBSTONES
281+
~_Flist_val() noexcept {
282+
if constexpr (is_pointer_v<_Nodeptr>) {
283+
const auto _Tombstone{reinterpret_cast<_Nodeptr>(_MSVC_STL_UINTPTR_TOMBSTONE_VALUE)};
284+
_Myhead = _Tombstone;
285+
}
286+
}
287+
#endif // _MSVC_STL_DESTRUCTOR_TOMBSTONES
288+
280289
_Nodeptr _Before_head() const noexcept { // return pointer to the "before begin" pseudo node
281290
return pointer_traits<_Nodeptr>::pointer_to(reinterpret_cast<_Node&>(const_cast<_Nodeptr&>(_Myhead)));
282291
}

stl/inc/functional

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -928,6 +928,11 @@ public:
928928

929929
~_Func_class() noexcept {
930930
_Tidy();
931+
932+
#if _MSVC_STL_DESTRUCTOR_TOMBSTONES
933+
const auto _Tombstone{reinterpret_cast<_Ptrt*>(_MSVC_STL_UINTPTR_TOMBSTONE_VALUE)};
934+
_Set(_Tombstone);
935+
#endif // _MSVC_STL_DESTRUCTOR_TOMBSTONES
931936
}
932937

933938
protected:
@@ -1934,6 +1939,11 @@ public:
19341939
// Do cleanup in this class destructor rather than base,
19351940
// so that if object construction throws, the unnecessary cleanup isn't called.
19361941
this->_Checked_destroy(this->_Data);
1942+
1943+
#if _MSVC_STL_DESTRUCTOR_TOMBSTONES
1944+
const auto _Tombstone{reinterpret_cast<const void*>(_MSVC_STL_UINTPTR_TOMBSTONE_VALUE)};
1945+
this->_Data._Impl = _Tombstone;
1946+
#endif // _MSVC_STL_DESTRUCTOR_TOMBSTONES
19371947
}
19381948

19391949
move_only_function& operator=(nullptr_t) noexcept {

stl/inc/list

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,16 @@ public:
352352

353353
_List_val() noexcept : _Myhead(), _Mysize(0) {} // initialize data
354354

355+
#if _MSVC_STL_DESTRUCTOR_TOMBSTONES
356+
~_List_val() noexcept {
357+
if constexpr (is_pointer_v<_Nodeptr>) {
358+
const auto _Tombstone{reinterpret_cast<_Nodeptr>(_MSVC_STL_UINTPTR_TOMBSTONE_VALUE)};
359+
_Myhead = _Tombstone;
360+
_Mysize = 0;
361+
}
362+
}
363+
#endif // _MSVC_STL_DESTRUCTOR_TOMBSTONES
364+
355365
void _Orphan_ptr2(_Nodeptr _Ptr) noexcept { // orphan iterators with specified node pointers
356366
#if _ITERATOR_DEBUG_LEVEL == 2
357367
_Lockit _Lock(_LOCK_DEBUG);

stl/inc/memory

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1311,7 +1311,15 @@ protected:
13111311

13121312
constexpr _Ptr_base() noexcept = default;
13131313

1314+
#if _MSVC_STL_DESTRUCTOR_TOMBSTONES
1315+
~_Ptr_base() noexcept {
1316+
const uintptr_t _Tombstone_value{_MSVC_STL_UINTPTR_TOMBSTONE_VALUE};
1317+
_Ptr = reinterpret_cast<element_type*>(_Tombstone_value);
1318+
_Rep = reinterpret_cast<_Ref_count_base*>(_Tombstone_value);
1319+
}
1320+
#else // ^^^ _MSVC_STL_DESTRUCTOR_TOMBSTONES / !_MSVC_STL_DESTRUCTOR_TOMBSTONES vvv
13141321
~_Ptr_base() = default;
1322+
#endif // ^^^ !_MSVC_STL_DESTRUCTOR_TOMBSTONES ^^^
13151323

13161324
template <class _Ty2>
13171325
void _Move_construct_from(_Ptr_base<_Ty2>&& _Right) noexcept {
@@ -3418,6 +3426,15 @@ public:
34183426
if (_Mypair._Myval2) {
34193427
_Mypair._Get_first()(_Mypair._Myval2);
34203428
}
3429+
3430+
#if _MSVC_STL_DESTRUCTOR_TOMBSTONES
3431+
if constexpr (is_pointer_v<pointer>) {
3432+
if (!_STD _Is_constant_evaluated()) {
3433+
const auto _Tombstone{reinterpret_cast<pointer>(_MSVC_STL_UINTPTR_TOMBSTONE_VALUE)};
3434+
_Mypair._Myval2 = _Tombstone;
3435+
}
3436+
}
3437+
#endif // _MSVC_STL_DESTRUCTOR_TOMBSTONES
34213438
}
34223439

34233440
_NODISCARD _CONSTEXPR23 _Dx& get_deleter() noexcept {
@@ -3555,6 +3572,15 @@ public:
35553572
if (_Mypair._Myval2) {
35563573
_Mypair._Get_first()(_Mypair._Myval2);
35573574
}
3575+
3576+
#if _MSVC_STL_DESTRUCTOR_TOMBSTONES
3577+
if constexpr (is_pointer_v<pointer>) {
3578+
if (!_STD _Is_constant_evaluated()) {
3579+
const auto _Tombstone{reinterpret_cast<pointer>(_MSVC_STL_UINTPTR_TOMBSTONE_VALUE)};
3580+
_Mypair._Myval2 = _Tombstone;
3581+
}
3582+
}
3583+
#endif // _MSVC_STL_DESTRUCTOR_TOMBSTONES
35583584
}
35593585

35603586
_NODISCARD _CONSTEXPR23 _Dx& get_deleter() noexcept {

stl/inc/optional

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ struct _Optional_destruct_base { // either contains a value of _Ty or is empty (
8888
: _Value(_STD invoke(_STD forward<_Fn>(_Func), _STD forward<_Ux>(_Arg))), _Has_value{true} {}
8989
#endif // _HAS_CXX23
9090

91+
// For the trivially destructible case, we can't add a destructor for _MSVC_STL_DESTRUCTOR_TOMBSTONES, due to
92+
// N5001 [optional.dtor]/2: "Remarks: If is_trivially_destructible_v<T> is true, then this destructor is trivial."
93+
9194
_CONSTEXPR20 void reset() noexcept {
9295
_Has_value = false;
9396
}
@@ -104,6 +107,13 @@ struct _Optional_destruct_base<_Ty, false> { // either contains a value of _Ty o
104107
_CONSTEXPR20 ~_Optional_destruct_base() noexcept {
105108
if (_Has_value) {
106109
_Value.~_Ty();
110+
111+
#if _MSVC_STL_DESTRUCTOR_TOMBSTONES
112+
// For the non-trivially destructible case, we can set the optional to be empty.
113+
// We don't attempt to scribble over the bytes of the object's storage because that could be expensive
114+
// and we don't know whether the object has an invalid representation, much less what it could be.
115+
_Has_value = false;
116+
#endif
107117
}
108118
}
109119

stl/inc/regex

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1923,6 +1923,11 @@ public:
19231923

19241924
~basic_regex() noexcept {
19251925
_Tidy();
1926+
1927+
#if _MSVC_STL_DESTRUCTOR_TOMBSTONES
1928+
const auto _Tombstone{reinterpret_cast<_Root_node*>(_MSVC_STL_UINTPTR_TOMBSTONE_VALUE)};
1929+
_Rep = _Tombstone;
1930+
#endif // _MSVC_STL_DESTRUCTOR_TOMBSTONES
19261931
}
19271932

19281933
basic_regex& operator=(const basic_regex& _Right) {

stl/inc/valarray

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,14 @@ public:
137137

138138
~valarray() noexcept {
139139
_Tidy_deallocate();
140+
141+
#if _MSVC_STL_DESTRUCTOR_TOMBSTONES
142+
const auto _Tombstone{reinterpret_cast<_Ty*>(_MSVC_STL_UINTPTR_TOMBSTONE_VALUE)};
143+
_Myptr = _Tombstone;
144+
145+
// _Tidy_deallocate() sets _Mysize to 0, so we don't need to set it here.
146+
_STL_INTERNAL_CHECK(_Mysize == 0);
147+
#endif // _MSVC_STL_DESTRUCTOR_TOMBSTONES
140148
}
141149

142150
valarray& operator=(const valarray& _Right) {

0 commit comments

Comments
 (0)