Skip to content

Commit 53f11dd

Browse files
authored
[libc++] Avoid type-punning between __value_type and pair (#134819)
Before this patch, we were dereferencing pointers to objects which were never constructed. Now we always assume that nodes store `pair<const KeyT, ValueT>` for maps instead, as they actually do. This patch also allows for significant follow-up simplifications, since `__node_value_type` and `__container_value_type` are the same type now.
1 parent 70ef89b commit 53f11dd

File tree

7 files changed

+137
-178
lines changed

7 files changed

+137
-178
lines changed

libcxx/include/__fwd/pair.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,12 @@ _LIBCPP_BEGIN_NAMESPACE_STD
2222
template <class, class>
2323
struct pair;
2424

25+
template <class _Type>
26+
inline const bool __is_pair_v = false;
27+
28+
template <class _Type1, class _Type2>
29+
inline const bool __is_pair_v<pair<_Type1, _Type2> > = true;
30+
2531
template <size_t _Ip, class _T1, class _T2>
2632
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 typename tuple_element<_Ip, pair<_T1, _T2> >::type&
2733
get(pair<_T1, _T2>&) _NOEXCEPT;

libcxx/include/__memory/uses_allocator_construction.h

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
#include <__memory/uses_allocator.h>
1515
#include <__tuple/tuple_like_no_subrange.h>
1616
#include <__type_traits/enable_if.h>
17-
#include <__type_traits/is_same.h>
1817
#include <__type_traits/remove_cv.h>
1918
#include <__utility/declval.h>
2019
#include <__utility/pair.h>
@@ -31,14 +30,8 @@ _LIBCPP_BEGIN_NAMESPACE_STD
3130

3231
#if _LIBCPP_STD_VER >= 17
3332

34-
template <class _Type>
35-
inline constexpr bool __is_std_pair = false;
36-
37-
template <class _Type1, class _Type2>
38-
inline constexpr bool __is_std_pair<pair<_Type1, _Type2>> = true;
39-
4033
template <class _Tp>
41-
inline constexpr bool __is_cv_std_pair = __is_std_pair<remove_cv_t<_Tp>>;
34+
inline constexpr bool __is_cv_std_pair = __is_pair_v<remove_cv_t<_Tp>>;
4235

4336
template <class _Tp, class = void>
4437
struct __uses_allocator_construction_args;

libcxx/include/__node_handle

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ public:
6262
#include <__config>
6363
#include <__memory/allocator_traits.h>
6464
#include <__memory/pointer_traits.h>
65+
#include <__type_traits/is_specialization.h>
6566
#include <optional>
6667

6768
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
@@ -173,17 +174,40 @@ struct __set_node_handle_specifics {
173174
_LIBCPP_HIDE_FROM_ABI value_type& value() const { return static_cast<_Derived const*>(this)->__ptr_->__get_value(); }
174175
};
175176

177+
template <class, class>
178+
struct __hash_value_type;
179+
176180
template <class _NodeType, class _Derived>
177181
struct __map_node_handle_specifics {
178-
typedef typename _NodeType::__node_value_type::key_type key_type;
179-
typedef typename _NodeType::__node_value_type::mapped_type mapped_type;
182+
template <class _Tp>
183+
struct __get_type {
184+
using key_type = __remove_const_t<typename _Tp::first_type>;
185+
using mapped_type = typename _Tp::second_type;
186+
};
187+
188+
template <class _Key, class _Mapped>
189+
struct __get_type<__hash_value_type<_Key, _Mapped> > {
190+
using key_type = _Key;
191+
using mapped_type = _Mapped;
192+
};
193+
194+
using key_type = typename __get_type<typename _NodeType::__node_value_type>::key_type;
195+
using mapped_type = typename __get_type<typename _NodeType::__node_value_type>::mapped_type;
180196

181197
_LIBCPP_HIDE_FROM_ABI key_type& key() const {
182-
return static_cast<_Derived const*>(this)->__ptr_->__get_value().__ref().first;
198+
if constexpr (__is_specialization_v<typename _NodeType::__node_value_type, __hash_value_type>) {
199+
return static_cast<_Derived const*>(this)->__ptr_->__get_value().__ref().first;
200+
} else {
201+
return const_cast<key_type&>(static_cast<_Derived const*>(this)->__ptr_->__get_value().first);
202+
}
183203
}
184204

185205
_LIBCPP_HIDE_FROM_ABI mapped_type& mapped() const {
186-
return static_cast<_Derived const*>(this)->__ptr_->__get_value().__ref().second;
206+
if constexpr (__is_specialization_v<typename _NodeType::__node_value_type, __hash_value_type>) {
207+
return static_cast<_Derived const*>(this)->__ptr_->__get_value().__ref().second;
208+
} else {
209+
return static_cast<_Derived const*>(this)->__ptr_->__get_value().second;
210+
}
187211
}
188212
};
189213

libcxx/include/__tree

Lines changed: 71 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <__assert>
1515
#include <__config>
1616
#include <__fwd/map.h>
17+
#include <__fwd/pair.h>
1718
#include <__fwd/set.h>
1819
#include <__iterator/distance.h>
1920
#include <__iterator/iterator_traits.h>
@@ -25,6 +26,7 @@
2526
#include <__memory/swap_allocator.h>
2627
#include <__memory/unique_ptr.h>
2728
#include <__type_traits/can_extract_key.h>
29+
#include <__type_traits/copy_cvref.h>
2830
#include <__type_traits/enable_if.h>
2931
#include <__type_traits/invoke.h>
3032
#include <__type_traits/is_const.h>
@@ -505,48 +507,24 @@ struct __is_tree_value_type<_One> : __is_tree_value_type_imp<__remove_cvref_t<_O
505507
template <class _Tp>
506508
struct __tree_key_value_types {
507509
typedef _Tp key_type;
508-
typedef _Tp __node_value_type;
509510
typedef _Tp __container_value_type;
510511
static const bool __is_map = false;
511512

512513
_LIBCPP_HIDE_FROM_ABI static key_type const& __get_key(_Tp const& __v) { return __v; }
513-
_LIBCPP_HIDE_FROM_ABI static __container_value_type const& __get_value(__node_value_type const& __v) { return __v; }
514-
_LIBCPP_HIDE_FROM_ABI static __container_value_type* __get_ptr(__node_value_type& __n) { return std::addressof(__n); }
515-
_LIBCPP_HIDE_FROM_ABI static __container_value_type&& __move(__node_value_type& __v) { return std::move(__v); }
516514
};
517515

518516
template <class _Key, class _Tp>
519517
struct __tree_key_value_types<__value_type<_Key, _Tp> > {
520518
typedef _Key key_type;
521519
typedef _Tp mapped_type;
522-
typedef __value_type<_Key, _Tp> __node_value_type;
523520
typedef pair<const _Key, _Tp> __container_value_type;
524521
typedef __container_value_type __map_value_type;
525522
static const bool __is_map = true;
526523

527-
_LIBCPP_HIDE_FROM_ABI static key_type const& __get_key(__node_value_type const& __t) {
528-
return __t.__get_value().first;
529-
}
530-
531524
template <class _Up, __enable_if_t<__is_same_uncvref<_Up, __container_value_type>::value, int> = 0>
532525
_LIBCPP_HIDE_FROM_ABI static key_type const& __get_key(_Up& __t) {
533526
return __t.first;
534527
}
535-
536-
_LIBCPP_HIDE_FROM_ABI static __container_value_type const& __get_value(__node_value_type const& __t) {
537-
return __t.__get_value();
538-
}
539-
540-
template <class _Up, __enable_if_t<__is_same_uncvref<_Up, __container_value_type>::value, int> = 0>
541-
_LIBCPP_HIDE_FROM_ABI static __container_value_type const& __get_value(_Up& __t) {
542-
return __t;
543-
}
544-
545-
_LIBCPP_HIDE_FROM_ABI static __container_value_type* __get_ptr(__node_value_type& __n) {
546-
return std::addressof(__n.__get_value());
547-
}
548-
549-
_LIBCPP_HIDE_FROM_ABI static pair<key_type&&, mapped_type&&> __move(__node_value_type& __v) { return __v.__move(); }
550528
};
551529

552530
template <class _VoidPtr>
@@ -587,6 +565,19 @@ struct __tree_map_pointer_types<_Tp, _AllocPtr, _KVTypes, true> {
587565
typedef __rebind_pointer_t<_AllocPtr, const _Mv> __const_map_value_type_pointer;
588566
};
589567

568+
template <class _Tp>
569+
struct __get_node_value_type {
570+
using type _LIBCPP_NODEBUG = _Tp;
571+
};
572+
573+
template <class _Key, class _ValueT>
574+
struct __get_node_value_type<__value_type<_Key, _ValueT> > {
575+
using type _LIBCPP_NODEBUG = pair<const _Key, _ValueT>;
576+
};
577+
578+
template <class _Tp>
579+
using __get_node_value_type_t _LIBCPP_NODEBUG = typename __get_node_value_type<_Tp>::type;
580+
590581
template <class _NodePtr, class _NodeT = typename pointer_traits<_NodePtr>::element_type>
591582
struct __tree_node_types;
592583

@@ -599,7 +590,7 @@ public:
599590
typedef typename pointer_traits<_NodePtr>::element_type __node_type;
600591
typedef _NodePtr __node_pointer;
601592

602-
typedef _Tp __node_value_type;
593+
using __node_value_type _LIBCPP_NODEBUG = __get_node_value_type_t<_Tp>;
603594
typedef __rebind_pointer_t<_VoidPtr, __node_value_type> __node_value_type_pointer;
604595
typedef __rebind_pointer_t<_VoidPtr, const __node_value_type> __const_node_value_type_pointer;
605596

@@ -650,11 +641,11 @@ public:
650641
template <class _Tp, class _VoidPtr>
651642
class _LIBCPP_STANDALONE_DEBUG __tree_node : public __tree_node_base<_VoidPtr> {
652643
public:
653-
typedef _Tp __node_value_type;
644+
using __node_value_type _LIBCPP_NODEBUG = __get_node_value_type_t<_Tp>;
654645

655646
__node_value_type __value_;
656647

657-
_LIBCPP_HIDE_FROM_ABI _Tp& __get_value() { return __value_; }
648+
_LIBCPP_HIDE_FROM_ABI __node_value_type& __get_value() { return __value_; }
658649

659650
~__tree_node() = delete;
660651
__tree_node(__tree_node const&) = delete;
@@ -685,7 +676,7 @@ public:
685676

686677
_LIBCPP_HIDE_FROM_ABI void operator()(pointer __p) _NOEXCEPT {
687678
if (__value_constructed)
688-
__alloc_traits::destroy(__na_, _NodeTypes::__get_ptr(__p->__value_));
679+
__alloc_traits::destroy(__na_, std::addressof(__p->__value_));
689680
if (__p)
690681
__alloc_traits::deallocate(__na_, __p, 1);
691682
}
@@ -715,7 +706,7 @@ class __tree_iterator {
715706

716707
public:
717708
typedef bidirectional_iterator_tag iterator_category;
718-
typedef _Tp value_type;
709+
using value_type = __get_node_value_type_t<_Tp>;
719710
typedef _DiffType difference_type;
720711
typedef value_type& reference;
721712
typedef typename _NodeTypes::__node_value_type_pointer pointer;
@@ -789,7 +780,7 @@ class __tree_const_iterator {
789780

790781
public:
791782
typedef bidirectional_iterator_tag iterator_category;
792-
typedef _Tp value_type;
783+
using value_type = __get_node_value_type_t<_Tp>;
793784
typedef _DiffType difference_type;
794785
typedef const value_type& reference;
795786
typedef typename _NodeTypes::__const_node_value_type_pointer pointer;
@@ -802,7 +793,7 @@ public:
802793
}
803794

804795
private:
805-
typedef __tree_iterator<value_type, __node_pointer, difference_type> __non_const_iterator;
796+
typedef __tree_iterator<_Tp, __node_pointer, difference_type> __non_const_iterator;
806797

807798
public:
808799
_LIBCPP_HIDE_FROM_ABI __tree_const_iterator(__non_const_iterator __p) _NOEXCEPT : __ptr_(__p.__ptr_) {}
@@ -1107,6 +1098,18 @@ public:
11071098
return __emplace_hint_unique(__p, std::forward<_Vp>(__v));
11081099
}
11091100

1101+
template <class _ValueT = _Tp, __enable_if_t<__is_tree_value_type<_ValueT>::value, int> = 0>
1102+
_LIBCPP_HIDE_FROM_ABI void
1103+
__insert_unique_from_orphaned_node(const_iterator __p, __get_node_value_type_t<_Tp>&& __value) {
1104+
using __key_type = typename _NodeTypes::key_type;
1105+
__emplace_hint_unique(__p, const_cast<__key_type&&>(__value.first), std::move(__value.second));
1106+
}
1107+
1108+
template <class _ValueT = _Tp, __enable_if_t<!__is_tree_value_type<_ValueT>::value, int> = 0>
1109+
_LIBCPP_HIDE_FROM_ABI void __insert_unique_from_orphaned_node(const_iterator __p, _Tp&& __value) {
1110+
__emplace_hint_unique(__p, std::move(__value));
1111+
}
1112+
11101113
_LIBCPP_HIDE_FROM_ABI iterator __insert_multi(__container_value_type&& __v) {
11111114
return __emplace_multi(std::move(__v));
11121115
}
@@ -1125,6 +1128,18 @@ public:
11251128
return __emplace_hint_multi(__p, std::forward<_Vp>(__v));
11261129
}
11271130

1131+
template <class _ValueT = _Tp, __enable_if_t<__is_tree_value_type<_ValueT>::value, int> = 0>
1132+
_LIBCPP_HIDE_FROM_ABI void
1133+
__insert_multi_from_orphaned_node(const_iterator __p, __get_node_value_type_t<_Tp>&& __value) {
1134+
using __key_type = typename _NodeTypes::key_type;
1135+
__emplace_hint_multi(__p, const_cast<__key_type&&>(__value.first), std::move(__value.second));
1136+
}
1137+
1138+
template <class _ValueT = _Tp, __enable_if_t<!__is_tree_value_type<_ValueT>::value, int> = 0>
1139+
_LIBCPP_HIDE_FROM_ABI void __insert_multi_from_orphaned_node(const_iterator __p, _Tp&& __value) {
1140+
__emplace_hint_multi(__p, std::move(__value));
1141+
}
1142+
11281143
_LIBCPP_HIDE_FROM_ABI pair<iterator, bool>
11291144
__node_assign_unique(const __container_value_type& __v, __node_pointer __dest);
11301145

@@ -1266,6 +1281,21 @@ private:
12661281
}
12671282
_LIBCPP_HIDE_FROM_ABI void __move_assign_alloc(__tree&, false_type) _NOEXCEPT {}
12681283

