https://gcc.gnu.org/bugzilla/show_bug.cgi?id=125693
Bug ID: 125693
Summary: std::vector::insert(it, cref) crashes when
sizeof(value_type) ≥ 8MiB
Product: gcc
Version: 15.2.1
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: libstdc++
Assignee: unassigned at gcc dot gnu.org
Reporter: marc.mutz at hotmail dot com
Target Milestone: ---
I found this gem in Qt's test suite:
{ // make sure we don't kill the compiler with recursive templates
QList<std::array<Object *, 1'000'000>> wideList;
QRangeModel model(wideList);
}
so I was wondering how std::vector fares with types whose size exceeds the
platform's stack size.
For the most part, everything works (good!), but insert(it, cref) doesn't,
because it takes a protective copy on the stack. That triggers SIGSEGV due to
stack exhaustion from that single element, of course).
I know that insertion in the middle of a vector with 8MiB-sized elements is
dubious, but one thing is performance, the other is correctness.
Note that emplace(it, args...) somehow gets this right (though `sizeof...(args)
== 0` in my test; emplace(it, val) doesn't).
Here's the test I'm using:
template<typename Container>
void copesWithVeryLargeElementTypes_impl()
{
using V = typename Container::value_type;
using S = typename Container::size_type;
// WARNING: Objects of type V must never be allocated on the stack,
// as that would blow the stack up!
Container c; // c == {}
S n = 0;
#define CHECK_SIZE_INCREASED() do { ++n; assert(c.size() == n); } while (false)
#define CHECK_SIZE_DECREASED() do { --n; assert(c.size() == n); } while (false)
// default-constructed elements:
c.emplace_back(); // c == {1} at the end
CHECK_SIZE_INCREASED();
c.emplace(c.begin()); // c == {21} at the front
CHECK_SIZE_INCREASED();
c.emplace(std::next(c.begin())); // c == {231} in the middle
CHECK_SIZE_INCREASED();
// copies (lvalue):
{
auto p = std::make_unique<V>();
c.push_back(*p); // c == {2314}
CHECK_SIZE_INCREASED();
if constexpr (false) { // stack exhaustion on these two operations
c.emplace(c.begin(), *p); // c == {52314}
CHECK_SIZE_INCREASED();
c.insert(std::next(c.begin(), 2), *p); // c == {526314}
CHECK_SIZE_INCREASED();
}
}
// erasure
c.erase(std::next(c.begin())); // c == {56314} in the middle
CHECK_SIZE_DECREASED();
c.erase(c.begin()); // c == {6314} at the front
CHECK_SIZE_DECREASED();
c.pop_back(); // c == {631} at the end
CHECK_SIZE_DECREASED();
// moves (rvalues)
const auto rvalue = [](std::unique_ptr<V> &&storage = nullptr)
-> V&& // don't return by value!
{
// std::unique_ptr::op* isn't overloaded on rvalue-this:
storage = std::make_unique<V>();
return std::move(*storage);
};
c.push_back(rvalue()); // c == {6317}
CHECK_SIZE_INCREASED();
c.emplace(c.begin(), rvalue()); // c == {86317}
CHECK_SIZE_INCREASED();
c.insert(std::next(c.begin(), 2), rvalue()); // c == {869317}
CHECK_SIZE_INCREASED();
// and, finally, clear():
c.clear(); // c == {}
QCOMPARE(c.size(), S{0});
#undef CHECK_SIZE_DECREASED
#undef CHECK_SIZE_INCREASED
}
using LargerThanStack = std::array<std::byte, 8 * 1024 * 1024>;
int main() {
copesWithVeryLargeElementTypes_impl<std::vector<LargerThanStack>();
}