Skip to content

Commit 53d1e23

Browse files
committed
[libc++] Avoid type-punning betwee __hash_value_type and pair
1 parent 650b451 commit 53d1e23

File tree

4 files changed

+125
-156
lines changed

4 files changed

+125
-156
lines changed

libcxx/include/__hash_table

Lines changed: 80 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <__memory/unique_ptr.h>
3030
#include <__new/launder.h>
3131
#include <__type_traits/can_extract_key.h>
32+
#include <__type_traits/copy_cvref.h>
3233
#include <__type_traits/enable_if.h>
3334
#include <__type_traits/invoke.h>
3435
#include <__type_traits/is_const.h>
@@ -108,9 +109,22 @@ struct __hash_node_base {
108109
_LIBCPP_HIDE_FROM_ABI explicit __hash_node_base(__next_pointer __next) _NOEXCEPT : __next_(__next) {}
109110
};
110111

112+
template <class _Tp>
113+
struct __get_hash_node_value_type {
114+
using type _LIBCPP_NODEBUG = _Tp;
115+
};
116+
117+
template <class _Key, class _Tp>
118+
struct __get_hash_node_value_type<__hash_value_type<_Key, _Tp> > {
119+
using type _LIBCPP_NODEBUG = pair<const _Key, _Tp>;
120+
};
121+
122+
template <class _Tp>
123+
using __get_hash_node_value_type_t _LIBCPP_NODEBUG = typename __get_hash_node_value_type<_Tp>::type;
124+
111125
template <class _Tp, class _VoidPtr>
112126
struct __hash_node : public __hash_node_base< __rebind_pointer_t<_VoidPtr, __hash_node<_Tp, _VoidPtr> > > {
113-
typedef _Tp __node_value_type;
127+
using __node_value_type _LIBCPP_NODEBUG = __get_hash_node_value_type_t<_Tp>;
114128
using _Base _LIBCPP_NODEBUG = __hash_node_base<__rebind_pointer_t<_VoidPtr, __hash_node<_Tp, _VoidPtr> > >;
115129
using __next_pointer _LIBCPP_NODEBUG = typename _Base::__next_pointer;
116130

@@ -122,18 +136,20 @@ struct __hash_node : public __hash_node_base< __rebind_pointer_t<_VoidPtr, __has
122136

123137
private:
124138
union {
125-
_Tp __value_;
139+
__node_value_type __value_;
126140
};
127141

128142
public:
129-
_LIBCPP_HIDE_FROM_ABI _Tp& __get_value() { return __value_; }
143+
_LIBCPP_HIDE_FROM_ABI __node_value_type& __get_value() { return __value_; }
130144
#else
131145

132146
private:
133-
_ALIGNAS_TYPE(_Tp) char __buffer_[sizeof(_Tp)];
147+
_ALIGNAS_TYPE(__node_value_type) char __buffer_[sizeof(__node_value_type)];
134148

135149
public:
136-
_LIBCPP_HIDE_FROM_ABI _Tp& __get_value() { return *std::__launder(reinterpret_cast<_Tp*>(&__buffer_)); }
150+
_LIBCPP_HIDE_FROM_ABI __node_value_type& __get_value() {
151+
return *std::__launder(reinterpret_cast<__node_value_type*>(&__buffer_));
152+
}
137153
#endif
138154

139155
_LIBCPP_HIDE_FROM_ABI explicit __hash_node(__next_pointer __next, size_t __hash) : _Base(__next), __hash_(__hash) {}
@@ -201,8 +217,8 @@ struct __hash_key_value_types<__hash_value_type<_Key, _Tp> > {
201217
return __t;
202218
}
203219

204-
_LIBCPP_HIDE_FROM_ABI static __container_value_type* __get_ptr(__node_value_type& __n) {
205-
return std::addressof(__n.__get_value());
220+
_LIBCPP_HIDE_FROM_ABI static __container_value_type* __get_ptr(__container_value_type& __n) {
221+
return std::addressof(__n);
206222
}
207223
_LIBCPP_HIDE_FROM_ABI static pair<key_type&&, mapped_type&&> __move(__node_value_type& __v) { return __v.__move(); }
208224
};
@@ -242,7 +258,7 @@ public:
242258

243259
typedef typename __node_base_type::__next_pointer __next_pointer;
244260

245-
typedef _Tp __node_value_type;
261+
using __node_value_type _LIBCPP_NODEBUG = __get_hash_node_value_type_t<_Tp>;
246262
typedef __rebind_pointer_t<_VoidPtr, __node_value_type> __node_value_type_pointer;
247263
typedef __rebind_pointer_t<_VoidPtr, const __node_value_type> __const_node_value_type_pointer;
248264

@@ -667,14 +683,14 @@ int __diagnose_unordered_container_requirements(void*);
667683
template <class _Tp, class _Hash, class _Equal, class _Alloc>
668684
class __hash_table {
669685
public:
670-
typedef _Tp value_type;
686+
using value_type = __get_hash_node_value_type_t<_Tp>;
671687
typedef _Hash hasher;
672688
typedef _Equal key_equal;
673689
typedef _Alloc allocator_type;
674690

675691
private:
676692
typedef allocator_traits<allocator_type> __alloc_traits;
677-
typedef typename __make_hash_node_types<value_type, typename __alloc_traits::void_pointer>::type _NodeTypes;
693+
typedef typename __make_hash_node_types<_Tp, typename __alloc_traits::void_pointer>::type _NodeTypes;
678694

679695
public:
680696
typedef typename _NodeTypes::__node_value_type __node_value_type;
@@ -845,6 +861,22 @@ public:
845861
return __emplace_unique(std::forward<_Pp>(__x));
846862
}
847863

864+
template <class _ValueT = _Tp, __enable_if_t<__is_hash_value_type<_ValueT>::value, int> = 0>
865+
_LIBCPP_HIDE_FROM_ABI void __insert_unique_from_orphaned_node(value_type&& __value) {
866+
using __key_type = typename _NodeTypes::key_type;
867+
868+
__node_holder __h = __construct_node(const_cast<__key_type&&>(__value.first), std::move(__value.second));
869+
__node_insert_unique(__h.get());
870+
__h.release();
871+
}
872+
873+
template <class _ValueT = _Tp, __enable_if_t<!__is_hash_value_type<_ValueT>::value, int> = 0>
874+
_LIBCPP_HIDE_FROM_ABI void __insert_unique_from_orphaned_node(value_type&& __value) {
875+
__node_holder __h = __construct_node(std::move(__value));
876+
__node_insert_unique(__h.get());
877+
__h.release();
878+
}
879+
848880
template <class _Pp>
849881
_LIBCPP_HIDE_FROM_ABI iterator __insert_multi(_Pp&& __x) {
850882
return __emplace_multi(std::forward<_Pp>(__x));
@@ -855,6 +887,22 @@ public:
855887
return __emplace_hint_multi(__p, std::forward<_Pp>(__x));
856888
}
857889

890+
template <class _ValueT = _Tp, __enable_if_t<__is_hash_value_type<_ValueT>::value, int> = 0>
891+
_LIBCPP_HIDE_FROM_ABI void __insert_multi_from_orphaned_node(value_type&& __value) {
892+
using __key_type = typename _NodeTypes::key_type;
893+
894+
__node_holder __h = __construct_node(const_cast<__key_type&&>(__value.first), std::move(__value.second));
895+
__node_insert_multi(__h.get());
896+
__h.release();
897+
}
898+
899+
template <class _ValueT = _Tp, __enable_if_t<!__is_hash_value_type<_ValueT>::value, int> = 0>
900+
_LIBCPP_HIDE_FROM_ABI void __insert_multi_from_orphaned_node(value_type&& __value) {
901+
__node_holder __h = __construct_node(std::move(__value));
902+
__node_insert_multi(__h.get());
903+
__h.release();
904+
}
905+
858906
_LIBCPP_HIDE_FROM_ABI pair<iterator, bool> __insert_unique(const __container_value_type& __x) {
859907
return __emplace_unique_key_args(_NodeTypes::__get_key(__x), __x);
860908
}
@@ -1020,6 +1068,21 @@ private:
10201068
_LIBCPP_HIDE_FROM_ABI void __deallocate_node(__next_pointer __np) _NOEXCEPT;
10211069
_LIBCPP_HIDE_FROM_ABI __next_pointer __detach() _NOEXCEPT;
10221070

1071+
template <class _From, class _ValueT = _Tp, __enable_if_t<__is_hash_value_type<_ValueT>::value, int> = 0>
1072+
_LIBCPP_HIDE_FROM_ABI void __assign_value(__get_hash_node_value_type_t<_Tp>& __lhs, _From&& __rhs) {
1073+
using __key_type = typename _NodeTypes::key_type;
1074+
1075+
// This is technically UB, since the object was constructed as `const`.
1076+
// Clang doesn't optimize on this currently though.
1077+
const_cast<__key_type&>(__lhs.first) = const_cast<__copy_cvref_t<_From, __key_type>&&>(__rhs.first);
1078+
__lhs.second = std::forward<_From>(__rhs).second;
1079+
}
1080+
1081+
template <class _From, class _ValueT = _Tp, __enable_if_t<!__is_hash_value_type<_ValueT>::value, int> = 0>
1082+
_LIBCPP_HIDE_FROM_ABI void __assign_value(_Tp& __lhs, _From&& __rhs) {
1083+
__lhs = std::forward<_From>(__rhs);
1084+
}
1085+
10231086
template <class, class, class, class, class>
10241087
friend class unordered_map;
10251088
template <class, class, class, class, class>
@@ -1216,8 +1279,8 @@ void __hash_table<_Tp, _Hash, _Equal, _Alloc>::__move_assign(__hash_table& __u,
12161279
#endif // _LIBCPP_HAS_EXCEPTIONS
12171280
const_iterator __i = __u.begin();
12181281
while (__cache != nullptr && __u.size() != 0) {
1219-
__cache->__upcast()->__get_value() = std::move(__u.remove(__i++)->__get_value());
1220-
__next_pointer __next = __cache->__next_;
1282+
__assign_value(__cache->__upcast()->__get_value(), std::move(__u.remove(__i++)->__get_value()));
1283+
__next_pointer __next = __cache->__next_;
12211284
__node_insert_multi(__cache->__upcast());
12221285
__cache = __next;
12231286
}
@@ -1230,11 +1293,8 @@ void __hash_table<_Tp, _Hash, _Equal, _Alloc>::__move_assign(__hash_table& __u,
12301293
__deallocate_node(__cache);
12311294
}
12321295
const_iterator __i = __u.begin();
1233-
while (__u.size() != 0) {
1234-
__node_holder __h = __construct_node(_NodeTypes::__move(__u.remove(__i++)->__get_value()));
1235-
__node_insert_multi(__h.get());
1236-
__h.release();
1237-
}
1296+
while (__u.size() != 0)
1297+
__insert_multi_from_orphaned_node(std::move(__u.remove(__i++)->__get_value()));
12381298
}
12391299
}
12401300

