Bug 2043767 - const key type in unordered_map leaves mapped_type undefined
Summary: const key type in unordered_map leaves mapped_type undefined
Keywords:
Status: CLOSED RAWHIDE
Alias: None
Product: Fedora
Classification: Fedora
Component: gcc
Version: 36
Hardware: Unspecified
OS: Unspecified
unspecified
low
Target Milestone: ---
Assignee: Jakub Jelinek
QA Contact: Fedora Extras Quality Assurance
URL:
Whiteboard:
: 2045799 (view as bug list)
Depends On:
Blocks: F36FinalBlocker F36FTBFS F37FTBFS
TreeView+ depends on / blocked
 
Reported: 2022-01-21 23:07 UTC by Jerry James
Modified: 2022-03-09 08:55 UTC (History)
13 users (show)

Fixed In Version:
Doc Type: If docs needed, set a value
Doc Text:
Clone Of:
Environment:
Last Closed: 2022-03-09 08:55:16 UTC
Type: Bug
Embargoed:


Attachments (Terms of Use)
Code that triggers the error (1.11 KB, text/x-csrc)
2022-01-21 23:07 UTC, Jerry James
no flags Details


Links
System ID Private Priority Status Summary Last Updated
GNU Compiler Collection 104174 0 P1 RESOLVED [12 Regression] unordered_map<const T, U, H> fails 2022-03-09 00:17:16 UTC

Description Jerry James 2022-01-21 23:07:41 UTC
Created attachment 1852624 [details]
Code that triggers the error

