https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86189

            Bug ID: 86189
           Summary: not equal allocators not behaving as expected
           Product: gcc
           Version: 8.1.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: libstdc++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: rianquinn at gmail dot com
  Target Milestone: ---

I think this is a bug, but I am dealing with allocators so I am not 100% sure
here. I have created a really simple allocator for testing. The allocators are
NOT equal, and I have instructed the containers to propagate the allocator
using the propagate_on_container_copy_assignment alias set to true. Propagation
occurs, but during the destruction of the containers, the old allocator is
somehow being used. Whats worse is the newly allocated memory that was created
using one allocator, is deallocated using another, even though they are not
equal. Seems like a bug. 

Here is the allocator I have created:
template<typename T>
class myallocator4
{
public:

    using value_type = T;
    using pointer = T *;
    using size_type = std::size_t;
    using propagate_on_container_copy_assignment = std::true_type;
    using is_always_equal = std::false_type;

public:

    myallocator4() = default;

    template <typename U>
    myallocator4(const myallocator4<U> &other) noexcept
    { (void) other; }

    pointer allocate(size_type n)
    {
        if (auto ptr = static_cast<pointer>(malloc(sizeof(T) * n))) {
            std::cout << "A: " << this << " ptr: " << ptr << '\n';
            return ptr;
        }

        throw std::bad_alloc();
    }

    void deallocate(pointer p, size_type n)
    {
        (void) n;

        std::cout << "D: " << this << " ptr: " << p << '\n';
        free(p);
    }
};

template <typename T1, typename T2>
bool operator==(const myallocator4<T1> &, const myallocator4<T2> &)
{ return false; }

template <typename T1, typename T2>
bool operator!=(const myallocator4<T1> &, const myallocator4<T2> &)
{ return true; }

Here is the test case:
TEST_CASE("copy container, propogate")
{
    std::cout << "copy container, propogate\n";

    std::list<int, myallocator4<int>> mylist1;
    std::list<int, myallocator4<int>> mylist2;

    mylist1.push_back(1);
    mylist1.push_back(2);
    mylist1.push_back(3);
    mylist2.push_back(4);
    mylist2.push_back(5);
    mylist2.push_back(6);

    std::cout << "--------------------------" << '\n';
    mylist2 = mylist1;
    std::cout << "--------------------------" << '\n';
}

And here is the result:
copy container, propogate
A: 0x7ffce414af40 ptr: 0x562e4822ca30
A: 0x7ffce414af40 ptr: 0x562e4822cbf0
A: 0x7ffce414af40 ptr: 0x562e4822cc40
A: 0x7ffce414af60 ptr: 0x562e48237880
A: 0x7ffce414af60 ptr: 0x562e48233fe0
A: 0x7ffce414af60 ptr: 0x562e4822cc60
--------------------------
D: 0x7ffce414af60 ptr: 0x562e48237880
D: 0x7ffce414af60 ptr: 0x562e48233fe0
D: 0x7ffce414af60 ptr: 0x562e4822cc60
A: 0x7ffce414ae40 ptr: 0x562e4822cc60
A: 0x7ffce414ae40 ptr: 0x562e48233fe0
A: 0x7ffce414ae40 ptr: 0x562e48237880
--------------------------
D: 0x7ffce414af60 ptr: 0x562e4822cc60
D: 0x7ffce414af60 ptr: 0x562e48233fe0
D: 0x7ffce414af60 ptr: 0x562e48237880
D: 0x7ffce414af40 ptr: 0x562e4822ca30
D: 0x7ffce414af40 ptr: 0x562e4822cbf0
D: 0x7ffce414af40 ptr: 0x562e4822cc40

As can be seen, the allocator at 0x7ffce414af60 (which is the original
allocator for the second list) is used to deallocate memory from
0x7ffce414ae40, even though the allocators are not equal.

Reply via email to