@@ -1262,8 +1322,8 @@ void __hash_table<_Tp, _Hash, _Equal, _Alloc>::__assign_unique(_InputIterator __
12621322
try {
12631323
#endif // _LIBCPP_HAS_EXCEPTIONS
12641324
for (; __cache != nullptr && __first != __last; ++__first) {
1265-
__cache->__upcast()->__get_value() = *__first;
1266-
__next_pointer __next = __cache->__next_;
1325+
__assign_value(__cache->__upcast()->__get_value(), *__first);
1326+
__next_pointer __next = __cache->__next_;
12671327
__node_insert_unique(__cache->__upcast());
12681328
__cache = __next;
12691329
}
@@ -1294,7 +1354,7 @@ void __hash_table<_Tp, _Hash, _Equal, _Alloc>::__assign_multi(_InputIterator __f
12941354
try {
12951355
#endif // _LIBCPP_HAS_EXCEPTIONS
12961356
for (; __cache != nullptr && __first != __last; ++__first) {
1297-
__cache->__upcast()->__get_value() = *__first;
1357+
__assign_value(__cache->__upcast()->__get_value(), *__first);
12981358
__next_pointer __next = __cache->__next_;
12991359
__node_insert_multi(__cache->__upcast());
13001360
__cache = __next;

0 commit comments

Comments
 (0)