[Bug libstdc++/56112] [4.8 Regression] cannot create unordered_map from range of types convertible to value_type

redi at gcc dot gnu.org gcc-bugzilla@gcc.gnu.org
Sun Jan 27 16:25:00 GMT 2013


http://gcc.gnu.org/bugzilla/show_bug.cgi?id=56112

--- Comment #6 from Jonathan Wakely <redi at gcc dot gnu.org> 2013-01-27 16:25:36 UTC ---
(In reply to comment #5)
> Uhhmm, I wasn't considering the fact that the testcases added for 53339 are now
> xfailed?!?

That was me: http://gcc.gnu.org/ml/libstdc++/2013-01/msg00035.html


> So what? If that's the case (for now at least) we could as well
> revert the core of the 53339 changes, that is go back to the 4.7 situation and
> just use std::_Select1st<value_type>?!?

Yes, I suppose we could, but we'd need to revert the changes to stl_function.h
so that it doesn't derive from unary_function in C++11 mode.  I think having a
simple __detail::_Select1st<_Pair> that just does what the hash tables need is
cleaner, and it can be made simpler than your patch because it doesn't need to
use perfect forwarding:

  template<typename _Pair>
    struct _Select1st
    {
      auto
      operator()(const _Pair& __x) const
      -> decltype(std::get<0>(__x))
      { return std::get<0>(__x); }
    };

Or we could simplify it even further

      const typename _Pair::first_type&
      operator()(const _Pair& __x) const
      { return __x.first); }

This would prevent fancy custom uses of _Hashtable, for example
_Hashtable<int, std::tuple<int, int, int>, ...>, but I don't think we need or
want to do that anyway.

My testcase for this bug reveals another problem in an area we've had recurring
problems:

      _Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
         _H1, _H2, _Hash, _RehashPolicy, _Traits>::
      _M_insert(_Arg&& __v, std::true_type)
      {
    const key_type& __k = this->_M_extract()(__v);
    __hash_code __code = this->_M_hash_code(__k);
    size_type __bkt = _M_bucket_index(__k, __code);

    __node_type* __n = _M_find_node(__bkt, __k, __code);

With my testcase __k is a dangling reference, because the call to _M_extract
creates a temporary, __k binds to a member of that temporary, then the
temporary goes out of scope.

Valgrind will show errors for this modified testcase which allocates memory in
the temporary:

#include <unordered_map>
#include <string>

struct S
{
    operator std::pair<const std::string, int>() const { return {"a", 0}; }
};

int main()
{
    S s[1];
    std::unordered_map<std::string, int> m(s, s+1);   // error
}


To fix this we need to extract the key and set __code, __bkt and __n in a
single statement before the temporary goes out of scope, e.g. using a lambda
just for a proof of concept:

    __hash_code __code;
    size_type __bkt;
    __node_type* __n;
    auto __find_node = [&](const key_type& __k) {
      __code = this->_M_hash_code(__k);
      __bkt = _M_bucket_index(__k, __code);
      __n = _M_find_node(__bkt, __k, __code);
    };
    __find_node(this->_M_extract()(value_type(__v)));

So I think we want something like your comment 4 attachment, with a simpler
_Select1st, and fixing the memory bug.  I'm testing that now ...



More information about the Gcc-bugs mailing list