diff --git a/src/corelib/tools/qcontainertools_impl.h b/src/corelib/tools/qcontainertools_impl.h index 2053de6408..723c984d81 100644 --- a/src/corelib/tools/qcontainertools_impl.h +++ b/src/corelib/tools/qcontainertools_impl.h @@ -73,6 +73,15 @@ static constexpr bool q_points_into_range(const T *p, const T *b, const T *e, return !less(p, b) && less(p, e); } +template +void q_uninitialized_move_if_noexcept_n(T* first, N n, T* out) +{ + if constexpr (std::is_nothrow_move_constructible_v || !std::is_copy_constructible_v) + std::uninitialized_move_n(first, n, out); + else + std::uninitialized_copy_n(first, n, out); +} + template void q_uninitialized_relocate_n(T* first, N n, T* out) { @@ -83,7 +92,7 @@ void q_uninitialized_relocate_n(T* first, N n, T* out) n * sizeof(T)); } } else { - std::uninitialized_move_n(first, n, out); + q_uninitialized_move_if_noexcept_n(first, n, out); if constexpr (QTypeInfo::isComplex) std::destroy_n(first, n); } diff --git a/src/corelib/tools/qvarlengtharray.h b/src/corelib/tools/qvarlengtharray.h index 621949f506..dfe15f439c 100644 --- a/src/corelib/tools/qvarlengtharray.h +++ b/src/corelib/tools/qvarlengtharray.h @@ -49,7 +49,9 @@ #include #include #include +#include #include + #include #include @@ -487,38 +489,29 @@ Q_OUTOFLINE_TEMPLATE void QVarLengthArray::reallocate(qsizetype asi const qsizetype copySize = qMin(asize, osize); Q_ASSUME(copySize >= 0); + if (aalloc != capacity()) { + struct free_deleter { + void operator()(void *p) const noexcept { free(p); } + }; + std::unique_ptr guard; + T *newPtr; + qsizetype newA; if (aalloc > Prealloc) { - T *newPtr = reinterpret_cast(malloc(aalloc * sizeof(T))); + newPtr = reinterpret_cast(malloc(aalloc * sizeof(T))); + guard.reset(newPtr); Q_CHECK_PTR(newPtr); // could throw // by design: in case of QT_NO_EXCEPTIONS malloc must not fail or it crashes here - ptr = newPtr; - a = aalloc; + newA = aalloc; } else { - ptr = reinterpret_cast(array); - a = Prealloc; - } - s = 0; - if constexpr (!QTypeInfo::isRelocatable) { - QT_TRY { - // move all the old elements - while (size() < copySize) { - new (end()) T(std::move(*(oldPtr+size()))); - (oldPtr+size())->~T(); - s++; - } - } QT_CATCH(...) { - // clean up all the old objects and then free the old ptr - qsizetype sClean = size(); - while (sClean < osize) - (oldPtr+(sClean++))->~T(); - if (oldPtr != reinterpret_cast(array) && oldPtr != data()) - free(oldPtr); - QT_RETHROW; - } - } else { - memcpy(static_cast(data()), static_cast(oldPtr), copySize * sizeof(T)); + newPtr = reinterpret_cast(array); + newA = Prealloc; } + QtPrivate::q_uninitialized_relocate_n(oldPtr, copySize, newPtr); + // commit: + ptr = newPtr; + guard.release(); + a = newA; } s = copySize; @@ -540,6 +533,7 @@ Q_OUTOFLINE_TEMPLATE void QVarLengthArray::reallocate(qsizetype asi } else { s = asize; } + } template diff --git a/tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp b/tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp index 4944da71a9..8f2871a54f 100644 --- a/tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp +++ b/tests/auto/corelib/tools/qvarlengtharray/tst_qvarlengtharray.cpp @@ -429,8 +429,6 @@ void tst_QVarLengthArray::appendIsStronglyExceptionSafe() }; { - // ### TODO: QVLA isn't exception-safe when throwing during reallocation, - // ### so check with size() < capacity() for now QVarLengthArray vla(1); { Thrower t; @@ -443,6 +441,28 @@ void tst_QVarLengthArray::appendIsStronglyExceptionSafe() QVERIFY_THROWS_EXCEPTION(int, vla.push_back({})); QCOMPARE(vla.size(), 1); } + vla.push_back({}); + QCOMPARE(vla.size(), 2); + { + Thrower t; + { + // tests the copy inside append() + const QScopedValueRollback rb(throwOnCopyNow, true); + QVERIFY_THROWS_EXCEPTION(int, vla.push_back(t)); + QCOMPARE(vla.size(), 2); + } + { + // tests the move inside reallocate() + const QScopedValueRollback rb(throwOnMoveNow, true); + QVERIFY_THROWS_EXCEPTION(int, vla.push_back(t)); + QCOMPARE(vla.size(), 2); + } + } + { + const QScopedValueRollback rb(throwOnMoveNow, true); + QVERIFY_THROWS_EXCEPTION(int, vla.push_back({})); + QCOMPARE(vla.size(), 2); + } } #endif }