[libcxx-commits] [libcxx] [libc++] Validate exception throwing for vector mutators on max_size violation (PR #131953)

Peng Liu via libcxx-commits libcxx-commits at lists.llvm.org
Tue Mar 18 19:05:40 PDT 2025


https://github.com/winner245 created https://github.com/llvm/llvm-project/pull/131953

None

>From 657b2a36d9ab1926fbdf98424a3551d641c970df Mon Sep 17 00:00:00 2001
From: Peng Liu <winner245 at hotmail.com>
Date: Tue, 18 Mar 2025 22:01:01 -0400
Subject: [PATCH] Validate exception throwing for vector mutators on max_size
 violation

---
 .../reserve_exceptions.pass.cpp               | 13 ++-
 .../resize_size_exceptions.pass.cpp           | 16 +++-
 .../resize_size_value_exceptions.pass.cpp     | 10 ++-
 .../shrink_to_fit_exceptions.pass.cpp         |  4 +
 .../append_range_exceptions.pass.cpp          | 39 +++++++++
 .../emplace_back_exception_safety.pass.cpp    | 33 +++++++
 .../emplace_exceptions.pass.cpp               | 49 +++++++++++
 .../insert_exceptions.pass.cpp                | 87 +++++++++++++++++++
 .../insert_range_exceptions.pass.cpp          | 51 +++++++++++
 .../push_back_exception_safety.pass.cpp       | 15 +++-
 10 files changed, 307 insertions(+), 10 deletions(-)
 create mode 100644 libcxx/test/std/containers/sequences/vector/vector.modifiers/append_range_exceptions.pass.cpp
 create mode 100644 libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace_back_exception_safety.pass.cpp
 create mode 100644 libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace_exceptions.pass.cpp
 create mode 100644 libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_exceptions.pass.cpp
 create mode 100644 libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_range_exceptions.pass.cpp

diff --git a/libcxx/test/std/containers/sequences/vector/vector.capacity/reserve_exceptions.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.capacity/reserve_exceptions.pass.cpp
index c381e23a04d02..129a52c97decd 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.capacity/reserve_exceptions.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.capacity/reserve_exceptions.pass.cpp
@@ -8,6 +8,10 @@
 
 // UNSUPPORTED: no-exceptions
 
+// <vector>
+
+// void reserve(size_type n);
+
 // This test file validates that std::vector<T>::reserve provides the strong exception guarantee if T is
 // Cpp17MoveInsertable and no exception is thrown by the move constructor of T during the reserve call.
 // It also checks that if T's move constructor is not noexcept, reserve provides only the basic exception
@@ -36,7 +40,8 @@ void test_allocation_exception_for_strong_guarantee(
   std::size_t old_cap  = v.capacity();
 
   try {
-    v.reserve(new_cap);
+    v.reserve(new_cap); // Expected exception
+    assert(false);
   } catch (...) { // std::length_error, std::bad_alloc
     assert(v.data() == old_data);
     assert(v.size() == old_size);
@@ -61,7 +66,8 @@ void test_copy_ctor_exception_for_strong_guarantee(std::vector<throwing_data<T>,
   std::size_t new_cap        = 2 * old_cap;
 
   try {
-    v.reserve(new_cap);
+    v.reserve(new_cap); // Expected exception
+    assert(false);
   } catch (...) {
     assert(v.data() == old_data);
     assert(v.size() == old_size);
@@ -83,7 +89,8 @@ void test_move_ctor_exception_for_basic_guarantee(std::vector<move_only_throwing
     v.emplace_back(values[i], throw_after);
 
   try {
-    v.reserve(2 * v.capacity());
+    v.reserve(2 * v.capacity()); // Expected exception
+    assert(false);
   } catch (...) {
     use_unspecified_but_valid_state_vector(v);
   }
diff --git a/libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size_exceptions.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size_exceptions.pass.cpp
index b5e12ea8e5d0a..4aa19f8d89927 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size_exceptions.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size_exceptions.pass.cpp
@@ -8,6 +8,10 @@
 
 // UNSUPPORTED: no-exceptions
 
+// <vector>
+
+// void resize(size_type sz);
+
 // This test file validates that std::vector<T>::resize(size_type) provides the strong exception guarantee
 // if no exception is thrown by the move constructor of T during the resize call. It also checks that if
 // T's move constructor is not noexcept, resize provides only the basic exception guarantee.
@@ -35,7 +39,8 @@ void test_allocation_exception_for_strong_guarantee(
   std::size_t old_cap  = v.capacity();
 
   try {
-    v.resize(new_size);
+    v.resize(new_size); // Expected exception
+    assert(false);
   } catch (...) { // std::length_error, std::bad_alloc
     assert(v.data() == old_data);
     assert(v.size() == old_size);
@@ -60,7 +65,8 @@ void test_default_ctor_exception_for_strong_guarantee(
   std::size_t new_size       = old_size + 1;
 
   try {
-    v.resize(new_size);
+    v.resize(new_size); // Expected exception
+    assert(false);
   } catch (...) {
     assert(v.data() == old_data);
     assert(v.size() == old_size);
@@ -85,7 +91,8 @@ void test_copy_ctor_exception_for_strong_guarantee(std::vector<throwing_data<T>,
   std::size_t new_size       = 2 * old_cap;
 
   try {
-    v.resize(new_size);
+    v.resize(new_size); // Expected exception
+    assert(false);
   } catch (...) {
     assert(v.data() == old_data);
     assert(v.size() == old_size);
@@ -107,7 +114,8 @@ void test_move_ctor_exception_for_basic_guarantee(std::vector<move_only_throwing
     v.emplace_back(values[i], throw_after);
 
   try {
-    v.resize(2 * v.capacity());
+    v.resize(2 * v.capacity()); // Expected exception
+    assert(false);
   } catch (...) {
     use_unspecified_but_valid_state_vector(v);
   }
diff --git a/libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size_value_exceptions.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size_value_exceptions.pass.cpp
index 7217b47d69e4d..3881d5f1b871b 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size_value_exceptions.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.capacity/resize_size_value_exceptions.pass.cpp
@@ -8,6 +8,10 @@
 
 // UNSUPPORTED: no-exceptions
 
+// <vector>
+
+// void resize(size_type sz, const value_type& x);
+
 // Check that std::vector<T>::resize(size_type sz, const value_type& x) provides the strong exception guarantee
 // if T is Cpp17CopyInsertable.
 
@@ -33,7 +37,8 @@ void test_allocation_exception_for_strong_guarantee(
   std::size_t old_cap  = v.capacity();
 
   try {
-    v.resize(new_size, values.empty() ? T() : values[0]);
+    v.resize(new_size, values.empty() ? T() : values[0]); // Expected exception
+    assert(false);
   } catch (...) { // std::length_error, std::bad_alloc
     assert(v.data() == old_data);
     assert(v.size() == old_size);
@@ -60,7 +65,8 @@ void test_copy_ctor_exception_for_strong_guarantee(std::vector<throwing_data<T>,
   try {
     int n = new_size - old_size + 1;
     throwing_data<T> t(T(), n);
-    v.resize(new_size, t);
+    v.resize(new_size, t); // Expected exception
+    assert(false);
   } catch (...) {
     assert(v.data() == old_data);
     assert(v.size() == old_size);
diff --git a/libcxx/test/std/containers/sequences/vector/vector.capacity/shrink_to_fit_exceptions.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.capacity/shrink_to_fit_exceptions.pass.cpp
index 521a25fdeda0f..9b316689b34b9 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.capacity/shrink_to_fit_exceptions.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.capacity/shrink_to_fit_exceptions.pass.cpp
@@ -8,6 +8,10 @@
 
 // UNSUPPORTED: no-exceptions
 
+// <vector>
+
+// void shrink_to_fit();
+
 // This test file validates that std::vector<T>::shrink_to_fit provides the strong exception guarantee when
 // T is Cpp17MoveInsertable and its move constructor does not throw exceptions during the shrink_to_fit
 // call. Additionally, it checks that for move-only types where T's move constructor is not noexcept, only
diff --git a/libcxx/test/std/containers/sequences/vector/vector.modifiers/append_range_exceptions.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.modifiers/append_range_exceptions.pass.cpp
new file mode 100644
index 0000000000000..1d8e60722d516
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/vector/vector.modifiers/append_range_exceptions.pass.cpp
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: no-exceptions
+
+// template<container-compatible-range<T> R>
+//   constexpr void append_range(R&& rg); // C++23
+
+#include <cassert>
+#include <vector>
+
+#include "test_allocator.h"
+
+void test() {
+  {
+    std::vector<int, limited_allocator<int, 10> > v(8, 42);
+    int a[] = {1, 2, 3};
+    try {
+      v.append_range(a);
+      assert(false);
+    } catch (...) {
+      assert(v.size() == 8);
+      for (std::size_t i = 0; i != v.size(); ++i)
+        assert(v[i] == 42);
+    }
+  }
+}
+
+int main(int, char**) {
+  test();
+
+  return 0;
+}
diff --git a/libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace_back_exception_safety.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace_back_exception_safety.pass.cpp
new file mode 100644
index 0000000000000..cb6e0c6f72e07
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace_back_exception_safety.pass.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: no-exceptions
+
+// <vector>
+
+// template <class... Args>
+//     reference emplace_back(Args&&... args); // reference in C++17
+
+#include <cassert>
+#include <vector>
+
+#include "test_allocator.h"
+
+int main(int, char**) {
+  std::vector<int, limited_allocator<int, 10> > v(10, 42);
+  try {
+    v.emplace_back(0);
+    assert(false);
+  } catch (...) {
+    assert(v.size() == v.max_size());
+    for (std::size_t i = 0; i != v.size(); ++i)
+      assert(v[i] == 42); // Strong exception safety guarantee
+  }
+
+  return 0;
+}
diff --git a/libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace_exceptions.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace_exceptions.pass.cpp
new file mode 100644
index 0000000000000..adcfa348cc5e5
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/vector/vector.modifiers/emplace_exceptions.pass.cpp
@@ -0,0 +1,49 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: no-exceptions
+
+// <vector>
+
+// template <class... Args> iterator emplace(const_iterator pos, Args&&... args);
+
+#include <cassert>
+#include <vector>
+
+#include "test_allocator.h"
+
+void test() {
+  {
+    std::vector<int, limited_allocator<int, 10> > v(10, 42);
+    try {
+      v.emplace(v.begin(), 0);
+      assert(false);
+    } catch (...) {
+      assert(v.size() == v.max_size());
+      for (std::size_t i = 0; i != v.size(); ++i)
+        assert(v[i] == 42);
+    }
+  }
+  {
+    std::vector<int, limited_allocator<int, 10> > v(10, 42);
+    try {
+      v.emplace(v.end(), 0);
+      assert(false);
+    } catch (...) {
+      assert(v.size() == v.max_size());
+      for (std::size_t i = 0; i != v.size(); ++i)
+        assert(v[i] == 42);
+    }
+  }
+}
+
+int main(int, char**) {
+  test();
+
+  return 0;
+}
diff --git a/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_exceptions.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_exceptions.pass.cpp
new file mode 100644
index 0000000000000..acc749323be88
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_exceptions.pass.cpp
@@ -0,0 +1,87 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: no-exceptions
+
+// <vector>
+
+// iterator insert(const_iterator position, const value_type& x);
+// iterator insert(const_iterator position, size_type n, const value_type& x);
+// iterator insert(const_iterator p, initializer_list<value_type> il);
+// template <class Iter>
+//   iterator insert(const_iterator position, Iter first, Iter last);
+
+#include <cassert>
+#include <vector>
+
+#include "test_allocator.h"
+
+void test() {
+  {
+    std::vector<int, limited_allocator<int, 10> > v(10, 42);
+    try {
+      v.insert(v.begin(), 0);
+      assert(false);
+    } catch (...) {
+      assert(v.size() == v.max_size());
+      for (std::size_t i = 0; i != v.size(); ++i)
+        assert(v[i] == 42);
+    }
+  }
+  {
+    std::vector<int, limited_allocator<int, 10> > v(10, 42);
+    try {
+      v.insert(v.end(), 0);
+      assert(false);
+    } catch (...) {
+      assert(v.size() == v.max_size());
+      for (std::size_t i = 0; i != v.size(); ++i)
+        assert(v[i] == 42);
+    }
+  }
+  {
+    std::vector<int, limited_allocator<int, 10> > v(8, 42);
+    try {
+      v.insert(v.begin(), 3, 0);
+      assert(false);
+    } catch (...) {
+      assert(v.size() == 8);
+      for (std::size_t i = 0; i != v.size(); ++i)
+        assert(v[i] == 42);
+    }
+  }
+  {
+    std::vector<int, limited_allocator<int, 10> > v(8, 42);
+    try {
+      v.insert(v.begin(), {1, 2, 3});
+      assert(false);
+    } catch (...) {
+      assert(v.size() == 8);
+      for (std::size_t i = 0; i != v.size(); ++i)
+        assert(v[i] == 42);
+    }
+  }
+  {
+    std::vector<int, limited_allocator<int, 10> > v(8, 42);
+    int a[] = {1, 2, 3};
+    try {
+      v.insert(v.begin(), a, a + 3);
+      assert(false);
+    } catch (...) {
+      assert(v.size() == 8);
+      for (std::size_t i = 0; i != v.size(); ++i)
+        assert(v[i] == 42);
+    }
+  }
+}
+
+int main(int, char**) {
+  test();
+
+  return 0;
+}
diff --git a/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_range_exceptions.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_range_exceptions.pass.cpp
new file mode 100644
index 0000000000000..560d6f3de4282
--- /dev/null
+++ b/libcxx/test/std/containers/sequences/vector/vector.modifiers/insert_range_exceptions.pass.cpp
@@ -0,0 +1,51 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: no-exceptions
+
+// template<container-compatible-range<T> R>
+//   constexpr iterator insert_range(const_iterator position, R&& rg); // C++23
+
+#include <cassert>
+#include <vector>
+
+#include "test_allocator.h"
+
+void test() {
+  {
+    std::vector<int, limited_allocator<int, 10> > v(8, 42);
+    int a[] = {1, 2, 3};
+    try {
+      v.insert_range(v.begin(), a);
+      assert(false);
+    } catch (...) {
+      assert(v.size() == 8);
+      for (std::size_t i = 0; i != v.size(); ++i)
+        assert(v[i] == 42);
+    }
+  }
+  {
+    std::vector<int, limited_allocator<int, 10> > v(8, 42);
+    int a[] = {1, 2, 3};
+    try {
+      v.insert_range(v.end(), a);
+      assert(false);
+    } catch (...) {
+      assert(v.size() == 8);
+      for (std::size_t i = 0; i != v.size(); ++i)
+        assert(v[i] == 42);
+    }
+  }
+}
+
+int main(int, char**) {
+  test();
+
+  return 0;
+}
diff --git a/libcxx/test/std/containers/sequences/vector/vector.modifiers/push_back_exception_safety.pass.cpp b/libcxx/test/std/containers/sequences/vector/vector.modifiers/push_back_exception_safety.pass.cpp
index cbdc55a700b75..d41577dc79a1c 100644
--- a/libcxx/test/std/containers/sequences/vector/vector.modifiers/push_back_exception_safety.pass.cpp
+++ b/libcxx/test/std/containers/sequences/vector/vector.modifiers/push_back_exception_safety.pass.cpp
@@ -10,10 +10,11 @@
 
 // void push_back(const value_type& x);
 
-#include <vector>
 #include <cassert>
+#include <vector>
 
 #include "asan_testing.h"
+#include "test_allocator.h"
 #include "test_macros.h"
 
 // Flag that makes the copy constructor for CMyClass throw an exception
@@ -86,6 +87,18 @@ int main(int, char**) {
     assert(vec == vec2);
     assert(is_contiguous_container_asan_correct(vec));
   }
+
+  {
+    std::vector<int, limited_allocator<int, 10> > v(10, 42);
+    try {
+      v.push_back(0);
+      assert(false);
+    } catch (...) {
+      assert(v.size() == v.max_size());
+      for (std::size_t i = 0; i != v.size(); ++i)
+        assert(v[i] == 42); // Strong exception safety guarantee
+    }
+  }
 #endif
 
   return 0;



More information about the libcxx-commits mailing list