blob: f0a06a8c1a281834ec0fd7e312b5ce765357fedd [file] [log] [blame]
// { dg-do run { target c++17 } }
// PR libstdc++/114401 allocator destructor omitted when reinserting node_handle
#include <set>
#include <memory>
#include <testsuite_hooks.h>
template<typename T>
struct Alloc
{
using value_type = T;
using propagate_on_container_copy_assignment = std::true_type;
using propagate_on_container_move_assignment = std::true_type;
using propagate_on_container_swap = std::true_type;
Alloc(int identity) : id(std::make_shared<int>(identity)) { }
template<typename U>
Alloc(const Alloc<U> a) : id(a.id) { }
T* allocate(std::size_t n) { return std::allocator<T>().allocate(n); }
void deallocate(T* p, std::size_t n) { std::allocator<T>().deallocate(p, n); }
template<typename U>
friend bool
operator==(const Alloc& a, const Alloc<U>& a2)
{ return a.id == a2.id; }
template<typename U>
friend bool
operator!=(const Alloc& a, const Alloc<U>& a2)
{ return !(a == a2); }
std::shared_ptr<int> id;
};
using test_type = std::set<int, std::less<int>, Alloc<int>>;
void
test_node_ops()
{
test_type s1({1,3,5}, Alloc<int>(1));
test_type s2({2,4,6,8}, Alloc<int>(2));
VERIFY( s1.get_allocator() != s2.get_allocator() );
auto node_a = s1.extract(1);
VERIFY( ! node_a.empty() );
VERIFY( node_a.get_allocator() == s1.get_allocator() );
node_a = std::move(node_a); // self-move
VERIFY( node_a.empty() );
swap(node_a, node_a); // self-swap
VERIFY( node_a.empty() );
auto node_b = s2.extract(2);
VERIFY( node_b.get_allocator() == s2.get_allocator() );
node_a = std::move(node_b); // empty = !empty
VERIFY( node_a.get_allocator() == s2.get_allocator() );
VERIFY( node_b.empty() );
swap(node_a, node_b); // swap(!empty, empty)
VERIFY( node_a.empty() );
VERIFY( node_b.get_allocator() == s2.get_allocator() );
swap(node_a, node_b); // swap(empty, !empty)
VERIFY( node_a.get_allocator() == s2.get_allocator() );
VERIFY( node_b.empty() );
node_a = s1.extract(3); // !empty = !empty
VERIFY( node_a.get_allocator() == s1.get_allocator() );
node_b = s2.extract(0); // empty = empty
VERIFY( node_b.empty() );
node_b = s2.extract(6); // empty = !empty
VERIFY( node_b.get_allocator() == s2.get_allocator() );
swap(node_a, node_b); // swap(!empty, !empty)
VERIFY( node_a.get_allocator() == s2.get_allocator() );
VERIFY( node_b.get_allocator() == s1.get_allocator() );
node_a = {};
node_b = std::move(node_a); // !empty = empty
VERIFY( node_a.empty() );
VERIFY( node_b.empty() );
swap(node_a, node_b); // swap(empty, empty)
VERIFY( node_a.empty() );
VERIFY( node_b.empty() );
}
void
test_alloc_lifetime()
{
Alloc<int> a(1);
test_type s({1,2,3}, a);
VERIFY( a.id.use_count() == 2 ); // a and the copy in s
s.insert(s.extract(1));
VERIFY( a.id.use_count() == 2 );
s.insert(s.begin(), s.extract(2));
VERIFY( a.id.use_count() == 2 );
auto node = s.extract(1);
VERIFY( a.id.use_count() == 3 );
node = s.extract(0);
VERIFY( a.id.use_count() == 2 );
s.insert(std::move(node));
VERIFY( a.id.use_count() == 2 );
s.merge(test_type(s));
VERIFY( a.id.use_count() == 2 );
s.merge(test_type({4,5,6}, a));
VERIFY( a.id.use_count() == 2 );
}
int main()
{
test_node_ops();
test_alloc_lifetime();
}