Description of problem:
The libsemigroups package failed the mass rebuild (https://koji.fedoraproject.org/koji/taskinfo?taskID=81537706).  I will attach a small reproducer.  If an unordered_map is declared with a const key type, the compiler complains that mapped_type is undefined.  If the key type is non-const, compilation is successful.

This isn't necessarily a bug, but I don't understand why having a const key type is wrong.  Will you please explain it to me, so I can explain it to libsemigroups upstream?  The attached code compiles successfully with gcc 11, by the way.

Version-Release number of selected component (if applicable):
gcc-12.0.0-0.5.fc36.x86_64

How reproducible:
Always

Steps to Reproduce:
1. g++ -c unordered_map_const_key.cpp (the attachment)

Actual results:
In file included from /usr/include/c++/12/unordered_map:47,
                 from unordered_map_const_key.cpp:3:
/usr/include/c++/12/bits/unordered_map.h: In instantiation of 'class std::unordered_map<const BitSet, long unsigned int, Action<BitSet, ActionTraits<BitSet> >::InternalHash, Action<BitSet, ActionTraits<BitSet> >::InternalEqualTo, std::allocator<std::pair<const BitSet, long unsigned int> > >':
unordered_map_const_key.cpp:33:39:   required from 'class Action<BitSet, ActionTraits<BitSet> >'
unordered_map_const_key.cpp:44:38:   required from here
/usr/include/c++/12/bits/unordered_map.h:113:49: error: no type named 'mapped_type' in 'class std::_Hashtable<const BitSet, std::pair<const BitSet, long unsigned int>, std::allocator<std::pair<const BitSet, long unsigned int> >, std::__detail::_Select1st, Action<BitSet, ActionTraits<BitSet> >::InternalEqualTo, Action<BitSet, ActionTraits<BitSet> >::InternalHash, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, false, true> >'
  113 |       typedef typename _Hashtable::mapped_type  mapped_type;
      |                                                 ^~~~~~~~~~~

Expected results:
Successful compilation, unless the code is actually wrong, of course.

Additional info:

Comment 1 Jonathan Wakely 2022-01-22 00:23:51 UTC
This is caused by https://gcc.gnu.org/g:d87105d697ced10e1f7af3f1f80ef6c9890c8585 and should be fixed. I didn't consider anybody would use unordered_map<const T, U> because that's just dumb and pointless.

It's not wrong, but it hurts performance for zero benefit. For example, these APIa that are supposed to be able to move the key:

      mapped_type&
      operator[](key_type&& __k)
      { return _M_h[std::move(__k)]; }


      template <typename... _Args>
	pair<iterator, bool>
	try_emplace(key_type&& __k, _Args&&... __args)
	{
	  return _M_h.try_emplace(cend(), std::move(__k),
				  std::forward<_Args>(__args)...);
	}


If the key_type is const, it can't be moved.

There is zero benefit to using a const key_type, because it's already stored as const inside the pair anyway, so the only thing it does is prevent move semantics.

Comment 2 Jerry James 2022-01-22 17:41:35 UTC
It turns out that the cvc4 package does this, too.  I will submit PRs to both upstreams to remove the const.  Thank you for the explanation!

Comment 3 Jerry James 2022-01-22 22:48:30 UTC
One more note on this: with both libsemigroups and cvc4, removing the const on the key type leads to compiler errors on every call to map.find().  The upstreams are making those calls from contexts where the available references are const, and both upstreams are still on C++11.  This means that I have to approach both upstreams and say, "Hey, I've got this great patch that removes the const from your unordered_map key types, but you either have to convert your entire code base to C++20, or add a const_cast to the argument of every map.find() call!"  I suspect that isn't going to go over well.

Comment 4 Jonathan Wakely 2022-01-23 21:34:03 UTC
What do those find calls look like? I don't see why you'd get errors for them.

Comment 5 Jonathan Wakely 2022-01-23 21:34:38 UTC
Or why converting anything to C++20 would help.

Comment 6 Jakub Jelinek 2022-01-27 10:33:54 UTC
Please retry against gcc-12.0.1-0.3.fc36 and annobin-10.51-2.fc36 now in rawhide.

Comment 7 Jerry James 2022-01-27 18:02:49 UTC
(In reply to Jonathan Wakely from comment #4)
> What do those find calls look like? I don't see why you'd get errors for
> them.

If I only remove the const from the key type and make no other changes, I get errors like this (this one from libsemigroups):

libtool: compile:  g++ -DHAVE_CONFIG_H -I. -I./config -DNDEBUG -I/builddir/build/BUILD/libsemigroups-1.3.7/include -I/builddir/build/BUILD/libsemigroups-1.3.7/extern -std=gnu++11 -O3 -Wall -Wextra -I/usr/include/eigen3 -O2 -flto=auto -ffat-lto-objects -fexceptions -g -grecord-gcc-switches -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection -fwrapv -c src/knuth-bendix.cpp  -fPIC -DPIC -o src/.libs/libsemigroups_la-knuth-bendix.o
In file included from /builddir/build/BUILD/libsemigroups-1.3.7/include/libsemigroups/froidure-pin.hpp:935,
                 from src/knuth-bendix.cpp:26:
/builddir/build/BUILD/libsemigroups-1.3.7/include/libsemigroups/froidure-pin-impl.hpp: In instantiation of 'libsemigroups::element_index_type libsemigroups::FroidurePin<TElementType, TTraits>::position(const_reference) [with TElementType = libsemigroups::detail::KBE; TTraits = libsemigroups::FroidurePinTraits<libsemigroups::detail::KBE, libsemigroups::fpsemigroup::KnuthBendix>; libsemigroups::element_index_type = long unsigned int; const_reference = const libsemigroups::detail::KBE&]':
src/knuth-bendix.cpp:461:31:   required from here
/builddir/build/BUILD/libsemigroups-1.3.7/include/libsemigroups/froidure-pin-impl.hpp:322:50: error: invalid conversion from 'libsemigroups::detail::BruidhinnTraits<libsemigroups::detail::KBE, void>::internal_const_value_type' {aka 'const libsemigroups::detail::KBE*'} to 'std::unordered_map<libsemigroups::detail::KBE*, long unsigned int, libsemigroups::FroidurePin<libsemigroups::detail::KBE, libsemigroups::FroidurePinTraits<libsemigroups::detail::KBE, libsemigroups::fpsemigroup::KnuthBendix> >::InternalHash, libsemigroups::FroidurePin<libsemigroups::detail::KBE, libsemigroups::FroidurePinTraits<libsemigroups::detail::KBE, libsemigroups::fpsemigroup::KnuthBendix> >::InternalEqualTo, std::allocator<std::pair<libsemigroups::detail::KBE* const, long unsigned int> > >::key_type' {aka 'libsemigroups::detail::KBE*'} [-fpermissive]
  322 |       auto it = _map.find(this->to_internal_const(x));
      |                           ~~~~~~~~~~~~~~~~~~~~~~~^~~
      |                                                  |
      |                                                  libsemigroups::detail::BruidhinnTraits<libsemigroups::detail::KBE, void>::internal_const_value_type {aka const libsemigroups::detail::KBE*}

(In reply to Jonathan Wakely from comment #5)
> Or why converting anything to C++20 would help.

You're right.  It wouldn't.  I was looking at the new templated versions of find() in C++20 and misinterpreted what I saw.

(In reply to Jakub Jelinek from comment #6)
> Please retry against gcc-12.0.1-0.3.fc36 and annobin-10.51-2.fc36 now in
> rawhide.

I get the same error showed above if I just remove const from the key type.  If I keep my grubby fingers off of the code, then it builds successfully.  Thank you for the fix!

Comment 8 Jonathan Wakely 2022-01-27 20:35:03 UTC
(In reply to Jerry James from comment #7)
> (In reply to Jonathan Wakely from comment #4)
> > What do those find calls look like? I don't see why you'd get errors for
> > them.
> 
> If I only remove the const from the key type and make no other changes, I
> get errors like this (this one from libsemigroups):
> 
> libtool: compile:  g++ -DHAVE_CONFIG_H -I. -I./config -DNDEBUG
> -I/builddir/build/BUILD/libsemigroups-1.3.7/include
> -I/builddir/build/BUILD/libsemigroups-1.3.7/extern -std=gnu++11 -O3 -Wall
> -Wextra -I/usr/include/eigen3 -O2 -flto=auto -ffat-lto-objects -fexceptions
> -g -grecord-gcc-switches -pipe -Wall -Werror=format-security
> -Wp,-D_FORTIFY_SOURCE=2 -Wp,-D_GLIBCXX_ASSERTIONS
> -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -fstack-protector-strong
> -specs=/usr/lib/rpm/redhat/redhat-annobin-cc1 -m64 -mtune=generic
> -fasynchronous-unwind-tables -fstack-clash-protection -fcf-protection
> -fwrapv -c src/knuth-bendix.cpp  -fPIC -DPIC -o
> src/.libs/libsemigroups_la-knuth-bendix.o
> In file included from
> /builddir/build/BUILD/libsemigroups-1.3.7/include/libsemigroups/froidure-pin.
> hpp:935,
>                  from src/knuth-bendix.cpp:26:
> /builddir/build/BUILD/libsemigroups-1.3.7/include/libsemigroups/froidure-pin-
> impl.hpp: In instantiation of 'libsemigroups::element_index_type
> libsemigroups::FroidurePin<TElementType, TTraits>::position(const_reference)
> [with TElementType = libsemigroups::detail::KBE; TTraits =
> libsemigroups::FroidurePinTraits<libsemigroups::detail::KBE,
> libsemigroups::fpsemigroup::KnuthBendix>; libsemigroups::element_index_type
> = long unsigned int; const_reference = const libsemigroups::detail::KBE&]':
> src/knuth-bendix.cpp:461:31:   required from here
> /builddir/build/BUILD/libsemigroups-1.3.7/include/libsemigroups/froidure-pin-
> impl.hpp:322:50: error: invalid conversion from
> 'libsemigroups::detail::BruidhinnTraits<libsemigroups::detail::KBE,
> void>::internal_const_value_type' {aka 'const libsemigroups::detail::KBE*'}
> to 'std::unordered_map<libsemigroups::detail::KBE*, long unsigned int,
> libsemigroups::FroidurePin<libsemigroups::detail::KBE,
> libsemigroups::FroidurePinTraits<libsemigroups::detail::KBE,
> libsemigroups::fpsemigroup::KnuthBendix> >::InternalHash,
> libsemigroups::FroidurePin<libsemigroups::detail::KBE,
> libsemigroups::FroidurePinTraits<libsemigroups::detail::KBE,
> libsemigroups::fpsemigroup::KnuthBendix> >::InternalEqualTo,
> std::allocator<std::pair<libsemigroups::detail::KBE* const, long unsigned
> int> > >::key_type' {aka 'libsemigroups::detail::KBE*'} [-fpermissive]
>   322 |       auto it = _map.find(this->to_internal_const(x));
>       |                           ~~~~~~~~~~~~~~~~~~~~~~~^~~
>       |                                                  |
>       |                                                 
> libsemigroups::detail::BruidhinnTraits<libsemigroups::detail::KBE,
> void>::internal_const_value_type {aka const libsemigroups::detail::KBE*}

This looks like something is doing:

using Map = unordered_map<const A, B>;
Map m;
Map::key_type& k = m.begin().first;

i.e. expecting the type of the key to be key_type, which is wrong. It's const key_type.
Which is why using unordered_map<const A, B> is dumb in the first place: the objects in the map are const anyway.

Comment 9 Jonathan Wakely 2022-01-27 20:43:46 UTC
Ah, no, it's trying to use const T* as the key to lookup in an unordered_map<T*, U>.

Are you sure you've changed unordered_map<const T, U> to unordered_map<T, U>, rather than changing unordered_map<const T*, U> to unordered_map<T*, U>?

Comment 10 Florian Weimer 2022-01-30 21:29:15 UTC
*** Bug 2045799 has been marked as a duplicate of this bug. ***

Comment 11 Ben Cotton 2022-02-08 20:24:55 UTC
This bug appears to have been reported against 'rawhide' during the Fedora 36 development cycle.
Changing version to 36.

Comment 12 Raphael Groner 2022-03-08 21:28:27 UTC
My proposal as blocker due to gcc is a critical component. We'd should not promise something as new feature aka Change that we can't deliver.

Comment 13 Jonathan Wakely 2022-03-09 00:17:48 UTC
(In reply to Raphael Groner from comment #12)
> My proposal as blocker due to gcc is a critical component. We'd should not
> promise something as new feature aka Change that we can't deliver.

I'm not sure what you mean, but the GCC bug was fixed in January.

Comment 14 Raphael Groner 2022-03-09 08:43:29 UTC
why this bug has still status new?

Comment 15 Jakub Jelinek 2022-03-09 08:55:16 UTC
Because nobody confirmed that all the problems are fixed with it.
Anyway, will close now, please reopen if you have other issues.


Note You need to log in before you can comment on or make changes to this bug.