[libcxx-commits] [libcxx] [libc++] [test] Exception-safety for deque constructors (PR #202469)
via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Jun 8 17:46:05 PDT 2026
https://github.com/halbi2 updated https://github.com/llvm/llvm-project/pull/202469
>From 9dd98320b27bc9cb186dd692141b4d67cba233ed Mon Sep 17 00:00:00 2001
From: halbi2 <hehiralbi at gmail.com>
Date: Mon, 8 Jun 2026 20:15:35 -0400
Subject: [PATCH] [libc++] [test] Exception-safety for deque constructors
Every throwing constructor is tested except for the allocator-extended
move constructor that can copy when the allocators mismatch.
Fixes #62056
---
libcxx/include/deque | 41 ++-
.../deque/deque.cons/from_range.pass.cpp | 6 +-
.../sequences/deque/exception_safety.pass.cpp | 271 ++++++++++++++++++
3 files changed, 308 insertions(+), 10 deletions(-)
create mode 100644 libcxx/test/std/containers/sequences/deque/exception_safety.pass.cpp
diff --git a/libcxx/include/deque b/libcxx/include/deque
index bf589b05aef72..7bb09f3fc9703 100644
--- a/libcxx/include/deque
+++ b/libcxx/include/deque
@@ -622,7 +622,9 @@ public:
__annotate_new(0);
}
- _LIBCPP_HIDE_FROM_ABI ~deque() {
+ _LIBCPP_HIDE_FROM_ABI ~deque() { __dtor_logic(); }
+
+ _LIBCPP_HIDE_FROM_ABI void __dtor_logic() _NOEXCEPT {
clear();
__annotate_delete();
typename __map::iterator __i = __map_.begin();
@@ -646,8 +648,11 @@ public:
_LIBCPP_HIDE_FROM_ABI deque(size_type __n, const value_type& __v, const allocator_type& __a)
: __map_(__pointer_allocator(__a)), __start_(0), __size_(0), __alloc_(__a) {
__annotate_new(0);
- if (__n > 0)
+ if (__n > 0) {
+ auto __guard = std::__make_exception_guard([&] { __dtor_logic(); });
__append(__n, __v);
+ __guard.__complete();
+ }
}
template <class _InputIter, __enable_if_t<__has_input_iterator_category<_InputIter>::value, int> = 0>
@@ -659,14 +664,15 @@ public:
template <_ContainerCompatibleRange<_Tp> _Range>
_LIBCPP_HIDE_FROM_ABI deque(from_range_t, _Range&& __range, const allocator_type& __a = allocator_type())
: __map_(__pointer_allocator(__a)), __start_(0), __size_(0), __alloc_(__a) {
+ auto __guard = std::__make_exception_guard([&] { __dtor_logic(); });
if constexpr (ranges::forward_range<_Range> || ranges::sized_range<_Range>) {
__append_with_size(ranges::begin(__range), ranges::distance(__range));
-
} else {
for (auto&& __e : __range) {
emplace_back(std::forward<decltype(__e)>(__e));
}
}
+ __guard.__complete();
}
# endif
@@ -1312,8 +1318,11 @@ deque(from_range_t, _Range&&, _Alloc = _Alloc()) -> deque<ranges::range_value_t<
template <class _Tp, class _Allocator>
deque<_Tp, _Allocator>::deque(size_type __n) : __start_(0), __size_(0) {
__annotate_new(0);
- if (__n > 0)
+ if (__n > 0) {
+ auto __guard = std::__make_exception_guard([&] { __dtor_logic(); });
__append(__n);
+ __guard.__complete();
+ }
}
# if _LIBCPP_STD_VER >= 14
@@ -1321,23 +1330,31 @@ template <class _Tp, class _Allocator>
deque<_Tp, _Allocator>::deque(size_type __n, const _Allocator& __a)
: __map_(__pointer_allocator(__a)), __start_(0), __size_(0), __alloc_(__a) {
__annotate_new(0);
- if (__n > 0)
+ if (__n > 0) {
+ auto __guard = std::__make_exception_guard([&] { __dtor_logic(); });
__append(__n);
+ __guard.__complete();
+ }
}
# endif
template <class _Tp, class _Allocator>
deque<_Tp, _Allocator>::deque(size_type __n, const value_type& __v) : __start_(0), __size_(0) {
__annotate_new(0);
- if (__n > 0)
+ if (__n > 0) {
+ auto __guard = std::__make_exception_guard([&] { __dtor_logic(); });
__append(__n, __v);
+ __guard.__complete();
+ }
}
template <class _Tp, class _Allocator>
template <class _InputIter, __enable_if_t<__has_input_iterator_category<_InputIter>::value, int> >
deque<_Tp, _Allocator>::deque(_InputIter __f, _InputIter __l) : __start_(0), __size_(0) {
__annotate_new(0);
+ auto __guard = std::__make_exception_guard([&] { __dtor_logic(); });
__append(__f, __l);
+ __guard.__complete();
}
template <class _Tp, class _Allocator>
@@ -1345,7 +1362,9 @@ template <class _InputIter, __enable_if_t<__has_input_iterator_category<_InputIt
deque<_Tp, _Allocator>::deque(_InputIter __f, _InputIter __l, const allocator_type& __a)
: __map_(__pointer_allocator(__a)), __start_(0), __size_(0), __alloc_(__a) {
__annotate_new(0);
+ auto __guard = std::__make_exception_guard([&] { __dtor_logic(); });
__append(__f, __l);
+ __guard.__complete();
}
template <class _Tp, class _Allocator>
@@ -1355,14 +1374,18 @@ deque<_Tp, _Allocator>::deque(const deque& __c)
__size_(0),
__alloc_(__map_.__get_allocator()) {
__annotate_new(0);
+ auto __guard = std::__make_exception_guard([&] { __dtor_logic(); });
__append(__c.begin(), __c.end());
+ __guard.__complete();
}
template <class _Tp, class _Allocator>
deque<_Tp, _Allocator>::deque(const deque& __c, const __type_identity_t<allocator_type>& __a)
: __map_(__pointer_allocator(__a)), __start_(0), __size_(0), __alloc_(__a) {
__annotate_new(0);
+ auto __guard = std::__make_exception_guard([&] { __dtor_logic(); });
__append(__c.begin(), __c.end());
+ __guard.__complete();
}
template <class _Tp, class _Allocator>
@@ -1379,14 +1402,18 @@ deque<_Tp, _Allocator>& deque<_Tp, _Allocator>::operator=(const deque& __c) {
template <class _Tp, class _Allocator>
deque<_Tp, _Allocator>::deque(initializer_list<value_type> __il) : __start_(0), __size_(0) {
__annotate_new(0);
+ auto __guard = std::__make_exception_guard([&] { __dtor_logic(); });
__append(__il.begin(), __il.end());
+ __guard.__complete();
}
template <class _Tp, class _Allocator>
deque<_Tp, _Allocator>::deque(initializer_list<value_type> __il, const allocator_type& __a)
: __map_(__pointer_allocator(__a)), __start_(0), __size_(0), __alloc_(__a) {
__annotate_new(0);
+ auto __guard = std::__make_exception_guard([&] { __dtor_logic(); });
__append(__il.begin(), __il.end());
+ __guard.__complete();
}
template <class _Tp, class _Allocator>
@@ -1413,7 +1440,9 @@ inline deque<_Tp, _Allocator>::deque(deque&& __c, const __type_identity_t<alloca
__start_ = 0;
__size() = 0;
typedef move_iterator<iterator> _Ip;
+ auto __guard = std::__make_exception_guard([&] { __dtor_logic(); });
assign(_Ip(__c.begin()), _Ip(__c.end()));
+ __guard.__complete();
}
}
diff --git a/libcxx/test/std/containers/sequences/deque/deque.cons/from_range.pass.cpp b/libcxx/test/std/containers/sequences/deque/deque.cons/from_range.pass.cpp
index 3b3632a3d3c67..0094386382df1 100644
--- a/libcxx/test/std/containers/sequences/deque/deque.cons/from_range.pass.cpp
+++ b/libcxx/test/std/containers/sequences/deque/deque.cons/from_range.pass.cpp
@@ -27,10 +27,8 @@ int main(int, char**) {
static_assert(test_constraints<std::deque, int, double>());
- // TODO(varconst): `deque`'s constructors currently aren't exception-safe.
- // See https://llvm.org/PR62056.
- //test_exception_safety_throwing_copy<std::deque>();
- //test_exception_safety_throwing_allocator<std::deque, int>();
+ test_exception_safety_throwing_copy<std::deque>();
+ test_exception_safety_throwing_allocator<std::deque, int>();
return 0;
}
diff --git a/libcxx/test/std/containers/sequences/deque/exception_safety.pass.cpp b/libcxx/test/std/containers/sequences/deque/exception_safety.pass.cpp
new file mode 100644
index 0000000000000..56f1b083addc6
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/deque/exception_safety.pass.cpp
@@ -0,0 +1,271 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// <deque>
+
+// UNSUPPORTED: c++03, no-exceptions
+
+// TODO:
+// - deque(deque&&, const allocator_type&)
+
+// deque(size_type n);
+// deque(size_type n, const allocator_type& a);
+// deque(size_type n, const value_type& v);
+// deque(size_type n, const value_type& v, const allocator_type& a);
+// template <class InputIterator>
+// deque(InputIterator first, InputIterator last);
+// template <class InputIterator>
+// deque(InputIterator first, InputIterator last, const allocator_type& a);
+// template<container-compatible-range<T> R>
+// deque(from_range_t, R&& rg, const Allocator& = Allocator()); // C++23
+// deque(const deque& x);
+// deque(const deque& x, const allocator_type& a);
+//
+// deque& operator=(const deque& x);
+//
+// template <class InputIterator>
+// void assign(InputIterator first, InputIterator last);
+// void assign(size_type n, const value_type& v);
+// template<container-compatible-range<T> R>
+// void assign_range(R&& rg); // C++23
+//
+// template<container-compatible-range<T> R>
+// void prepend_range(R&& rg); // C++23
+// void push_back(const value_type& x);
+// template<container-compatible-range<T> R>
+// void append_range(R&& rg); // C++23
+// void push_front(const value_type& v);
+//
+// iterator insert(const_iterator p, const value_type& v);
+// iterator insert(const_iterator p, size_type n, const value_type& v);
+// template <class InputIterator>
+// iterator insert(const_iterator p,
+// InputIterator first, InputIterator last);
+// template<container-compatible-range<T> R>
+// iterator insert_range(const_iterator position, R&& rg); // C++23
+//
+// void resize(size_type n, const value_type& v);
+
+#include <deque>
+
+#include <cassert>
+#include "../../exception_safety_helpers.h"
+#include "test_macros.h"
+
+#if TEST_STD_VER >= 23
+# include <ranges>
+#endif
+
+int main(int, char**) {
+ {
+ constexpr int ThrowOn = 1;
+ constexpr int Size = 1;
+ using T = ThrowingCopy<ThrowOn>;
+
+ // void push_front(const value_type& v);
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T*) {
+ std::deque<T> c;
+ c.push_front(*from);
+ });
+
+ // void push_back(const value_type& v);
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T*) {
+ std::deque<T> c;
+ c.push_back(*from);
+ });
+
+ // iterator insert(const_iterator p, const value_type& v);
+ test_exception_safety_throwing_copy</*ThrowOn=*/1, Size>([](T* from, T*) {
+ std::deque<T> c;
+ c.insert(c.end(), *from);
+ });
+ }
+
+ {
+ constexpr int ThrowOn = 3;
+ constexpr int Size = 5;
+ using T = ThrowingDefault<ThrowOn>;
+ using Alloc = std::allocator<T>;
+
+ // deque(size_type n);
+ test_exception_safety_throwing_default<ThrowOn, Size>([](size_t n) {
+ std::deque<T> c(n);
+ (void)c;
+ });
+
+ // deque(size_type n, const allocator_type& a);
+ test_exception_safety_throwing_default<ThrowOn, Size>([](size_t n) {
+ std::deque<T> c(n, Alloc());
+ (void)c;
+ });
+ }
+
+ {
+ constexpr int ThrowOn = 3;
+ constexpr int Size = 5;
+ using T = ThrowingCopy<ThrowOn>;
+ using C = std::deque<T>;
+ using Alloc = std::allocator<T>;
+
+ std::initializer_list<T> il = {1, 2, 3, 4, 5};
+
+ // deque(size_type n, const value_type& v);
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T*) {
+ std::deque<T> c(Size, *from);
+ (void)c;
+ });
+
+ // deque(size_type n, const value_type& v, const allocator_type& a);
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T*) {
+ std::deque<T> c(Size, *from, Alloc());
+ (void)c;
+ });
+
+ // template <class InputIterator>
+ // deque(InputIterator first, InputIterator last);
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to) {
+ std::deque<T> c(from, to);
+ (void)c;
+ });
+
+ // template <class InputIterator>
+ // deque(InputIterator first, InputIterator last, const allocator_type& a);
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to) {
+ std::deque<T> c(from, to, Alloc());
+ (void)c;
+ });
+
+#if TEST_STD_VER >= 23
+ // template<container-compatible-range<T> R>
+ // deque(from_range_t, R&& rg, const Allocator& = Allocator()); // C++23
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to) {
+ {
+ std::deque<T> c(std::from_range, std::ranges::subrange(from, to));
+ (void)c;
+ }
+
+ {
+ std::deque<T> c(std::from_range, std::ranges::subrange(from, to), Alloc());
+ (void)c;
+ }
+ });
+#endif
+
+ // template <class InputIterator>
+ // deque(InputIterator first, InputIterator last, const allocator_type& a);
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to) {
+ std::deque<T> c(from, to, Alloc());
+ (void)c;
+ });
+
+ // deque(const deque& x);
+ test_exception_safety_throwing_copy_container<C, ThrowOn, Size>([](C&& in) {
+ std::deque<T> c(in);
+ (void)c;
+ });
+
+ // deque(const deque& x, const allocator_type& a);
+ test_exception_safety_throwing_copy_container<C, ThrowOn, Size>([](C&& in) {
+ std::deque<T> c(in, Alloc());
+ (void)c;
+ });
+
+ // deque(initializer_list<value_type> il);
+ test_exception_safety_throwing_copy<ThrowOn, Size>([&il](T*, T*) {
+ std::deque<T> c(il);
+ (void)c;
+ });
+
+ // deque(initializer_list<value_type> il, const allocator_type& a);
+ test_exception_safety_throwing_copy<ThrowOn, Size>([&il](T*, T*) {
+ std::deque<T> c(il, Alloc());
+ (void)c;
+ });
+
+ // deque& operator=(const deque& x);
+ test_exception_safety_throwing_copy_container<C, ThrowOn, Size>([](C&& in) {
+ std::deque<T> c;
+ c = in;
+ });
+
+ // deque& operator=(initializer_list<value_type> il);
+ test_exception_safety_throwing_copy<ThrowOn, Size>([&il](T*, T*) {
+ std::deque<T> c;
+ c = il;
+ });
+
+ // template <class InputIterator>
+ // void assign(InputIterator first, InputIterator last);
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to) {
+ std::deque<T> c;
+ c.assign(from, to);
+ });
+
+#if TEST_STD_VER >= 23
+ // template<container-compatible-range<T> R>
+ // void assign_range(R&& rg); // C++23
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to) {
+ std::deque<T> c;
+ c.assign_range(std::ranges::subrange(from, to));
+ });
+#endif
+
+ // void assign(size_type n, const value_type& v);
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T*) {
+ std::deque<T> c;
+ c.assign(Size, *from);
+ });
+
+#if TEST_STD_VER >= 23
+ // template<container-compatible-range<T> R>
+ // void prepend_range(R&& rg); // C++23
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to) {
+ std::deque<T> c;
+ c.prepend_range(std::ranges::subrange(from, to));
+ });
+
+ // template<container-compatible-range<T> R>
+ // void append_range(R&& rg); // C++23
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to) {
+ std::deque<T> c;
+ c.append_range(std::ranges::subrange(from, to));
+ });
+#endif
+
+ // iterator insert(const_iterator p, size_type n, const value_type& v);
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T*) {
+ std::deque<T> c;
+ c.insert(c.end(), Size, *from);
+ });
+
+ // template <class InputIterator>
+ // iterator insert(const_iterator p,
+ // InputIterator first, InputIterator last);
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to) {
+ std::deque<T> c;
+ c.insert(c.end(), from, to);
+ });
+
+#if TEST_STD_VER >= 23
+ // template<container-compatible-range<T> R>
+ // iterator insert_range(const_iterator position, R&& rg); // C++23
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T* to) {
+ std::deque<T> c;
+ c.insert_range(c.end(), std::ranges::subrange(from, to));
+ });
+#endif
+
+ // void resize(size_type n, const value_type& v);
+ test_exception_safety_throwing_copy<ThrowOn, Size>([](T* from, T*) {
+ std::deque<T> c;
+ c.resize(Size, *from);
+ });
+ }
+
+ return 0;
+}
More information about the libcxx-commits
mailing list