Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[libc++] strong exception safety not guaranteed by std::vector<T,Allocator>::insert #112362

Open
futog opened this issue Oct 15, 2024 · 5 comments
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.

Comments

@futog
Copy link
Contributor

futog commented Oct 15, 2024

The following code

#include <stdexcept>
#include <vector>
#include <cassert>

#include <stdio.h>

int count = 0;

template <typename T>
struct bad_alloc {
    typedef T value_type;
    std::allocator<T> alloc;

    std::size_t max_size() const {
        return 1000;
    }

    template <class U, class... Args>
    void construct(U* p, Args&&... args) {
        if (count-- <= 0) throw 28;
        std::allocator_traits<std::allocator<T>>::construct(alloc, p,
                std::forward<Args>(args)...);
    }

    void deallocate(T* p, std::size_t n) {
        return alloc.deallocate(p, n);
    }

    T* allocate(std::size_t n) {
        return alloc.allocate(n);
    }

    T* allocate(std::size_t n, const void* hint) {
        return alloc.allocate(n, hint);
    }
};

template <typename T>
bool operator==(const bad_alloc<T>& lhs, const bad_alloc<T>& rhs) {
    return true;
}

template <typename T>
bool operator!=(const bad_alloc<T>& lhs, const bad_alloc<T>& rhs) {
    return false;
}

int main() {
    count = 9;
    std::vector<int, bad_alloc<int> > vc;
    vc.reserve(4);
    try {
        /* push_back */
        vc.push_back(3);
        vc.push_back(3);
        vc.push_back(3);
        vc.push_back(3);
        
        assert(vc.size() == 4);
        assert(vc.capacity() == 4);
        assert(count == 5);
        // this insert throws
        vc.insert(vc.begin() + 1, 2, 3);
    } catch (int e) {
        assert(e == 28);
        assert(vc.size() == 4);
    }

    return 1;
}

fails with

Assertion `vc.size() == 4' failed

Tested on x86_64 latest: https://godbolt.org/z/7Gjsh9e6f

@github-actions github-actions bot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Oct 15, 2024
@philnik777
Copy link
Contributor

Why do you think there is a strong exception guarantee for vector::insert? https://eel.is/c++draft/vector#modifiers-2 seems to say that the effects are unspecified if any copy/move construction/assignment throws.

@futog
Copy link
Contributor Author

futog commented Oct 15, 2024

It is not the copy/move construction/assignment that throws, we are copying ints. The allocator throws.

@philnik777
Copy link
Contributor

It is not the copy/move construction/assignment that throws, we are copying ints. The allocator throws.

Sure, but IMO this is a distinction without a difference here. Copy constructing an object means calling allocator::construct. If it were the intention to allow allocator::construct to throw and that not counting as the constructor throwing the wording would be basically useless.

@philnik777
Copy link
Contributor

Also note that that interpretation would make it impossible to fulfill the strong exception guarantee in the is_nothrow_move_constructible_v<T> case.

@frederick-vs-ja
Copy link
Contributor

frederick-vs-ja commented Oct 15, 2024

#80558 seemed to cause this issue. In LLVM 18 the program worked as expected.

Sure, but IMO this is a distinction without a difference here. Copy constructing an object means calling allocator::construct.

Also note that that interpretation would make it impossible to fulfill the strong exception guarantee in the is_nothrow_move_constructible_v<T> case.

It seems that the standard wording is very defective here. There're already LWG2158 and LWG4123.

Moreover, copy/move constructors are mentioned, but ator::construct doesn't even need to call these constructors, which is the case for uses-allocator construction of some containers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

No branches or pull requests

3 participants