[Bug libstdc++/115285] [12/13/14/15 Regression] std::unordered_set can have duplicate value

Jonathan Wakely jwakely@redhat.com
Thu Oct 31 19:16:02 GMT 2024


On 31/10/24 19:11 +0000, Jonathan Wakely wrote:
>On 31/10/24 10:38 +0000, Jonathan Wakely wrote:
>>On 28/10/24 21:51 +0100, François Dumont wrote:
>>>
>>>>On 24/10/2024 21:49, Jonathan Wakely wrote:
>>>>>On Thu, 24 Oct 2024 at 19:43, François Dumont<frs.dumont@gmail.com>  wrote:
>>>>>>Committed as trivial the attached patch.
>>>>>>
>>>>>>    libstdc++: Fix test broken when using COW std::string
>>>>>>
>>>>>>    libstdc++-v3/ChangeLog:
>>>>>>
>>>>>>            * testsuite/23_containers/unordered_map/96088.cc (test03):
>>>>>Thanks! I think it was affecting the unordered_set test too.
>>>>>
>>>Hi
>>>
>>>This was indeed revealing a problem in my attempt to fix this bug.
>>>
>>>Here is the complete patch that I will backport if validated.
>>>
>>>libstdc++: [_Hashtable] Avoid temporaries when inserting existing key
>>>
>>>Following PR 115285 fix, the hashtable _S_forward_key responsible 
>>>for finding
>>>the best way to transmit the key part of the argument parameter to the hash
>>>functor was modified to return a key_type instance for any 
>>>argument type. Only
>>>the overloads for const key_type& and key_type&& were still 
>>>preventing a key_type
>>>instanciation.
>>>
>>>It revealed that a overload for key_type& was missing. To fix this 
>>>problem we now
>>>deal with all sort of key_type of reference type in the unique 
>>>_S_forward_key static
>>>method and remove the overloads. When decayed type of the input 
>>>instance is the same
>>>as key_type we avoid the instanciation. Note that for the 
>>>comparison we also decay
>>>key_type to properly managed when unordered container is 
>>>instantiated with a const
>>>type.
>>>
>>>libstdc++-v3/ChangeLog:
>>>
>>>        * include/bits/hashtable_policy.h
>>>        (_ConvertToValueType<_Select1st, 
>>>_Value>::operator(std::pair<>&)): New,
>>>        allow consistent behavior with 
>>>_ConvertToValueType<_Identity, _Value>.
>>>        * include/bits/hashtable.h
>>>        (_S_forward_key<_Kt>(_Kt&&)): Adapt to return _Kt&& when 
>>>decayed _Kt is
>>>        key_type, return a new key_type instance otherwise.
>>>        (_S_forward_key(const key_type&)): Remove.
>>>        (_S_forward_key(key_type&&)): Remove.
>>>        * testsuite/23_containers/unordered_set/96088.cc (test03): 
>>>Adapt expected
>>>        allocation increment.
>>>
>>>Tested under linux x64, both std::string abi.
>>>
>>>Ok to commit ?
>>>
>>>François
>>
>>>diff --git a/libstdc++-v3/include/bits/hashtable.h b/libstdc++-v3/include/bits/hashtable.h
>>>index f1a5490b5ff..8324e75aefd 100644
>>>--- a/libstdc++-v3/include/bits/hashtable.h
>>>+++ b/libstdc++-v3/include/bits/hashtable.h
>>>@@ -930,18 +930,12 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>>	_M_insert_unique(_Kt&&, _Arg&&, _NodeGenerator&);
>>>
>>>     template<typename _Kt>
>>>-	key_type
>>>+	static __conditional_t<std::is_same<__decay_t<_Kt>,
>>>+					    __decay_t<key_type>>::value,
>>>+			       _Kt&&, key_type>
>>>	_S_forward_key(_Kt&& __k)
>>>	{ return std::forward<_Kt>(__k); }
>>
>>Does this assume that __k is implicitly convertible to key_type?
>>
>>e.g. if Kt is int and the key_type(int) constructor is explicit?
>
>Yes, this fails, and I think it should compile:
>
>#include <unordered_set>
>
>struct K {
>  explicit K(int) noexcept { }
>  bool operator==(const K&) const { return true; }
>};
>
>template<> struct std::hash<K> {
>  auto operator()(const K&) const { return 0ul; }
>};
>
>int main()
>{
>  int i[1]{};
>  std::unordered_set<K> s(i, i);
>}
>
>
>>Does it need to be something like:
>>
>>template<typename _Kt>
>> static __conditional_t<is_same<const _Kt&, const key_type&>::value,
>>                        _Kt&&, key_type>
>> _S_forward_key(_Kt&& __k)
>> {
>>   using type = __conditional_t<is_same<const _Kt&, const key_type&>::value,
>>                                _Kt&&, key_type>
>>   return static_cast<type>(__k);
>> }
>>
>>?

Can we just remove _S_forward_key and use static_cast directly?


--- a/libstdc++-v3/include/bits/hashtable.h
+++ b/libstdc++-v3/include/bits/hashtable.h
@@ -929,26 +929,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
  	std::pair<iterator, bool>
  	_M_insert_unique(_Kt&&, _Arg&&, _NodeGenerator&);
  
-      template<typename _Kt>
-	key_type
-	_S_forward_key(_Kt&& __k)
-	{ return std::forward<_Kt>(__k); }
-
-      static const key_type&
-      _S_forward_key(const key_type& __k)
-      { return __k; }
-
-      static key_type&&
-      _S_forward_key(key_type&& __k)
-      { return std::move(__k); }
-
        template<typename _Arg, typename _NodeGenerator>
  	std::pair<iterator, bool>
  	_M_insert_unique_aux(_Arg&& __arg, _NodeGenerator& __node_gen)
  	{
+	  using _Kt
+	    = __conditional_t<is_same<const _Arg&, const key_type&>::value,
+			      _Arg&&, key_type>;
  	  return _M_insert_unique(
-	    _S_forward_key(_ExtractKey{}(std::forward<_Arg>(__arg))),
-	    std::forward<_Arg>(__arg), __node_gen);
+		   static_cast<_Kt>(_ExtractKey{}(std::forward<_Arg>(__arg))),
+		   std::forward<_Arg>(__arg), __node_gen);
  	}
  
        template<typename _Arg, typename _NodeGenerator>




More information about the Libstdc++ mailing list