Skip to content

Conversation

@sssersh
Copy link

@sssersh sssersh commented Apr 11, 2025

Method __get_value() in __hash_value_type is used to access to address of stored in __hash_value_type value.

This method called in __hash_table::__construct_node via _NodeTypes::__get_ptr() before creation of object __hash_value_type.

But according to [basic.life]:
"Before the lifetime of an object has started but after the storage which the object will occupy has been allocated23 or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any pointer that represents the address of the storage location where the object will be or was located may be used but only in limited ways.[...]
The program has undefined behavior if [...] the pointer is used to access a non-static data member or call a non-static member function of the object [...]".

But it's possible to access to member in __hash_value_type using pointer interconvertibility according to [basic.compound]: "Two objects a and b are pointer-interconvertible if [...] - one is a standard-layout class object and the other is the first non-static data member of that object, or, if the object has no non-static data members, the first base class subobject of that object, or [...]. If two objects are pointer-interconvertible, then they have the same address, and it is possible to obtain a pointer to one from a pointer to the other via a reinterpret_cast."

…ue_type

Method __get_value() in __hash_value_type is used to access to address of stored in __hash_value_type value.

This method called in __hash_table::__construct_node via _NodeTypes::__get_ptr() **before** creation of object __hash_value_type.

But according to [basic.life]: "The program has undefined behavior if [...] the pointer is used to access a non-static data member or call a non-static member function of the object [...]".

But it's possible to access to member in __hash_value_type using pointer interconvertibility according to [basic.compound]:
"Two objects `a` and `b` are pointer-interconvertible if [...] - one is a standard-layout class object and the other is the first non-static data member of that object, or, if the object has no non-static data members, the first base class subobject of that object, or [...]. If two objects are pointer-interconvertible, then they have the same address, and it is possible to obtain a pointer to one from a pointer to the other via a `reinterpret_cast`."
@sssersh sssersh requested a review from a team as a code owner April 11, 2025 13:08
@github-actions
Copy link

Thank you for submitting a Pull Request (PR) to the LLVM Project!

This PR will be automatically labeled and the relevant teams will be notified.

If you wish to, you can add reviewers by using the "Reviewers" section on this page.

If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using @ followed by their GitHub username.

If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers.

If you have further questions, they may be answered by the LLVM GitHub User Guide.

You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums.

@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Apr 11, 2025
@llvmbot
Copy link
Member

llvmbot commented Apr 11, 2025

@llvm/pr-subscribers-libcxx

Author: Sergey (sssersh)

Changes

Method __get_value() in __hash_value_type is used to access to address of stored in __hash_value_type value.

This method called in __hash_table::__construct_node via _NodeTypes::__get_ptr() before creation of object __hash_value_type.

But according to [basic.life]: "The program has undefined behavior if [...] the pointer is used to access a non-static data member or call a non-static member function of the object [...]".

But it's possible to access to member in __hash_value_type using pointer interconvertibility according to [basic.compound]: "Two objects a and b are pointer-interconvertible if [...] - one is a standard-layout class object and the other is the first non-static data member of that object, or, if the object has no non-static data members, the first base class subobject of that object, or [...]. If two objects are pointer-interconvertible, then they have the same address, and it is possible to obtain a pointer to one from a pointer to the other via a reinterpret_cast."


Patch is 45.12 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/135362.diff

10 Files Affected:

  • (modified) libcxx/include/__cxx03/__hash_table (+2-2)
  • (modified) libcxx/include/__cxx03/__node_handle (+3-3)
  • (modified) libcxx/include/__cxx03/__tree (+3-3)
  • (modified) libcxx/include/__cxx03/map (+57-41)
  • (modified) libcxx/include/__cxx03/unordered_map (+22-10)
  • (modified) libcxx/include/__hash_table (+2-2)
  • (modified) libcxx/include/__tree (+2-2)
  • (modified) libcxx/include/ext/hash_map (+4-4)
  • (modified) libcxx/include/map (+56-41)
  • (modified) libcxx/include/unordered_map (+60-40)
