[committed,1/2] libstdc++: Avoid instantiation of _Hash_node before it's needed

Message ID YWDbTGFXF3IMTRjE@redhat.com
State Committed
Commit 64acc43de1e33616e43b239887a260eb4a51fcc7
Headers
Series [committed,1/2] libstdc++: Avoid instantiation of _Hash_node before it's needed |

Commit Message

Jonathan Wakely Oct. 8, 2021, 11:59 p.m. UTC
  This is a step towards restoring support for incomplete types in
unordered containers (PR 53339).

We do not need to instantiate the node type to get its value_type
member, because we know that the value type is the first template
parameter. We can deduce that template argument using a custom trait and
a partial specialization for _Hash_node. If we wanted to support custom
hash node types we could still use typename _Tp::value_type in the
primary template of that trait, but that seems unnecessary.

The other change needed is to defer a static assert at class scope, so
that it is done when the types are complete. We must have a complete
type in the destructor, so we can do it there instead.

libstdc++-v3/ChangeLog:

	* include/bits/hashtable.h: Move static assertion to destructor.
	* include/bits/hashtable_policy.h: Deduce value type from node
	type without instantiating it.

Tested powerpc64le-linux. Committed to trunk.

This is the patch I referred to in:
https://gcc.gnu.org/pipermail/gcc-patches/2021-July/576028.html
commit 64acc43de1e33616e43b239887a260eb4a51fcc7
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Fri Oct 8 13:35:54 2021

    libstdc++: Avoid instantiation of _Hash_node before it's needed
    
    This is a step towards restoring support for incomplete types in
    unordered containers (PR 53339).
    
    We do not need to instantiate the node type to get its value_type
    member, because we know that the value type is the first template
    parameter. We can deduce that template argument using a custom trait and
    a partial specialization for _Hash_node. If we wanted to support custom
    hash node types we could still use typename _Tp::value_type in the
    primary template of that trait, but that seems unnecessary.
    
    The other change needed is to defer a static assert at class scope, so
    that it is done when the types are complete. We must have a complete
    type in the destructor, so we can do it there instead.
    
    libstdc++-v3/ChangeLog:
    
            * include/bits/hashtable.h: Move static assertion to destructor.
            * include/bits/hashtable_policy.h: Deduce value type from node
            type without instantiating it.
  

Patch

diff --git a/libstdc++-v3/include/bits/hashtable.h b/libstdc++-v3/include/bits/hashtable.h
index 79a3096b62b..ff8af2201cd 100644
--- a/libstdc++-v3/include/bits/hashtable.h
+++ b/libstdc++-v3/include/bits/hashtable.h
@@ -329,14 +329,6 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
       struct __hash_code_base_access : __hash_code_base
       { using __hash_code_base::_M_bucket_index; };
 
-      // Getting a bucket index from a node shall not throw because it is used
-      // in methods (erase, swap...) that shall not throw.
-      static_assert(noexcept(declval<const __hash_code_base_access&>()
-			._M_bucket_index(declval<const __node_value_type&>(),
-					 (std::size_t)0)),
-		    "Cache the hash code or qualify your functors involved"
-		    " in hash code and bucket index computation with noexcept");
-
       // To get bucket index we need _RangeHash not to throw.
       static_assert(is_nothrow_default_constructible<_RangeHash>::value,
 		    "Functor used to map hash code to bucket index"
@@ -1556,6 +1548,15 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 	       _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
     ~_Hashtable() noexcept
     {
+      // Getting a bucket index from a node shall not throw because it is used
+      // in methods (erase, swap...) that shall not throw. Need a complete
+      // type to check this, so do it in the destructor not at class scope.
+      static_assert(noexcept(declval<const __hash_code_base_access&>()
+			._M_bucket_index(declval<const __node_value_type&>(),
+					 (std::size_t)0)),
+		    "Cache the hash code or qualify your functors involved"
+		    " in hash code and bucket index computation with noexcept");
+
       clear();
       _M_deallocate_buckets();
     }
diff --git a/libstdc++-v3/include/bits/hashtable_policy.h b/libstdc++-v3/include/bits/hashtable_policy.h
index 2f8502588f5..75488da13f7 100644
--- a/libstdc++-v3/include/bits/hashtable_policy.h
+++ b/libstdc++-v3/include/bits/hashtable_policy.h
@@ -1840,6 +1840,13 @@  namespace __detail
     {
     private:
       using __ebo_node_alloc = _Hashtable_ebo_helper<0, _NodeAlloc>;
+
+      template<typename>
+	struct __get_value_type;
+      template<typename _Val, bool _Cache_hash_code>
+	struct __get_value_type<_Hash_node<_Val, _Cache_hash_code>>
+	{ using type = _Val; };
+
     public:
       using __node_type = typename _NodeAlloc::value_type;
       using __node_alloc_type = _NodeAlloc;
@@ -1847,7 +1854,7 @@  namespace __detail
       using __node_alloc_traits = __gnu_cxx::__alloc_traits<__node_alloc_type>;
 
       using __value_alloc_traits = typename __node_alloc_traits::template
-	rebind_traits<typename __node_type::value_type>;
+	rebind_traits<typename __get_value_type<__node_type>::type>;
 
       using __node_ptr = __node_type*;
       using __node_base = _Hash_node_base;