https://gcc.gnu.org/bugzilla/show_bug.cgi?id=88199
Bug ID: 88199 Summary: [7/8/9 Regression] memory leak on unordered container move assignment Product: gcc Version: unknown Status: UNCONFIRMED Severity: normal Priority: P3 Component: libstdc++ Assignee: unassigned at gcc dot gnu.org Reporter: redi at gcc dot gnu.org Target Milestone: --- Since allocator propagation was implemented in GCC 4.9, move assignment with non-equal allocators has leaked the original buckets in the LHS of the assignment: #include <unordered_set> template<typename T> struct Alloc : std::allocator<T> { Alloc(int i) : id(i) { } template<typename U> Alloc(const Alloc<U>& a) : id(a.id) { } template<typename U> struct rebind { using other = Alloc<U>; }; using propagate_on_container_move_assignment = std::false_type; using is_always_equal = std::false_type; int id; }; template<typename T, typename U> bool operator==(const Alloc<T>& lhs, const Alloc<U>& rhs) { return lhs.id == rhs.id; } template<typename T, typename U> bool operator!=(const Alloc<T>& lhs, const Alloc<U>& rhs) { return lhs.id != rhs.id; } int main() { Alloc<int> a1(1), a2(2); std::hash<int> h; std::equal_to<int> eq; using Set = std::unordered_set<int, std::hash<int>, std::equal_to<int>, Alloc<int>>; Set s1(1, h, eq, a1); Set s2(5, h, eq, a2); s1.insert(0); s2.insert(0); s1 = std::move(s2); } ================================================================= ==30693==ERROR: LeakSanitizer: detected memory leaks Direct leak of 16 byte(s) in 1 object(s) allocated from: #0 0x7fa9c95207f0 in operator new(unsigned long) /home/jwakely/src/gcc/gcc/libsanitizer/asan/asan_new_delete.cc:104 #1 0x405b16 in __gnu_cxx::new_allocator<std::__detail::_Hash_node_base*>::allocate(unsigned long, void const*) /home/jwakely/gcc/9/include/c++/9.0.0/ext/new_allocator.h:114 #2 0x405402 in std::allocator_traits<Alloc<std::__detail::_Hash_node_base*> >::allocate(Alloc<std::__detail::_Hash_node_base*>&, unsigned long) /home/jwakely/gcc/9/include/c++/9.0.0/bits/alloc_traits.h:306 #3 0x4047f8 in std::__detail::_Hashtable_alloc<Alloc<std::__detail::_Hash_node<int, false> > >::_M_allocate_buckets(unsigned long) /home/jwakely/gcc/9/include/c++/9.0.0/bits/hashtable_policy.h:2114 #4 0x4031a9 in std::_Hashtable<int, int, Alloc<int>, std::__detail::_Identity, std::equal_to<int>, std::hash<int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, true, true> >::_M_allocate_buckets(unsigned long) /home/jwakely/gcc/9/include/c++/9.0.0/bits/hashtable.h:366 #5 0x4027a7 in std::_Hashtable<int, int, Alloc<int>, std::__detail::_Identity, std::equal_to<int>, std::hash<int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, true, true> >::_Hashtable(unsigned long, std::hash<int> const&, std::__detail::_Mod_range_hashing const&, std::__detail::_Default_ranged_hash const&, std::equal_to<int> const&, std::__detail::_Identity const&, Alloc<int> const&) /home/jwakely/gcc/9/include/c++/9.0.0/bits/hashtable.h:961 #6 0x401b35 in std::_Hashtable<int, int, Alloc<int>, std::__detail::_Identity, std::equal_to<int>, std::hash<int>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<false, true, true> >::_Hashtable(unsigned long, std::hash<int> const&, std::equal_to<int> const&, Alloc<int> const&) /home/jwakely/gcc/9/include/c++/9.0.0/bits/hashtable.h:446 #7 0x401836 in std::unordered_set<int, std::hash<int>, std::equal_to<int>, Alloc<int> >::unordered_set(unsigned long, std::hash<int> const&, std::equal_to<int> const&, Alloc<int> const&) /home/jwakely/gcc/9/include/c++/9.0.0/bits/unordered_set.h:149 #8 0x401219 in main /tmp/ht.cc:34 #9 0x7fa9c87a5fe9 in __libc_start_main ../csu/libc-start.c:308 SUMMARY: AddressSanitizer: 16 byte(s) leaked in 1 allocation(s).