diff --git a/libcxx/include/__cxx03/__hash_table b/libcxx/include/__cxx03/__hash_table
index 4a27681442c27..72da4d724741e 100644
--- a/libcxx/include/__cxx03/__hash_table
+++ b/libcxx/include/__cxx03/__hash_table
@@ -185,7 +185,7 @@ struct __hash_key_value_types<__hash_value_type<_Key, _Tp> > {
 
   template <class _Up, __enable_if_t<__is_same_uncvref<_Up, __node_value_type>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI static __container_value_type const& __get_value(_Up& __t) {
-    return __t.__get_value();
+    return _Up::__get_value(__t);
   }
 
   template <class _Up, __enable_if_t<__is_same_uncvref<_Up, __container_value_type>::value, int> = 0>
@@ -194,7 +194,7 @@ struct __hash_key_value_types<__hash_value_type<_Key, _Tp> > {
   }
 
   _LIBCPP_HIDE_FROM_ABI static __container_value_type* __get_ptr(__node_value_type& __n) {
-    return std::addressof(__n.__get_value());
+    return std::addressof(__node_value_type::__get_value(__n));
   }
   _LIBCPP_HIDE_FROM_ABI static pair<key_type&&, mapped_type&&> __move(__node_value_type& __v) { return __v.__move(); }
 };
diff --git a/libcxx/include/__cxx03/__node_handle b/libcxx/include/__cxx03/__node_handle
index 6b272f9a49fff..77361d7066812 100644
--- a/libcxx/include/__cxx03/__node_handle
+++ b/libcxx/include/__cxx03/__node_handle
@@ -170,7 +170,7 @@ template <class _NodeType, class _Derived>
 struct __set_node_handle_specifics {
   typedef typename _NodeType::__node_value_type value_type;
 
-  _LIBCPP_HIDE_FROM_ABI value_type& value() const { return static_cast<_Derived const*>(this)->__ptr_->__get_value(); }
+  _LIBCPP_HIDE_FROM_ABI value_type& value() const { return value_type::__get_value(*static_cast<_Derived const*>(this)->__ptr_); }
 };
 
 template <class _NodeType, class _Derived>
@@ -179,11 +179,11 @@ struct __map_node_handle_specifics {
   typedef typename _NodeType::__node_value_type::mapped_type mapped_type;
 
   _LIBCPP_HIDE_FROM_ABI key_type& key() const {
-    return static_cast<_Derived const*>(this)->__ptr_->__get_value().__ref().first;
+    return value_type::__get_value(*static_cast<_Derived const*>(this)->__ptr_).__ref().first;
   }
 
   _LIBCPP_HIDE_FROM_ABI mapped_type& mapped() const {
-    return static_cast<_Derived const*>(this)->__ptr_->__get_value().__ref().second;
+    return value_type::__get_value(*static_cast<_Derived const*>(this)->__ptr_).__ref().second;
   }
 };
 
