From ee030b28004eade3da872e7ae62a526a2940a705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Dumont?= <frs.dumont@gmail.com> Date: Tue, 22 Oct 2024 19:13:34 +0200 Subject: [PATCH] libstdc++: Always instantiate key_type to compute hash code [PR115285] Even if it is possible to compute a hash code from the inserted arguments we need to instantiate the key_type to guaranty hash code consistency. Preserve the lazy instantiation of the mapped_type in the context of associative containers. libstdc++-v3/ChangeLog: PR libstdc++/115285 * include/bits/hashtable.h (_S_forward_key<_Kt>): Always return a temporary key_type instance. * testsuite/23_containers/unordered_map/96088.cc: Adapt to additional instanciation. Also check that mapped_type is not instantiated when there is no insertion. * testsuite/23_containers/unordered_multimap/96088.cc: Adapt to additional instanciation. * testsuite/23_containers/unordered_multiset/96088.cc: Likewise. * testsuite/23_containers/unordered_set/96088.cc: Likewise. * testsuite/23_containers/unordered_set/pr115285.cc: New test case. --- libstdc++-v3/include/bits/hashtable.h | 5 +- .../23_containers/unordered_map/96088.cc | 86 +++++++++---------- .../23_containers/unordered_multimap/96088.cc | 17 ++-- .../23_containers/unordered_multiset/96088.cc | 5 +- .../23_containers/unordered_set/96088.cc | 15 ++-- .../23_containers/unordered_set/pr115285.cc | 40 +++++++++ 6 files changed, 101 insertions(+), 67 deletions(-) create mode 100644 libstdc++-v3/testsuite/23_containers/unordered_set/pr115285.cc diff --git a/libstdc++-v3/include/bits/hashtable.h b/libstdc++-v3/include/bits/hashtable.h index b4e8e4d3fb25..efc155bdfa4b 100644 --- a/libstdc++-v3/include/bits/hashtable.h +++ b/libstdc++-v3/include/bits/hashtable.h @@ -930,10 +930,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION _M_insert_unique(_Kt&&, _Arg&&, _NodeGenerator&); template<typename _Kt> - static __conditional_t< - __and_<__is_nothrow_invocable<_Hash&, const key_type&>, - __not_<__is_nothrow_invocable<_Hash&, _Kt>>>::value, - key_type, _Kt&&> + key_type _S_forward_key(_Kt&& __k) { return std::forward<_Kt>(__k); } diff --git a/libstdc++-v3/testsuite/23_containers/unordered_map/96088.cc b/libstdc++-v3/testsuite/23_containers/unordered_map/96088.cc index 2065caab6c5c..b5be7d06aa03 100644 --- a/libstdc++-v3/testsuite/23_containers/unordered_map/96088.cc +++ b/libstdc++-v3/testsuite/23_containers/unordered_map/96088.cc @@ -29,46 +29,45 @@ #include <testsuite_hooks.h> #include <replacement_memory_operators.h> -static constexpr std::initializer_list<std::pair<const char*, int>> lst = { - {"long_str_for_dynamic_allocating", 1} -}; +static constexpr std::initializer_list<std::pair<const char*, const char*>> lst = + { { "long_str_for_dynamic_allocation", "long_str_for_dynamic_allocation" } }; void test01() { __gnu_test::counter::reset(); - std::unordered_map<std::string, int> um; + std::unordered_map<std::string, std::string> um; um.insert(lst.begin(), lst.end()); VERIFY( um.size() == 1 ); - VERIFY( __gnu_test::counter::count() == 3 ); - VERIFY( __gnu_test::counter::get()._M_increments == 3 ); + VERIFY( __gnu_test::counter::count() == 4 ); + VERIFY( __gnu_test::counter::get()._M_increments == 4 ); um.insert(lst.begin(), lst.end()); VERIFY( um.size() == 1 ); - VERIFY( __gnu_test::counter::count() == 3 ); - VERIFY( __gnu_test::counter::get()._M_increments == 4 ); + VERIFY( __gnu_test::counter::count() == 4 ); + VERIFY( __gnu_test::counter::get()._M_increments == 5 ); } void test02() { __gnu_test::counter::reset(); - std::unordered_map<std::string, int, + std::unordered_map<std::string, std::string, std::hash<std::string_view>, std::equal_to<std::string_view>> um; um.insert(lst.begin(), lst.end()); VERIFY( um.size() == 1 ); - VERIFY( __gnu_test::counter::count() == 3 ); - VERIFY( __gnu_test::counter::get()._M_increments == 3 ); + VERIFY( __gnu_test::counter::count() == 4 ); + VERIFY( __gnu_test::counter::get()._M_increments == 4 ); um.insert(lst.begin(), lst.end()); VERIFY( um.size() == 1 ); - VERIFY( __gnu_test::counter::count() == 3 ); - VERIFY( __gnu_test::counter::get()._M_increments == 3 ); + VERIFY( __gnu_test::counter::count() == 4 ); + VERIFY( __gnu_test::counter::get()._M_increments == 5 ); } std::size_t @@ -84,20 +83,20 @@ test11() typedef std::size_t (*hash_string_t)(const std::string&) noexcept; __gnu_test::counter::reset(); hash_string_t hasher = &hash_string_f; - std::unordered_map<std::string, int, + std::unordered_map<std::string, std::string, hash_string_t, std::equal_to<std::string>> um(0, hasher); um.insert(lst.begin(), lst.end()); VERIFY( um.size() == 1 ); - VERIFY( __gnu_test::counter::count() == 3 ); - VERIFY( __gnu_test::counter::get()._M_increments == 3 ); + VERIFY( __gnu_test::counter::count() == 4 ); + VERIFY( __gnu_test::counter::get()._M_increments == 4 ); um.insert(lst.begin(), lst.end()); VERIFY( um.size() == 1 ); - VERIFY( __gnu_test::counter::count() == 3 ); - VERIFY( __gnu_test::counter::get()._M_increments == 4 ); + VERIFY( __gnu_test::counter::count() == 4 ); + VERIFY( __gnu_test::counter::get()._M_increments == 5 ); } std::size_t @@ -113,19 +112,19 @@ test12() typedef std::size_t (*hash_stringview_t) (const std::string_view&) noexcept; __gnu_test::counter::reset(); hash_stringview_t hasher = &hash_string_view_f; - std::unordered_map<std::string, int, hash_stringview_t, + std::unordered_map<std::string, std::string, hash_stringview_t, std::equal_to<std::string_view>> um(0, hasher); um.insert(lst.begin(), lst.end()); VERIFY( um.size() == 1 ); - VERIFY( __gnu_test::counter::count() == 3 ); - VERIFY( __gnu_test::counter::get()._M_increments == 3 ); + VERIFY( __gnu_test::counter::count() == 4 ); + VERIFY( __gnu_test::counter::get()._M_increments == 4 ); um.insert(lst.begin(), lst.end()); VERIFY( um.size() == 1 ); - VERIFY( __gnu_test::counter::count() == 3 ); - VERIFY( __gnu_test::counter::get()._M_increments == 3 ); + VERIFY( __gnu_test::counter::count() == 4 ); + VERIFY( __gnu_test::counter::get()._M_increments == 5 ); } struct hash_string_functor @@ -142,20 +141,20 @@ void test21() { __gnu_test::counter::reset(); - std::unordered_map<std::string, int, + std::unordered_map<std::string, std::string, hash_string_functor, std::equal_to<std::string>> um; um.insert(lst.begin(), lst.end()); VERIFY( um.size() == 1 ); - VERIFY( __gnu_test::counter::count() == 3 ); - VERIFY( __gnu_test::counter::get()._M_increments == 3 ); + VERIFY( __gnu_test::counter::count() == 4 ); + VERIFY( __gnu_test::counter::get()._M_increments == 4 ); um.insert(lst.begin(), lst.end()); VERIFY( um.size() == 1 ); - VERIFY( __gnu_test::counter::count() == 3 ); - VERIFY( __gnu_test::counter::get()._M_increments == 4 ); + VERIFY( __gnu_test::counter::count() == 4 ); + VERIFY( __gnu_test::counter::get()._M_increments == 5 ); } struct hash_string_view_noexcept_functor @@ -172,20 +171,20 @@ void test22() { __gnu_test::counter::reset(); - std::unordered_map<std::string, int, + std::unordered_map<std::string, std::string, hash_string_view_noexcept_functor, std::equal_to<std::string_view>> um; um.insert(lst.begin(), lst.end()); VERIFY( um.size() == 1 ); - VERIFY( __gnu_test::counter::count() == 3 ); - VERIFY( __gnu_test::counter::get()._M_increments == 3 ); + VERIFY( __gnu_test::counter::count() == 4 ); + VERIFY( __gnu_test::counter::get()._M_increments == 4 ); um.insert(lst.begin(), lst.end()); VERIFY( um.size() == 1 ); - VERIFY( __gnu_test::counter::count() == 3 ); - VERIFY( __gnu_test::counter::get()._M_increments == 3 ); + VERIFY( __gnu_test::counter::count() == 4 ); + VERIFY( __gnu_test::counter::get()._M_increments == 5 ); } struct hash_string_view_functor @@ -202,40 +201,41 @@ void test23() { __gnu_test::counter::reset(); - std::unordered_map<std::string, int, + std::unordered_map<std::string, std::string, hash_string_view_functor, std::equal_to<std::string_view>> um; um.insert(lst.begin(), lst.end()); VERIFY( um.size() == 1 ); - VERIFY( __gnu_test::counter::count() == 3 ); - VERIFY( __gnu_test::counter::get()._M_increments == 3 ); + VERIFY( __gnu_test::counter::count() == 4 ); + VERIFY( __gnu_test::counter::get()._M_increments == 4 ); um.insert(lst.begin(), lst.end()); VERIFY( um.size() == 1 ); - VERIFY( __gnu_test::counter::count() == 3 ); - VERIFY( __gnu_test::counter::get()._M_increments == 3 ); + VERIFY( __gnu_test::counter::count() == 4 ); + VERIFY( __gnu_test::counter::get()._M_increments == 5 ); } void test03() { - std::vector<std::pair<std::string, int>> v; + std::vector<std::pair<std::string, std::string>> v; v.insert(v.end(), lst.begin(), lst.end()); const auto origin = __gnu_test::counter::count(); { __gnu_test::counter::reset(); - std::unordered_map<std::string, int, + std::unordered_map<std::string, std::string, std::hash<std::string_view>, std::equal_to<std::string_view>> um; um.insert(v.begin(), v.end()); VERIFY( um.size() == 1 ); - // Allocate array of buckets, a node, and the std::string (unless COW). - constexpr std::size_t increments = _GLIBCXX_USE_CXX11_ABI ? 3 : 2; + // Allocate array of buckets, a node, the std::string value and the + // std::string key (unless COW). + constexpr std::size_t increments = _GLIBCXX_USE_CXX11_ABI ? 4 : 3; VERIFY( __gnu_test::counter::count() == origin + increments ); VERIFY( __gnu_test::counter::get()._M_increments == increments ); @@ -250,7 +250,7 @@ test03() { __gnu_test::counter::reset(); - std::unordered_map<std::string, int, + std::unordered_map<std::string, std::string, std::hash<std::string_view>, std::equal_to<std::string_view>> um; um.insert(std::make_move_iterator(v.begin()), diff --git a/libstdc++-v3/testsuite/23_containers/unordered_multimap/96088.cc b/libstdc++-v3/testsuite/23_containers/unordered_multimap/96088.cc index b1b33708cde8..562ec76e6974 100644 --- a/libstdc++-v3/testsuite/23_containers/unordered_multimap/96088.cc +++ b/libstdc++-v3/testsuite/23_containers/unordered_multimap/96088.cc @@ -28,34 +28,33 @@ #include <testsuite_hooks.h> #include <replacement_memory_operators.h> -static constexpr std::initializer_list<std::pair<const char*, int>> lst = { - {"long_str_for_dynamic_allocating", 1} -}; +static constexpr std::initializer_list<std::pair<const char*, const char*>> lst = + { { "long_str_for_dynamic_allocation", "long_str_for_dynamic_allocation" } }; void test01() { __gnu_test::counter::reset(); - std::unordered_multimap<std::string, int, + std::unordered_multimap<std::string, std::string, std::hash<std::string_view>, std::equal_to<std::string_view>> foo; foo.insert(lst.begin(), lst.end()); VERIFY( foo.size() == 1 ); - VERIFY( __gnu_test::counter::count() == 3 ); - VERIFY( __gnu_test::counter::get()._M_increments == 3 ); + VERIFY( __gnu_test::counter::count() == 4 ); + VERIFY( __gnu_test::counter::get()._M_increments == 4 ); } void test02() { __gnu_test::counter::reset(); - std::unordered_multimap<std::string, int> foo; + std::unordered_multimap<std::string, std::string> foo; foo.insert(lst.begin(), lst.end()); VERIFY( foo.size() == 1 ); - VERIFY( __gnu_test::counter::count() == 3 ); - VERIFY( __gnu_test::counter::get()._M_increments == 3 ); + VERIFY( __gnu_test::counter::count() == 4 ); + VERIFY( __gnu_test::counter::get()._M_increments == 4 ); } int diff --git a/libstdc++-v3/testsuite/23_containers/unordered_multiset/96088.cc b/libstdc++-v3/testsuite/23_containers/unordered_multiset/96088.cc index d525bd532beb..1efd5be7f104 100644 --- a/libstdc++-v3/testsuite/23_containers/unordered_multiset/96088.cc +++ b/libstdc++-v3/testsuite/23_containers/unordered_multiset/96088.cc @@ -28,9 +28,8 @@ #include <testsuite_hooks.h> #include <replacement_memory_operators.h> -static constexpr std::initializer_list<const char*> lst = { - "long_str_for_dynamic_allocating" -}; +static constexpr std::initializer_list<const char*> lst = + { "long_str_for_dynamic_allocation" }; void test01() diff --git a/libstdc++-v3/testsuite/23_containers/unordered_set/96088.cc b/libstdc++-v3/testsuite/23_containers/unordered_set/96088.cc index f9ef94eac531..bc2f093f47c3 100644 --- a/libstdc++-v3/testsuite/23_containers/unordered_set/96088.cc +++ b/libstdc++-v3/testsuite/23_containers/unordered_set/96088.cc @@ -29,9 +29,8 @@ #include <testsuite_hooks.h> #include <replacement_memory_operators.h> -static constexpr std::initializer_list<const char*> lst = { - "long_str_for_dynamic_allocating" -}; +static constexpr std::initializer_list<const char*> lst = + { "long_str_for_dynamic_allocation" }; void test01() @@ -68,7 +67,7 @@ test02() VERIFY( us.size() == 1 ); VERIFY( __gnu_test::counter::count() == 3 ); - VERIFY( __gnu_test::counter::get()._M_increments == 3 ); + VERIFY( __gnu_test::counter::get()._M_increments == 4 ); } std::size_t @@ -126,7 +125,7 @@ test12() VERIFY( us.size() == 1 ); VERIFY( __gnu_test::counter::count() == 3 ); - VERIFY( __gnu_test::counter::get()._M_increments == 3 ); + VERIFY( __gnu_test::counter::get()._M_increments == 4 ); } struct hash_string_functor @@ -186,7 +185,7 @@ test22() VERIFY( us.size() == 1 ); VERIFY( __gnu_test::counter::count() == 3 ); - VERIFY( __gnu_test::counter::get()._M_increments == 3 ); + VERIFY( __gnu_test::counter::get()._M_increments == 4 ); } struct hash_string_view_functor @@ -216,7 +215,7 @@ test23() VERIFY( us.size() == 1 ); VERIFY( __gnu_test::counter::count() == 3 ); - VERIFY( __gnu_test::counter::get()._M_increments == 3 ); + VERIFY( __gnu_test::counter::get()._M_increments == 4 ); } void @@ -245,7 +244,7 @@ test03() VERIFY( us.size() == 1 ); VERIFY( __gnu_test::counter::count() == origin + increments ); - VERIFY( __gnu_test::counter::get()._M_increments == increments ); + VERIFY( __gnu_test::counter::get()._M_increments == increments + 1 ); } VERIFY( __gnu_test::counter::count() == origin ); diff --git a/libstdc++-v3/testsuite/23_containers/unordered_set/pr115285.cc b/libstdc++-v3/testsuite/23_containers/unordered_set/pr115285.cc new file mode 100644 index 000000000000..6c5cc24930ce --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/unordered_set/pr115285.cc @@ -0,0 +1,40 @@ +// { dg-do run { target c++11 } } + +// libstdc++/115285 + +#include <string> +#include <unordered_set> + +#include <testsuite_hooks.h> + +class TrimmedStr : public std::string +{ + static std::string trim_str(std::string const &str) + { + auto start = str.find_first_not_of(" \r\n\t"); + + return start == std::string::npos + ? str + : str.substr(start, str.find_last_not_of(" \r\n\t") - start + 1); + } + +public: + TrimmedStr(std::string const &arg) + : std::string{trim_str(arg)} {} + TrimmedStr(char const *arg) + : TrimmedStr{std::string{arg}} {} +}; + +int main() +{ + std::unordered_set<TrimmedStr, std::hash<std::string>, std::equal_to<std::string>> + set_from_initializer_list{ "foo", "bar", " foo ", " bar " }; + + VERIFY( set_from_initializer_list.size() == 2 ); + + std::vector<std::string> args{ "foo", "bar", " foo ", " bar " }; + std::unordered_set<TrimmedStr, std::hash<std::string>, std::equal_to<std::string>> + set_from_iterators; + set_from_iterators.insert(args.begin(), args.end()); + VERIFY( set_from_iterators.size() == 2 ); +} -- GitLab