From 83302ea6bc43bbc01a672e0ba9f18a8f8a0bebe8 Mon Sep 17 00:00:00 2001 From: Nikolas Klauser Date: Thu, 13 Nov 2025 12:19:25 +0100 Subject: [PATCH] [libc++] Fix __hash_table::erase(iterator, iterator) to update the bucket list correctly when erasing the last bucket --- libcxx/include/__hash_table | 2 ++ .../unord.map.modifiers/erase_range.pass.cpp | 22 +++++++++++++++++++ .../erase_range.pass.cpp | 22 +++++++++++++++++++ .../unord/unord.multiset/erase_range.pass.cpp | 22 +++++++++++++++++++ .../unord/unord.set/erase_range.pass.cpp | 22 +++++++++++++++++++ 5 files changed, 90 insertions(+) diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table index e1897949a47e6..ef487fb06dd5e 100644 --- a/libcxx/include/__hash_table +++ b/libcxx/include/__hash_table @@ -1910,6 +1910,8 @@ __hash_table<_Tp, _Hash, _Equal, _Alloc>::erase(const_iterator __first, const_it __bucket_list_[__next_chash] = __before_first; __chash = __next_chash; } + } else { // When __next is a nullptr we've fully erased the last bucket. Update the bucket list accordingly. + __bucket_list_[__chash] = nullptr; } } diff --git a/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/erase_range.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/erase_range.pass.cpp index 532413437f6be..81371638143c9 100644 --- a/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/erase_range.pass.cpp +++ b/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/erase_range.pass.cpp @@ -57,6 +57,28 @@ int main(int, char**) { assert(c.size() == 0); assert(k == c.end()); } + { // Make sure that we're properly updating the bucket list when we're erasing to the end + std::unordered_map m; + m.insert(std::make_pair(1, 1)); + m.insert(std::make_pair(2, 2)); + + { + auto pair = m.equal_range(1); + assert(pair.first != pair.second); + m.erase(pair.first, pair.second); + } + + { + auto pair = m.equal_range(2); + assert(pair.first != pair.second); + m.erase(pair.first, pair.second); + } + + m.insert(std::make_pair(3, 3)); + assert(m.size() == 1); + assert(*m.begin() == std::make_pair(3, 3)); + assert(++m.begin() == m.end()); + } #if TEST_STD_VER >= 11 { typedef std::unordered_map m; + m.insert(std::make_pair(1, 1)); + m.insert(std::make_pair(2, 2)); + + { + auto pair = m.equal_range(1); + assert(pair.first != pair.second); + m.erase(pair.first, pair.second); + } + + { + auto pair = m.equal_range(2); + assert(pair.first != pair.second); + m.erase(pair.first, pair.second); + } + + m.insert(std::make_pair(3, 3)); + assert(m.size() == 1); + assert(*m.begin() == std::make_pair(3, 3)); + assert(++m.begin() == m.end()); + } #if TEST_STD_VER >= 11 { typedef std::unordered_multimap m; + m.insert(1); + m.insert(2); + + { + auto pair = m.equal_range(1); + assert(pair.first != pair.second); + m.erase(pair.first, pair.second); + } + + { + auto pair = m.equal_range(2); + assert(pair.first != pair.second); + m.erase(pair.first, pair.second); + } + + m.insert(3); + assert(m.size() == 1); + assert(*m.begin() == 3); + assert(++m.begin() == m.end()); + } #if TEST_STD_VER >= 11 { typedef std::unordered_multiset, std::equal_to, min_allocator> C; diff --git a/libcxx/test/std/containers/unord/unord.set/erase_range.pass.cpp b/libcxx/test/std/containers/unord/unord.set/erase_range.pass.cpp index 5fa6e4199f756..1f049a295b8c3 100644 --- a/libcxx/test/std/containers/unord/unord.set/erase_range.pass.cpp +++ b/libcxx/test/std/containers/unord/unord.set/erase_range.pass.cpp @@ -47,6 +47,28 @@ int main(int, char**) { assert(c.size() == 0); assert(k == c.end()); } + { // Make sure that we're properly updating the bucket list when we're erasing to the end + std::unordered_set m; + m.insert(1); + m.insert(2); + + { + auto pair = m.equal_range(1); + assert(pair.first != pair.second); + m.erase(pair.first, pair.second); + } + + { + auto pair = m.equal_range(2); + assert(pair.first != pair.second); + m.erase(pair.first, pair.second); + } + + m.insert(3); + assert(m.size() == 1); + assert(*m.begin() == 3); + assert(++m.begin() == m.end()); + } #if TEST_STD_VER >= 11 { typedef std::unordered_set, std::equal_to, min_allocator> C;