[libc++] Avoid type-punning between __hash_value_type and pair (#143501)

This patch is very similar to #134819 in nature. 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
unordered_maps instead, as they actually do.
This commit is contained in:
Nikolas Klauser 2025-06-26 19:43:59 +02:00 committed by GitHub
parent 7842e9eada
commit 4c8fab399b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 125 additions and 156 deletions

View File

@ -29,6 +29,7 @@
#include <__memory/unique_ptr.h>
#include <__new/launder.h>
#include <__type_traits/can_extract_key.h>
#include <__type_traits/copy_cvref.h>
#include <__type_traits/enable_if.h>
#include <__type_traits/invoke.h>
#include <__type_traits/is_const.h>
@ -108,9 +109,22 @@ struct __hash_node_base {
_LIBCPP_HIDE_FROM_ABI explicit __hash_node_base(__next_pointer __next) _NOEXCEPT : __next_(__next) {}
};
template <class _Tp>
struct __get_hash_node_value_type {
using type _LIBCPP_NODEBUG = _Tp;
};
template <class _Key, class _Tp>
struct __get_hash_node_value_type<__hash_value_type<_Key, _Tp> > {
using type _LIBCPP_NODEBUG = pair<const _Key, _Tp>;
};
template <class _Tp>
using __get_hash_node_value_type_t _LIBCPP_NODEBUG = typename __get_hash_node_value_type<_Tp>::type;
template <class _Tp, class _VoidPtr>
struct __hash_node : public __hash_node_base< __rebind_pointer_t<_VoidPtr, __hash_node<_Tp, _VoidPtr> > > {
typedef _Tp __node_value_type;
using __node_value_type _LIBCPP_NODEBUG = __get_hash_node_value_type_t<_Tp>;
using _Base _LIBCPP_NODEBUG = __hash_node_base<__rebind_pointer_t<_VoidPtr, __hash_node<_Tp, _VoidPtr> > >;
using __next_pointer _LIBCPP_NODEBUG = typename _Base::__next_pointer;
@ -122,18 +136,20 @@ struct __hash_node : public __hash_node_base< __rebind_pointer_t<_VoidPtr, __has
private:
union {
_Tp __value_;
__node_value_type __value_;
};
public:
_LIBCPP_HIDE_FROM_ABI _Tp& __get_value() { return __value_; }
_LIBCPP_HIDE_FROM_ABI __node_value_type& __get_value() { return __value_; }
#else
private:
_ALIGNAS_TYPE(_Tp) char __buffer_[sizeof(_Tp)];
_ALIGNAS_TYPE(__node_value_type) char __buffer_[sizeof(__node_value_type)];
public:
_LIBCPP_HIDE_FROM_ABI _Tp& __get_value() { return *std::__launder(reinterpret_cast<_Tp*>(&__buffer_)); }
_LIBCPP_HIDE_FROM_ABI __node_value_type& __get_value() {
return *std::__launder(reinterpret_cast<__node_value_type*>(&__buffer_));
}
#endif
_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> > {
return __t;
}
_LIBCPP_HIDE_FROM_ABI static __container_value_type* __get_ptr(__node_value_type& __n) {
return std::addressof(__n.__get_value());
_LIBCPP_HIDE_FROM_ABI static __container_value_type* __get_ptr(__container_value_type& __n) {
return std::addressof(__n);
}
_LIBCPP_HIDE_FROM_ABI static pair<key_type&&, mapped_type&&> __move(__node_value_type& __v) { return __v.__move(); }
};
@ -242,7 +258,7 @@ public:
typedef typename __node_base_type::__next_pointer __next_pointer;
typedef _Tp __node_value_type;
using __node_value_type _LIBCPP_NODEBUG = __get_hash_node_value_type_t<_Tp>;
typedef __rebind_pointer_t<_VoidPtr, __node_value_type> __node_value_type_pointer;
typedef __rebind_pointer_t<_VoidPtr, const __node_value_type> __const_node_value_type_pointer;
@ -667,14 +683,14 @@ int __diagnose_unordered_container_requirements(void*);
template <class _Tp, class _Hash, class _Equal, class _Alloc>
class __hash_table {
public:
typedef _Tp value_type;
using value_type = __get_hash_node_value_type_t<_Tp>;
typedef _Hash hasher;
typedef _Equal key_equal;
typedef _Alloc allocator_type;
private:
typedef allocator_traits<allocator_type> __alloc_traits;
typedef typename __make_hash_node_types<value_type, typename __alloc_traits::void_pointer>::type _NodeTypes;
typedef typename __make_hash_node_types<_Tp, typename __alloc_traits::void_pointer>::type _NodeTypes;
public:
typedef typename _NodeTypes::__node_value_type __node_value_type;
@ -845,6 +861,22 @@ public:
return __emplace_unique(std::forward<_Pp>(__x));
}
template <class _ValueT = _Tp, __enable_if_t<__is_hash_value_type<_ValueT>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI void __insert_unique_from_orphaned_node(value_type&& __value) {
using __key_type = typename _NodeTypes::key_type;
__node_holder __h = __construct_node(const_cast<__key_type&&>(__value.first), std::move(__value.second));
__node_insert_unique(__h.get());
__h.release();
}
template <class _ValueT = _Tp, __enable_if_t<!__is_hash_value_type<_ValueT>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI void __insert_unique_from_orphaned_node(value_type&& __value) {
__node_holder __h = __construct_node(std::move(__value));
__node_insert_unique(__h.get());
__h.release();
}
template <class _Pp>
_LIBCPP_HIDE_FROM_ABI iterator __insert_multi(_Pp&& __x) {
return __emplace_multi(std::forward<_Pp>(__x));
@ -855,6 +887,22 @@ public:
return __emplace_hint_multi(__p, std::forward<_Pp>(__x));
}
template <class _ValueT = _Tp, __enable_if_t<__is_hash_value_type<_ValueT>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI void __insert_multi_from_orphaned_node(value_type&& __value) {
using __key_type = typename _NodeTypes::key_type;
__node_holder __h = __construct_node(const_cast<__key_type&&>(__value.first), std::move(__value.second));
__node_insert_multi(__h.get());
__h.release();
}
template <class _ValueT = _Tp, __enable_if_t<!__is_hash_value_type<_ValueT>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI void __insert_multi_from_orphaned_node(value_type&& __value) {
__node_holder __h = __construct_node(std::move(__value));
__node_insert_multi(__h.get());
__h.release();
}
_LIBCPP_HIDE_FROM_ABI pair<iterator, bool> __insert_unique(const __container_value_type& __x) {
return __emplace_unique_key_args(_NodeTypes::__get_key(__x), __x);
}
@ -1020,6 +1068,21 @@ private:
_LIBCPP_HIDE_FROM_ABI void __deallocate_node(__next_pointer __np) _NOEXCEPT;
_LIBCPP_HIDE_FROM_ABI __next_pointer __detach() _NOEXCEPT;
template <class _From, class _ValueT = _Tp, __enable_if_t<__is_hash_value_type<_ValueT>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI void __assign_value(__get_hash_node_value_type_t<_Tp>& __lhs, _From&& __rhs) {
using __key_type = typename _NodeTypes::key_type;
// This is technically UB, since the object was constructed as `const`.
// Clang doesn't optimize on this currently though.
const_cast<__key_type&>(__lhs.first) = const_cast<__copy_cvref_t<_From, __key_type>&&>(__rhs.first);
__lhs.second = std::forward<_From>(__rhs).second;
}
template <class _From, class _ValueT = _Tp, __enable_if_t<!__is_hash_value_type<_ValueT>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI void __assign_value(_Tp& __lhs, _From&& __rhs) {
__lhs = std::forward<_From>(__rhs);
}
template <class, class, class, class, class>
friend class unordered_map;
template <class, class, class, class, class>
@ -1216,8 +1279,8 @@ void __hash_table<_Tp, _Hash, _Equal, _Alloc>::__move_assign(__hash_table& __u,
#endif // _LIBCPP_HAS_EXCEPTIONS
const_iterator __i = __u.begin();
while (__cache != nullptr && __u.size() != 0) {
__cache->__upcast()->__get_value() = std::move(__u.remove(__i++)->__get_value());
__next_pointer __next = __cache->__next_;
__assign_value(__cache->__upcast()->__get_value(), std::move(__u.remove(__i++)->__get_value()));
__next_pointer __next = __cache->__next_;
__node_insert_multi(__cache->__upcast());
__cache = __next;
}
@ -1230,11 +1293,8 @@ void __hash_table<_Tp, _Hash, _Equal, _Alloc>::__move_assign(__hash_table& __u,
__deallocate_node(__cache);
}
const_iterator __i = __u.begin();
while (__u.size() != 0) {
__node_holder __h = __construct_node(_NodeTypes::__move(__u.remove(__i++)->__get_value()));
__node_insert_multi(__h.get());
__h.release();
}
while (__u.size() != 0)
__insert_multi_from_orphaned_node(std::move(__u.remove(__i++)->__get_value()));
}
}
@ -1262,8 +1322,8 @@ void __hash_table<_Tp, _Hash, _Equal, _Alloc>::__assign_unique(_InputIterator __
try {
#endif // _LIBCPP_HAS_EXCEPTIONS
for (; __cache != nullptr && __first != __last; ++__first) {
__cache->__upcast()->__get_value() = *__first;
__next_pointer __next = __cache->__next_;
__assign_value(__cache->__upcast()->__get_value(), *__first);
__next_pointer __next = __cache->__next_;
__node_insert_unique(__cache->__upcast());
__cache = __next;
}
@ -1294,7 +1354,7 @@ void __hash_table<_Tp, _Hash, _Equal, _Alloc>::__assign_multi(_InputIterator __f
try {
#endif // _LIBCPP_HAS_EXCEPTIONS
for (; __cache != nullptr && __first != __last; ++__first) {
__cache->__upcast()->__get_value() = *__first;
__assign_value(__cache->__upcast()->__get_value(), *__first);
__next_pointer __next = __cache->__next_;
__node_insert_multi(__cache->__upcast());
__cache = __next;

View File

@ -654,9 +654,7 @@ public:
_LIBCPP_HIDE_FROM_ABI __unordered_map_hasher(const _Hash& __h) _NOEXCEPT_(is_nothrow_copy_constructible<_Hash>::value)
: _Hash(__h) {}
_LIBCPP_HIDE_FROM_ABI const _Hash& hash_function() const _NOEXCEPT { return *this; }
_LIBCPP_HIDE_FROM_ABI size_t operator()(const _Cp& __x) const {
return static_cast<const _Hash&>(*this)(__x.__get_value().first);
}
_LIBCPP_HIDE_FROM_ABI size_t operator()(const _Cp& __x) const { return static_cast<const _Hash&>(*this)(__x.first); }
_LIBCPP_HIDE_FROM_ABI size_t operator()(const _Key& __x) const { return static_cast<const _Hash&>(*this)(__x); }
# if _LIBCPP_STD_VER >= 20
template <typename _K2>
@ -680,7 +678,7 @@ public:
_LIBCPP_HIDE_FROM_ABI __unordered_map_hasher(const _Hash& __h) _NOEXCEPT_(is_nothrow_copy_constructible<_Hash>::value)
: __hash_(__h) {}
_LIBCPP_HIDE_FROM_ABI const _Hash& hash_function() const _NOEXCEPT { return __hash_; }
_LIBCPP_HIDE_FROM_ABI size_t operator()(const _Cp& __x) const { return __hash_(__x.__get_value().first); }
_LIBCPP_HIDE_FROM_ABI size_t operator()(const _Cp& __x) const { return __hash_(__x.first); }
_LIBCPP_HIDE_FROM_ABI size_t operator()(const _Key& __x) const { return __hash_(__x); }
# if _LIBCPP_STD_VER >= 20
template <typename _K2>
@ -713,10 +711,10 @@ public:
: _Pred(__p) {}
_LIBCPP_HIDE_FROM_ABI const _Pred& key_eq() const _NOEXCEPT { return *this; }
_LIBCPP_HIDE_FROM_ABI bool operator()(const _Cp& __x, const _Cp& __y) const {
return static_cast<const _Pred&>(*this)(__x.__get_value().first, __y.__get_value().first);
return static_cast<const _Pred&>(*this)(__x.first, __y.first);
}
_LIBCPP_HIDE_FROM_ABI bool operator()(const _Cp& __x, const _Key& __y) const {
return static_cast<const _Pred&>(*this)(__x.__get_value().first, __y);
return static_cast<const _Pred&>(*this)(__x.first, __y);
}
_LIBCPP_HIDE_FROM_ABI bool operator()(const _Key& __x, const _Cp& __y) const {
return static_cast<const _Pred&>(*this)(__x, __y.__get_value().first);
@ -724,7 +722,7 @@ public:
# if _LIBCPP_STD_VER >= 20
template <typename _K2>
_LIBCPP_HIDE_FROM_ABI bool operator()(const _Cp& __x, const _K2& __y) const {
return static_cast<const _Pred&>(*this)(__x.__get_value().first, __y);
return static_cast<const _Pred&>(*this)(__x.first, __y);
}
template <typename _K2>
_LIBCPP_HIDE_FROM_ABI bool operator()(const _K2& __x, const _Cp& __y) const {
@ -755,23 +753,17 @@ public:
_LIBCPP_HIDE_FROM_ABI __unordered_map_equal(const _Pred& __p) _NOEXCEPT_(is_nothrow_copy_constructible<_Pred>::value)
: __pred_(__p) {}
_LIBCPP_HIDE_FROM_ABI const _Pred& key_eq() const _NOEXCEPT { return __pred_; }
_LIBCPP_HIDE_FROM_ABI bool operator()(const _Cp& __x, const _Cp& __y) const {
return __pred_(__x.__get_value().first, __y.__get_value().first);
}
_LIBCPP_HIDE_FROM_ABI bool operator()(const _Cp& __x, const _Key& __y) const {
return __pred_(__x.__get_value().first, __y);
}
_LIBCPP_HIDE_FROM_ABI bool operator()(const _Key& __x, const _Cp& __y) const {
return __pred_(__x, __y.__get_value().first);
}
_LIBCPP_HIDE_FROM_ABI bool operator()(const _Cp& __x, const _Cp& __y) const { return __pred_(__x.first, __y.first); }
_LIBCPP_HIDE_FROM_ABI bool operator()(const _Cp& __x, const _Key& __y) const { return __pred_(__x.first, __y); }
_LIBCPP_HIDE_FROM_ABI bool operator()(const _Key& __x, const _Cp& __y) const { return __pred_(__x, __y.first); }
# if _LIBCPP_STD_VER >= 20
template <typename _K2>
_LIBCPP_HIDE_FROM_ABI bool operator()(const _Cp& __x, const _K2& __y) const {
return __pred_(__x.__get_value().first, __y);
return __pred_(__x.first, __y);
}
template <typename _K2>
_LIBCPP_HIDE_FROM_ABI bool operator()(const _K2& __x, const _Cp& __y) const {
return __pred_(__x, __y.__get_value().first);
return __pred_(__x, __y.first);
}
template <typename _K2>
_LIBCPP_HIDE_FROM_ABI bool operator()(const _Key& __x, const _K2& __y) const {
@ -833,96 +825,16 @@ public:
_LIBCPP_HIDE_FROM_ABI void operator()(pointer __p) _NOEXCEPT {
if (__second_constructed)
__alloc_traits::destroy(__na_, std::addressof(__p->__get_value().__get_value().second));
__alloc_traits::destroy(__na_, std::addressof(__p->__get_value().second));
if (__first_constructed)
__alloc_traits::destroy(__na_, std::addressof(__p->__get_value().__get_value().first));
__alloc_traits::destroy(__na_, std::addressof(__p->__get_value().first));
if (__p)
__alloc_traits::deallocate(__na_, __p, 1);
}
};
# ifndef _LIBCPP_CXX03_LANG
template <class _Key, class _Tp>
struct _LIBCPP_STANDALONE_DEBUG __hash_value_type {
typedef _Key key_type;
typedef _Tp mapped_type;
typedef pair<const key_type, mapped_type> value_type;
typedef pair<key_type&, mapped_type&> __nc_ref_pair_type;
typedef pair<key_type&&, mapped_type&&> __nc_rref_pair_type;
private:
value_type __cc_;
public:
_LIBCPP_HIDE_FROM_ABI value_type& __get_value() {
# if _LIBCPP_STD_VER >= 17
return *std::launder(std::addressof(__cc_));
# else
return __cc_;
# endif
}
_LIBCPP_HIDE_FROM_ABI const value_type& __get_value() const {
# if _LIBCPP_STD_VER >= 17
return *std::launder(std::addressof(__cc_));
# else
return __cc_;
# endif
}
_LIBCPP_HIDE_FROM_ABI __nc_ref_pair_type __ref() {
value_type& __v = __get_value();
return __nc_ref_pair_type(const_cast<key_type&>(__v.first), __v.second);
}
_LIBCPP_HIDE_FROM_ABI __nc_rref_pair_type __move() {
value_type& __v = __get_value();
return __nc_rref_pair_type(std::move(const_cast<key_type&>(__v.first)), std::move(__v.second));
}
_LIBCPP_HIDE_FROM_ABI __hash_value_type& operator=(const __hash_value_type& __v) {
__ref() = __v.__get_value();
return *this;
}
_LIBCPP_HIDE_FROM_ABI __hash_value_type& operator=(__hash_value_type&& __v) {
__ref() = __v.__move();
return *this;
}
template <class _ValueTp, __enable_if_t<__is_same_uncvref<_ValueTp, value_type>::value, int> = 0>
_LIBCPP_HIDE_FROM_ABI __hash_value_type& operator=(_ValueTp&& __v) {
__ref() = std::forward<_ValueTp>(__v);
return *this;
}
__hash_value_type(const __hash_value_type& __v) = delete;
__hash_value_type(__hash_value_type&& __v) = delete;
template <class... _Args>
explicit __hash_value_type(_Args&&... __args) = delete;
~__hash_value_type() = delete;
};
# else
template <class _Key, class _Tp>
struct __hash_value_type {
typedef _Key key_type;
typedef _Tp mapped_type;
typedef pair<const key_type, mapped_type> value_type;
private:
value_type __cc_;
public:
_LIBCPP_HIDE_FROM_ABI value_type& __get_value() { return __cc_; }
_LIBCPP_HIDE_FROM_ABI const value_type& __get_value() const { return __cc_; }
~__hash_value_type() = delete;
};
# endif
struct __hash_value_type;
template <class _HashIterator>
class __hash_map_iterator {
@ -941,8 +853,8 @@ public:
_LIBCPP_HIDE_FROM_ABI __hash_map_iterator(_HashIterator __i) _NOEXCEPT : __i_(__i) {}
_LIBCPP_HIDE_FROM_ABI reference operator*() const { return __i_->__get_value(); }
_LIBCPP_HIDE_FROM_ABI pointer operator->() const { return pointer_traits<pointer>::pointer_to(__i_->__get_value()); }
_LIBCPP_HIDE_FROM_ABI reference operator*() const { return *__i_; }
_LIBCPP_HIDE_FROM_ABI pointer operator->() const { return pointer_traits<pointer>::pointer_to(*__i_); }
_LIBCPP_HIDE_FROM_ABI __hash_map_iterator& operator++() {
++__i_;
@ -995,8 +907,8 @@ public:
__hash_map_const_iterator(__hash_map_iterator<typename _HashIterator::__non_const_iterator> __i) _NOEXCEPT
: __i_(__i.__i_) {}
_LIBCPP_HIDE_FROM_ABI reference operator*() const { return __i_->__get_value(); }
_LIBCPP_HIDE_FROM_ABI pointer operator->() const { return pointer_traits<pointer>::pointer_to(__i_->__get_value()); }
_LIBCPP_HIDE_FROM_ABI reference operator*() const { return *__i_; }
_LIBCPP_HIDE_FROM_ABI pointer operator->() const { return pointer_traits<pointer>::pointer_to(*__i_); }
_LIBCPP_HIDE_FROM_ABI __hash_map_const_iterator& operator++() {
++__i_;
@ -1053,8 +965,8 @@ public:
private:
typedef __hash_value_type<key_type, mapped_type> __value_type;
typedef __unordered_map_hasher<key_type, __value_type, hasher, key_equal> __hasher;
typedef __unordered_map_equal<key_type, __value_type, key_equal, hasher> __key_equal;
typedef __unordered_map_hasher<key_type, value_type, hasher, key_equal> __hasher;
typedef __unordered_map_equal<key_type, value_type, key_equal, hasher> __key_equal;
typedef __rebind_alloc<allocator_traits<allocator_type>, __value_type> __allocator_type;
typedef __hash_table<__value_type, __hasher, __key_equal, __allocator_type> __table;
@ -1073,9 +985,6 @@ private:
static_assert(__check_valid_allocator<allocator_type>::value, "");
static_assert(is_same<typename __table::__container_value_type, value_type>::value, "");
static_assert(is_same<typename __table::__node_value_type, __value_type>::value, "");
public:
typedef typename __alloc_traits::pointer pointer;
typedef typename __alloc_traits::const_pointer const_pointer;
@ -1680,9 +1589,8 @@ unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::unordered_map(unordered_map&& __
: __table_(std::move(__u.__table_), typename __table::allocator_type(__a)) {
if (__a != __u.get_allocator()) {
iterator __i = __u.begin();
while (__u.size() != 0) {
__table_.__emplace_unique(__u.__table_.remove((__i++).__i_)->__get_value().__move());
}
while (__u.size() != 0)
__table_.__insert_unique_from_orphaned_node(std::move(__u.__table_.remove((__i++).__i_)->__get_value()));
}
}
@ -1741,8 +1649,7 @@ template <class _Key, class _Tp, class _Hash, class _Pred, class _Alloc>
_Tp& unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::operator[](const key_type& __k) {
return __table_
.__emplace_unique_key_args(__k, piecewise_construct, std::forward_as_tuple(__k), std::forward_as_tuple())
.first->__get_value()
.second;
.first->second;
}
template <class _Key, class _Tp, class _Hash, class _Pred, class _Alloc>
@ -1750,8 +1657,7 @@ _Tp& unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::operator[](key_type&& __k)
return __table_
.__emplace_unique_key_args(
__k, piecewise_construct, std::forward_as_tuple(std::move(__k)), std::forward_as_tuple())
.first->__get_value()
.second;
.first->second;
}
# else // _LIBCPP_CXX03_LANG
@ -1760,9 +1666,9 @@ typename unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::__node_holder
unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::__construct_node_with_key(const key_type& __k) {
__node_allocator& __na = __table_.__node_alloc();
__node_holder __h(__node_traits::allocate(__na, 1), _Dp(__na));
__node_traits::construct(__na, std::addressof(__h->__get_value().__get_value().first), __k);
__node_traits::construct(__na, std::addressof(__h->__get_value().first), __k);
__h.get_deleter().__first_constructed = true;
__node_traits::construct(__na, std::addressof(__h->__get_value().__get_value().second));
__node_traits::construct(__na, std::addressof(__h->__get_value().second));
__h.get_deleter().__second_constructed = true;
return __h;
}
@ -1869,8 +1775,8 @@ public:
private:
typedef __hash_value_type<key_type, mapped_type> __value_type;
typedef __unordered_map_hasher<key_type, __value_type, hasher, key_equal> __hasher;
typedef __unordered_map_equal<key_type, __value_type, key_equal, hasher> __key_equal;
typedef __unordered_map_hasher<key_type, value_type, hasher, key_equal> __hasher;
typedef __unordered_map_equal<key_type, value_type, key_equal, hasher> __key_equal;
typedef __rebind_alloc<allocator_traits<allocator_type>, __value_type> __allocator_type;
typedef __hash_table<__value_type, __hasher, __key_equal, __allocator_type> __table;
@ -2439,9 +2345,8 @@ unordered_multimap<_Key, _Tp, _Hash, _Pred, _Alloc>::unordered_multimap(
: __table_(std::move(__u.__table_), typename __table::allocator_type(__a)) {
if (__a != __u.get_allocator()) {
iterator __i = __u.begin();
while (__u.size() != 0) {
__table_.__insert_multi(__u.__table_.remove((__i++).__i_)->__get_value().__move());
}
while (__u.size() != 0)
__table_.__insert_multi_from_orphaned_node(std::move(__u.__table_.remove((__i++).__i_)->__get_value()));
}
}

View File

@ -20,6 +20,7 @@
#include <cfloat>
#include <cmath>
#include <cstddef>
#include <utility>
#include "test_macros.h"
#include "../../../test_compare.h"
@ -109,6 +110,19 @@ int main(int, char**) {
assert(c.max_load_factor() == 1);
}
#endif
{ // Test with std::pair, since we have some special handling for pairs inside __hash_table
struct pair_hash {
size_t operator()(std::pair<int, int> val) const TEST_NOEXCEPT { return val.first | val.second; }
};
std::pair<int, int> arr[] = {
std::make_pair(1, 2), std::make_pair(2, 3), std::make_pair(3, 4), std::make_pair(4, 5)};
std::unordered_set<std::pair<int, int>, pair_hash> a(arr, arr + 4);
std::unordered_set<std::pair<int, int>, pair_hash> b;
b = a;
assert(a == b);
}
return 0;
}

View File

@ -65,16 +65,6 @@ def _remove_generics(typename):
return match.group(1)
def _cc_field(node):
"""Previous versions of libcxx had inconsistent field naming naming. Handle
both types.
"""
try:
return node["__value_"]["__cc_"]
except:
return node["__value_"]["__cc"]
def _data_field(node):
"""Previous versions of libcxx had inconsistent field naming naming. Handle
both types.
@ -829,7 +819,7 @@ class StdUnorderedMapPrinter(AbstractUnorderedCollectionPrinter):
"""Print a std::unordered_(multi)map."""
def _get_key_value(self, node):
key_value = _cc_field(node)
key_value = node["__value_"]
return [key_value["first"], key_value["second"]]
def display_hint(self):
@ -885,7 +875,7 @@ class StdUnorderedMapIteratorPrinter(AbstractHashMapIteratorPrinter):
self._initialize(val, val["__i_"]["__node_"])
def _get_key_value(self):
key_value = _cc_field(self.node)
key_value = self.node["__value_"]
return [key_value["first"], key_value["second"]]
def display_hint(self):