1284+
template <class _From, __enable_if_t<__is_pair_v<__remove_cvref_t<_From> >, int> = 0>
1285+
_LIBCPP_HIDE_FROM_ABI static void __assign_value(__get_node_value_type_t<value_type>& __lhs, _From&& __rhs) {
1286+
using __key_type = typename _NodeTypes::key_type;
1287+
1288+
// This is technically UB, since the object was constructed as `const`.
1289+
// Clang doesn't optimize on this currently though.
1290+
const_cast<__key_type&>(__lhs.first) = const_cast<__copy_cvref_t<_From, __key_type>&&>(__rhs.first);
1291+
__lhs.second = std::forward<_From>(__rhs).second;
1292+
}
1293+
1294+
template <class _To, class _From, class _ValueT = _Tp, __enable_if_t<!__is_pair_v<__remove_cvref_t<_From> >, int> = 0>
1295+
_LIBCPP_HIDE_FROM_ABI static void __assign_value(_To& __lhs, _From&& __rhs) {
1296+
__lhs = std::forward<_From>(__rhs);
1297+
}
1298+
12691299
struct _DetachedTreeCache {
12701300
_LIBCPP_HIDE_FROM_ABI explicit _DetachedTreeCache(__tree* __t) _NOEXCEPT
12711301
: __t_(__t),
@@ -1406,14 +1436,14 @@ void __tree<_Tp, _Compare, _Allocator>::__assign_multi(_InputIterator __first, _
14061436
if (size() != 0) {
14071437
_DetachedTreeCache __cache(this);
14081438
for (; __cache.__get() && __first != __last; ++__first) {
1409-
__cache.__get()->__value_ = *__first;
1439+
__assign_value(__cache.__get()->__value_, *__first);
14101440
__node_insert_multi(__cache.__get());
14111441
__cache.__advance();
14121442
}
14131443
}
14141444
const_iterator __e = end();
14151445
for (; __first != __last; ++__first)
1416-
__insert_multi(__e, _NodeTypes::__get_value(*__first));
1446+
__insert_multi(__e, *__first);
14171447
}
14181448

14191449
template <class _Tp, class _Compare, class _Allocator>
@@ -1492,13 +1522,14 @@ void __tree<_Tp, _Compare, _Allocator>::__move_assign(__tree& __t, false_type) {
14921522
if (size() != 0) {
14931523
_DetachedTreeCache __cache(this);
14941524
while (__cache.__get() != nullptr && __t.size() != 0) {
1495-
__cache.__get()->__value_ = std::move(__t.remove(__t.begin())->__value_);
1525+
__assign_value(__cache.__get()->__value_, std::move(__t.remove(__t.begin())->__value_));
14961526
__node_insert_multi(__cache.__get());
14971527
__cache.__advance();
14981528
}
14991529
}
1500-
while (__t.size() != 0)
1501-
__insert_multi(__e, _NodeTypes::__move(__t.remove(__t.begin())->__value_));
1530+
while (__t.size() != 0) {
1531+
__insert_multi_from_orphaned_node(__e, std::move(__t.remove(__t.begin())->__value_));
1532+
}
15021533
}
15031534
}
15041535