diff --git a/libcxx/include/__cxx03/__tree b/libcxx/include/__cxx03/__tree
index 8982baf18d1a9..18b2205ecad98 100644
--- a/libcxx/include/__cxx03/__tree
+++ b/libcxx/include/__cxx03/__tree
@@ -533,7 +533,7 @@ struct __tree_key_value_types<__value_type<_Key, _Tp> > {
   static const bool __is_map = true;
 
   _LIBCPP_HIDE_FROM_ABI static key_type const& __get_key(__node_value_type const& __t) {
-    return __t.__get_value().first;
+    return __node_value_type::__get_value(__t).first;
   }
 
   template <class _Up, __enable_if_t<__is_same_uncvref<_Up, __container_value_type>::value, int> = 0>
@@ -542,7 +542,7 @@ struct __tree_key_value_types<__value_type<_Key, _Tp> > {
   }
 
   _LIBCPP_HIDE_FROM_ABI static __container_value_type const& __get_value(__node_value_type const& __t) {
-    return __t.__get_value();
+    return __node_value_type::__get_value(__t);
   }
 
   template <class _Up, __enable_if_t<__is_same_uncvref<_Up, __container_value_type>::value, int> = 0>
@@ -551,7 +551,7 @@ struct __tree_key_value_types<__value_type<_Key, _Tp> > {
   }
 
   _LIBCPP_HIDE_FROM_ABI static __container_value_type* __get_ptr(__node_value_type& __n) {
-    return std::addressof(__n.__get_value());
+    return std::addressof(__node_value_type::__get_value(__n));
   }
 
   _LIBCPP_HIDE_FROM_ABI static pair<key_type&&, mapped_type&&> __move(__node_value_type& __v) { return __v.__move(); }
diff --git a/libcxx/include/__cxx03/map b/libcxx/include/__cxx03/map
index bd03d8b741a71..d084a3a8434f0 100644
--- a/libcxx/include/__cxx03/map
+++ b/libcxx/include/__cxx03/map
@@ -592,6 +592,7 @@ erase_if(multimap<Key, T, Compare, Allocator>& c, Predicate pred);  // C++20
 #include <__cxx03/__ranges/from_range.h>
 #include <__cxx03/__tree>
 #include <__cxx03/__type_traits/is_allocator.h>
+#include <__cxx03/__type_traits/is_standard_layout.h>
 #include <__cxx03/__utility/forward.h>
 #include <__cxx03/__utility/piecewise_construct.h>
 #include <__cxx03/__utility/swap.h>
@@ -633,13 +634,13 @@ public:
       : _Compare(__c) {}
   _LIBCPP_HIDE_FROM_ABI const _Compare& key_comp() const _NOEXCEPT { return *this; }
   _LIBCPP_HIDE_FROM_ABI bool operator()(const _CP& __x, const _CP& __y) const {
-    return static_cast<const _Compare&>(*this)(__x.__get_value().first, __y.__get_value().first);
+    return static_cast<const _Compare&>(*this)(_CP::__get_value(__x).first, _CP::__get_value(__y).first);
   }
   _LIBCPP_HIDE_FROM_ABI bool operator()(const _CP& __x, const _Key& __y) const {
-    return static_cast<const _Compare&>(*this)(__x.__get_value().first, __y);
+    return static_cast<const _Compare&>(*this)(_CP::__get_value(__x).first, __y);
   }
   _LIBCPP_HIDE_FROM_ABI bool operator()(const _Key& __x, const _CP& __y) const {
-    return static_cast<const _Compare&>(*this)(__x, __y.__get_value().first);
+    return static_cast<const _Compare&>(*this)(__x, _CP::__get_value(__y).first);
   }
   _LIBCPP_HIDE_FROM_ABI void swap(__map_value_compare& __y) _NOEXCEPT_(__is_nothrow_swappable_v<_Compare>) {
     using std::swap;
@@ -649,12 +650,12 @@ public:
 #if _LIBCPP_STD_VER >= 14
   template <typename _K2>
   _LIBCPP_HIDE_FROM_ABI bool operator()(const _K2& __x, const _CP& __y) const {
-    return static_cast<const _Compare&>(*this)(__x, __y.__get_value().first);
+    return static_cast<const _Compare&>(*this)(__x, _CP::__get_value(__y).first);
   }
 
   template <typename _K2>
   _LIBCPP_HIDE_FROM_ABI bool operator()(const _CP& __x, const _K2& __y) const {
-    return static_cast<const _Compare&>(*this)(__x.__get_value().first, __y);
+    return static_cast<const _Compare&>(*this)(_CP::__get_value(__x).first, __y);
   }
 #endif
 };
@@ -671,13 +672,13 @@ public:
   _LIBCPP_HIDE_FROM_ABI const _Compare& key_comp() const _NOEXCEPT { return __comp_; }
 
   _LIBCPP_HIDE_FROM_ABI bool operator()(const _CP& __x, const _CP& __y) const {
-    return __comp_(__x.__get_value().first, __y.__get_value().first);
+    return __comp_(_CP::__get_value(__x).first, _CP::__get_value(__y).first);
   }
   _LIBCPP_HIDE_FROM_ABI bool operator()(const _CP& __x, const _Key& __y) const {
-    return __comp_(__x.__get_value().first, __y);
+    return __comp_(_CP::__get_value(__x).first, __y);
   }
   _LIBCPP_HIDE_FROM_ABI bool operator()(const _Key& __x, const _CP& __y) const {
-    return __comp_(__x, __y.__get_value().first);
+    return __comp_(__x, _CP::__get_value(__y).first);
   }
   void swap(__map_value_compare& __y) _NOEXCEPT_(__is_nothrow_swappable_v<_Compare>) {
     using std::swap;
@@ -687,12 +688,12 @@ public:
 #if _LIBCPP_STD_VER >= 14
   template <typename _K2>
   _LIBCPP_HIDE_FROM_ABI bool operator()(const _K2& __x, const _CP& __y) const {
-    return __comp_(__x, __y.__get_value().first);
+    return __comp_(__x, _CP::__get_value(__y).first);
   }
 
   template <typename _K2>
   _LIBCPP_HIDE_FROM_ABI bool operator()(const _CP& __x, const _K2& __y) const {
-    return __comp_(__x.__get_value().first, __y);
+    return __comp_(_CP::__get_value(__x).first, __y);
   }
 #endif
 };
@@ -708,6 +709,7 @@ template <class _Allocator>
 class __map_node_destructor {
   typedef _Allocator allocator_type;
   typedef allocator_traits<allocator_type> __alloc_traits;
+  typedef __alloc_traits::value_type value_type;
 
 public:
   typedef typename __alloc_traits::pointer pointer;
@@ -737,9 +739,9 @@ public:
 
   _LIBCPP_HIDE_FROM_ABI void operator()(pointer __p) _NOEXCEPT {
     if (__second_constructed)
-      __alloc_traits::destroy(__na_, std::addressof(__p->__value_.__get_value().second));
+      __alloc_traits::destroy(__na_, std::addressof(value_type::__get_value(__p->__value_).second));
     if (__first_constructed)
-      __alloc_traits::destroy(__na_, std::addressof(__p->__value_.__get_value().first));
+      __alloc_traits::destroy(__na_, std::addressof(value_type::__get_value(__p->__value_).first));
     if (__p)
       __alloc_traits::deallocate(__na_, __p, 1);
   }
@@ -765,35 +767,38 @@ struct _LIBCPP_STANDALONE_DEBUG __value_type {
 private:
   value_type __cc_;
 
+private:
+  static_assert(is_standard_layout_v<__hash_value_type<_Key, _Tp>>, "");
+
 public:
-  _LIBCPP_HIDE_FROM_ABI value_type& __get_value() {
+  _LIBCPP_HIDE_FROM_ABI static value_type& __get_value(__hash_value_type& __v) {
 #  if _LIBCPP_STD_VER >= 17
-    return *std::launder(std::addressof(__cc_));
+    return *std::launder(std::addressof(reinterpret_cast<value_type&>(__v)));
 #  else
-    return __cc_;
+    return reinterpret_cast<value_type&>(__v);
 #  endif
   }
 
-  _LIBCPP_HIDE_FROM_ABI const value_type& __get_value() const {
+  _LIBCPP_HIDE_FROM_ABI static const value_type& __get_value(const __hash_value_type& __v) {
 #  if _LIBCPP_STD_VER >= 17
-    return *std::launder(std::addressof(__cc_));
+    return *std::launder(std::addressof(reinterpret_cast<const value_type&>(__v)));
 #  else
-    return __cc_;
+    return reinterpret_cast<const value_type&>(__v);
 #  endif
   }
 
   _LIBCPP_HIDE_FROM_ABI __nc_ref_pair_type __ref() {
-    value_type& __v = __get_value();
+    value_type& __v = __get_value(*this);
     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();
+    value_type& __v = __get_value(*this);
     return __nc_rref_pair_type(std::move(const_cast<key_type&>(__v.first)), std::move(__v.second));
   }
 
   _LIBCPP_HIDE_FROM_ABI __value_type& operator=(const __value_type& __v) {
-    __ref() = __v.__get_value();
+    __ref() = __get_value(__v);
     return *this;
   }
 
@@ -826,8 +831,13 @@ 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_; }
+  _LIBCPP_HIDE_FROM_ABI static value_type& __get_value(__hash_value_type& __v) {
+    return reinterpret_cast<value_type&>(__v);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI static const value_type& __get_value(const __hash_value_type& __v) const {
+    return reinterpret_cast<const value_type&>(__v);
+  }
 
   __value_type()                               = delete;
   __value_type(__value_type const&)            = delete;
@@ -864,8 +874,10 @@ public:
 
   _LIBCPP_HIDE_FROM_ABI __map_iterator(_TreeIterator __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 value_type::__get_value(*__i_); }
+  _LIBCPP_HIDE_FROM_ABI pointer operator->() const {
+    return pointer_traits<pointer>::pointer_to(value_type::__get_value(*__i_));
+  }
 
   _LIBCPP_HIDE_FROM_ABI __map_iterator& operator++() {
     ++__i_;
@@ -922,8 +934,10 @@ public:
   _LIBCPP_HIDE_FROM_ABI
   __map_const_iterator(__map_iterator< typename _TreeIterator::__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 value_type::__get_value(*__i_); }
+  _LIBCPP_HIDE_FROM_ABI pointer operator->() const {
+    return pointer_traits<pointer>::pointer_to(value_type::__get_value(*__i_));
+  }
 
   _LIBCPP_HIDE_FROM_ABI __map_const_iterator& operator++() {
     ++__i_;
@@ -1286,7 +1300,7 @@ public:
     auto [__r, __inserted] = __tree_.__emplace_hint_unique_key_args(__h.__i_, __k, __k, std::forward<_Vp>(__v));
 
     if (!__inserted)
-      __r->__get_value().second = std::forward<_Vp>(__v);
+      __value_type::__get_value(*__r).second = std::forward<_Vp>(__v);
 
     return __r;
   }
@@ -1297,7 +1311,7 @@ public:
         __tree_.__emplace_hint_unique_key_args(__h.__i_, __k, std::move(__k), std::forward<_Vp>(__v));
 
     if (!__inserted)
-      __r->__get_value().second = std::forward<_Vp>(__v);
+      __value_type::__get_value(*__r).second = std::forward<_Vp>(__v);
 
     return __r;
   }
@@ -1509,9 +1523,10 @@ map<_Key, _Tp, _Compare, _Allocator>::map(map&& __m, const allocator_type& __a)
 
 template <class _Key, class _Tp, class _Compare, class _Allocator>
 _Tp& map<_Key, _Tp, _Compare, _Allocator>::operator[](const key_type& __k) {
-  return __tree_
-      .__emplace_unique_key_args(__k, std::piecewise_construct, std::forward_as_tuple(__k), std::forward_as_tuple())
-      .first->__get_value()
+  return _Tp::__get_value(__tree_
+                              .__emplace_unique_key_args(
+                                  __k, std::piecewise_construct, std::forward_as_tuple(__k), std::forward_as_tuple())
+                              .first)
       .second;
 }
 
@@ -1519,10 +1534,11 @@ template <class _Key, class _Tp, class _Compare, class _Allocator>
 _Tp& map<_Key, _Tp, _Compare, _Allocator>::operator[](key_type&& __k) {
   // TODO investigate this clang-tidy warning.
   // NOLINTBEGIN(bugprone-use-after-move)
-  return __tree_
-      .__emplace_unique_key_args(
-          __k, std::piecewise_construct, std::forward_as_tuple(std::move(__k)), std::forward_as_tuple())
-      .first->__get_value()
+  return _Tp::__get_value(
+             __tree_
+                 .__emplace_unique_key_args(
+                     __k, std::piecewise_construct, std::forward_as_tuple(std::move(__k)), std::forward_as_tuple())
+                 .first)
       .second;
   // NOLINTEND(bugprone-use-after-move)
 }
@@ -1534,9 +1550,9 @@ typename map<_Key, _Tp, _Compare, _Allocator>::__node_holder
 map<_Key, _Tp, _Compare, _Allocator>::__construct_node_with_key(const key_type& __k) {
   __node_allocator& __na = __tree_.__node_alloc();
   __node_holder __h(__node_traits::allocate(__na, 1), _Dp(__na));
-  __node_traits::construct(__na, std::addressof(__h->__value_.__get_value().first), __k);
+  __node_traits::construct(__na, std::addressof(_Tp::__get_value(__h->__value_).first), __k);
   __h.get_deleter().__first_constructed = true;
-  __node_traits::construct(__na, std::addressof(__h->__value_.__get_value().second));
+  __node_traits::construct(__na, std::addressof(_Tp::__get_value(__h->__value_).second));
   __h.get_deleter().__second_constructed = true;
   return __h;
 }
@@ -1551,7 +1567,7 @@ _Tp& map<_Key, _Tp, _Compare, _Allocator>::operator[](const key_type& __k) {
     __tree_.__insert_node_at(__parent, __child, static_cast<__node_base_pointer>(__h.get()));
     __r = __h.release();
   }
-  return __r->__value_.__get_value().second;
+  return _Tp::__get_value(__r->__value_).second;
 }
 
 #endif // _LIBCPP_CXX03_LANG
@@ -1562,7 +1578,7 @@ _Tp& map<_Key, _Tp, _Compare, _Allocator>::at(const key_type& __k) {
   __node_base_pointer& __child = __tree_.__find_equal(__parent, __k);
   if (__child == nullptr)
     __throw_out_of_range("map::at:  key not found");
-  return static_cast<__node_pointer>(__child)->__value_.__get_value().second;
+  return _Tp::__get_value(static_cast<__node_pointer>(__child)->__value_).second;
 }
 
 template <class _Key, class _Tp, class _Compare, class _Allocator>
@@ -1571,7 +1587,7 @@ const _Tp& map<_Key, _Tp, _Compare, _Allocator>::at(const key_type& __k) const {
   __node_base_pointer __child = __tree_.__find_equal(__parent, __k);
   if (__child == nullptr)
     __throw_out_of_range("map::at:  key not found");
-  return static_cast<__node_pointer>(__child)->__value_.__get_value().second;
+  return _Tp::__get_value(static_cast<__node_pointer>(__child)->__value_).second;
 }
 
 template <class _Key, class _Tp, class _Compare, class _Allocator>
diff --git a/libcxx/include/__cxx03/unordered_map b/libcxx/include/__cxx03/unordered_map
index 612ad02f77164..df7adf91aaefe 100644
--- a/libcxx/include/__cxx03/unordered_map
+++ b/libcxx/include/__cxx03/unordered_map
@@ -601,6 +601,7 @@ template <class Key, class T, class Hash, class Pred, class Alloc>
 #include <__cxx03/__ranges/container_compatible_range.h>
 #include <__cxx03/__ranges/from_range.h>
 #include <__cxx03/__type_traits/is_allocator.h>
+#include <__cxx03/__type_traits/is_standard_layout.h>
 #include <__cxx03/__type_traits/type_identity.h>
 #include <__cxx03/__utility/forward.h>
 #include <__cxx03/stdexcept>
@@ -839,30 +840,33 @@ struct _LIBCPP_STANDALONE_DEBUG __hash_value_type {
 private:
   value_type __cc_;
 
+private:
+  static_assert(is_standard_layout_v<__hash_value_type<_Key, _Tp>>, "");
+
 public:
-  _LIBCPP_HIDE_FROM_ABI value_type& __get_value() {
+  _LIBCPP_HIDE_FROM_ABI static value_type& __get_value(__hash_value_type& __v) {
 #  if _LIBCPP_STD_VER >= 17
-    return *std::launder(std::addressof(__cc_));
+    return *std::launder(std::addressof(reinterpret_cast<value_type&>(__v)));
 #  else
-    return __cc_;
+    return reinterpret_cast<value_type&>(__v);
 #  endif
   }
 
-  _LIBCPP_HIDE_FROM_ABI const value_type& __get_value() const {
+  _LIBCPP_HIDE_FROM_ABI static const value_type& __get_value(const __hash_value_type& __v) {
 #  if _LIBCPP_STD_VER >= 17
-    return *std::launder(std::addressof(__cc_));
+    return *std::launder(std::addressof(reinterpret_cast<const value_type&>(__v)));
 #  else
-    return __cc_;
+    return reinterpret_cast<const value_type&>(__v);
 #  endif
   }
 
   _LIBCPP_HIDE_FROM_ABI __nc_ref_pair_type __ref() {
-    value_type& __v = __get_value();
+    value_type& __v = __get_value(*this);
     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();
+    value_type& __v = __get_value(*this);
     return __nc_rref_pair_type(std::move(const_cast<key_type&>(__v.first)), std::move(__v.second));
   }
 
@@ -901,9 +905,17 @@ struct __hash_value_type {
 private:
   value_type __cc_;
 
+private:
+  static_assert(std::is_standard_layout_v<__hash_value_type<_Key, _Tp>>, "");
+
 public:
-  _LIBCPP_HIDE_FROM_ABI value_type& __get_value() { return __cc_; }
-  _LIBCPP_HIDE_FROM_ABI const value_type& __get_value() const { return __cc_; }
+  _LIBCPP_HIDE_FROM_ABI static value_type& __get_value(__hash_value_type& __v) {
+    return reinterpret_cast<value_type&>(__v);
+  }
+
+  _LIBCPP_HIDE_FROM_ABI static const value_type& __get_value(const __hash_value_type& __v) const {
+    return reinterpret_cast<const value_type&>(__v);
+  }
 
   ~__hash_value_type() = delete;
 };
diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table
index d7b312f8774fc..8ba25c4cd4c82 100644
--- a/libcxx/include/__hash_table
+++ b/libcxx/include/__hash_table
@@ -193,7 +193,7 @@ struct __hash_key_value_types<__hash_value_type<_Key, _Tp> > {
 
   template <class _Up, __enable_if_t<__is_same_uncvref<_Up, __node_value_type>::value, int> = 0>
   _LIBCPP_HIDE_FROM_ABI static __container_value_type const& __get_value(_Up& __t) {
-    return __t.__get_value();
+    return __node_value_type::__get_value(__t);
   }
 
   template <class _Up, __enable_if_t<__is_same_uncvref<_Up, __container_value_type>::value, int> = 0>
@@ -202,7 +202,7 @@ struct __hash_key_value_types<__hash_value_type<_Key, _Tp> > {
   }
 
   _LIBCPP_HIDE_FROM_ABI static __container_value_type* __get_ptr(__node_value_type& __n) {
-    return std::addressof(__n.__get_value());
+    return std::addressof(__node_value_type::__get_value(__n));
   }
   _LIBCPP_HIDE_FROM_ABI static pair<key_type&&, mapped_type&&> __move(__node_value_type& __v) { return __v.__move(); }
 };
diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index 9d28381c8c2ce..e1a1a2f956dee 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -534,7 +534,7 @@ struct __tree_key_value_types<__value_type<_Key, _Tp> > {
   }
 
   _LIBCPP_HIDE_FROM_ABI static __container_value_type const& __get_value(__node_value_type const& __t) {
-    return __t.__get_value();
+    return __node_value_type::__get_value(__t);
   }
 
   template <class _Up, __enable_if_t<__is_same_uncvref<_Up, __container_value_type>::value, int> = 0>
@@ -543,7 +543,7 @@ struct __tree_key_value_types<_...
[truncated]

@sssersh sssersh closed this Apr 11, 2025
@sssersh sssersh deleted the unordered_map_fix_ub branch April 11, 2025 13:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants