[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