@@ -1524,7 +1555,7 @@ void __tree<_Tp, _Compare, _Allocator>::destroy(__node_pointer __nd) _NOEXCEPT {
15241555
destroy(static_cast<__node_pointer>(__nd->__left_));
15251556
destroy(static_cast<__node_pointer>(__nd->__right_));
15261557
__node_allocator& __na = __node_alloc();
1527-
__node_traits::destroy(__na, _NodeTypes::__get_ptr(__nd->__value_));
1558+
__node_traits::destroy(__na, std::addressof(__nd->__value_));
15281559
__node_traits::deallocate(__na, __nd, 1);
15291560
}
15301561
}
@@ -1794,10 +1825,9 @@ template <class _Tp, class _Compare, class _Allocator>
17941825
template <class... _Args>
17951826
typename __tree<_Tp, _Compare, _Allocator>::__node_holder
17961827
__tree<_Tp, _Compare, _Allocator>::__construct_node(_Args&&... __args) {
1797-
static_assert(!__is_tree_value_type<_Args...>::value, "Cannot construct from __value_type");
17981828
__node_allocator& __na = __node_alloc();
17991829
__node_holder __h(__node_traits::allocate(__na, 1), _Dp(__na));
1800-
__node_traits::construct(__na, _NodeTypes::__get_ptr(__h->__value_), std::forward<_Args>(__args)...);
1830+
__node_traits::construct(__na, std::addressof(__h->__value_), std::forward<_Args>(__args)...);
18011831
__h.get_deleter().__value_constructed = true;
18021832
return __h;
18031833
}
@@ -1865,7 +1895,7 @@ __tree<_Tp, _Compare, _Allocator>::__node_assign_unique(const __container_value_
18651895
__node_pointer __r = static_cast<__node_pointer>(__child);
18661896
bool __inserted = false;
18671897
if (__child == nullptr) {
1868-
__nd->__value_ = __v;
1898+
__assign_value(__nd->__value_, __v);
18691899
__insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__nd));
18701900
__r = __nd;
18711901
__inserted = true;
@@ -2027,7 +2057,7 @@ typename __tree<_Tp, _Compare, _Allocator>::iterator __tree<_Tp, _Compare, _Allo
20272057
__node_pointer __np = __p.__get_np();
20282058
iterator __r = __remove_node_pointer(__np);
20292059
__node_allocator& __na = __node_alloc();
2030-
__node_traits::destroy(__na, _NodeTypes::__get_ptr(const_cast<__node_value_type&>(*__p)));
2060+
__node_traits::destroy(__na, std::addressof(const_cast<__node_value_type&>(*__p)));
20312061
__node_traits::deallocate(__na, __np, 1);
20322062
return __r;
20332063
}

0 commit comments

Comments
 (0)