[libcxx-commits] [libcxx] [libc++] Assert that the elements selected by a valarray subscript exist (PR #236120)

Ilyas Mallah via libcxx-commits libcxx-commits at lists.llvm.org
Tue Nov 3 00:51:01 PST 2026


https://github.com/ilyas-mallah updated https://github.com/llvm/llvm-project/pull/236120

>From 38ca4d14dbc191cbdb9651d42a9f88b832b67eb3 Mon Sep 17 00:00:00 2001
From: Ilyas Mallah <github at ilyasm.dev>
Date: Fri, 9 Oct 2026 17:04:09 +0200
Subject: [PATCH] [libc++] Assert that the elements selected by a valarray
 subscript exist

---
 libcxx/include/valarray                       |  42 +++++++
 .../numarray/assert.subscript.pass.cpp        | 115 ++++++++++++++++++
 2 files changed, 157 insertions(+)
 create mode 100644 libcxx/test/libcxx/numerics/numarray/assert.subscript.pass.cpp

diff --git a/libcxx/include/valarray b/libcxx/include/valarray
index df3ce3252e3d2c..ae6466dc80e37f 100644
--- a/libcxx/include/valarray
+++ b/libcxx/include/valarray
@@ -1047,6 +1047,34 @@ struct _BinaryOp<_Op, valarray<_Tp>, valarray<_Tp> > {
   _LIBCPP_HIDE_FROM_ABI size_t size() const { return __a0_.size(); }
 };
 
+// Bounds checks for the subscripts that return a slice_array, gslice_array, mask_array or indirect_array.
+
+// Whether start() + i * stride() is below __n for every i < size(), computed without overflow.
+inline _LIBCPP_HIDE_FROM_ABI bool __slice_in_bounds(const slice& __sl, size_t __n) {
+  if (__sl.size() == 0)
+    return true;
+  if (__sl.start() >= __n)
+    return false;
+  return __sl.stride() == 0 || __sl.size() - 1 <= (__n - 1 - __sl.start()) / __sl.stride();
+}
+
+inline _LIBCPP_HIDE_FROM_ABI bool __indices_in_bounds(const size_t* __first, const size_t* __last, size_t __n) {
+  for (; __first != __last; ++__first)
+    if (*__first >= __n)
+      return false;
+  return true;
+}
+
+// Whether the mask [__first, __last) selects no position at or past __n.
+inline _LIBCPP_HIDE_FROM_ABI bool __mask_in_bounds(const bool* __first, const bool* __last, size_t __n) {
+  if (static_cast<size_t>(__last - __first) <= __n)
+    return true;
+  for (__first += __n; __first != __last; ++__first)
+    if (*__first)
+      return false;
+  return true;
+}
+
 // slice_array
 
 template <class _Tp>
@@ -2224,6 +2252,8 @@ inline __val_expr<__slice_expr<const valarray<_Tp>&> > valarray<_Tp>::operator[]
 
 template <class _Tp>
 inline slice_array<_Tp> valarray<_Tp>::operator[](slice __s) {
+  _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+      std::__slice_in_bounds(__s, size()), "valarray::operator[](slice) selects elements out of bounds");
   return slice_array<value_type>(__s, *this);
 }
 
@@ -2234,6 +2264,8 @@ inline __val_expr<__indirect_expr<const valarray<_Tp>&> > valarray<_Tp>::operato
 
 template <class _Tp>
 inline gslice_array<_Tp> valarray<_Tp>::operator[](const gslice& __gs) {
+  _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(std::__indices_in_bounds(__gs.__1d_.__begin_, __gs.__1d_.__end_, size()),
+                                      "valarray::operator[](gslice) selects elements out of bounds");
   return gslice_array<value_type>(__gs, *this);
 }
 
@@ -2246,6 +2278,8 @@ inline __val_expr<__indirect_expr<const valarray<_Tp>&> > valarray<_Tp>::operato
 
 template <class _Tp>
 inline gslice_array<_Tp> valarray<_Tp>::operator[](gslice&& __gs) {
+  _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(std::__indices_in_bounds(__gs.__1d_.__begin_, __gs.__1d_.__end_, size()),
+                                      "valarray::operator[](gslice) selects elements out of bounds");
   return gslice_array<value_type>(std::move(__gs), *this);
 }
 
@@ -2258,6 +2292,8 @@ inline __val_expr<__mask_expr<const valarray<_Tp>&> > valarray<_Tp>::operator[](
 
 template <class _Tp>
 inline mask_array<_Tp> valarray<_Tp>::operator[](const valarray<bool>& __vb) {
+  _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(std::__mask_in_bounds(__vb.__begin_, __vb.__end_, size()),
+                                      "valarray::operator[](valarray<bool>) selects elements out of bounds");
   return mask_array<value_type>(__vb, *this);
 }
 
@@ -2270,6 +2306,8 @@ inline __val_expr<__mask_expr<const valarray<_Tp>&> > valarray<_Tp>::operator[](
 
 template <class _Tp>
 inline mask_array<_Tp> valarray<_Tp>::operator[](valarray<bool>&& __vb) {
+  _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(std::__mask_in_bounds(__vb.__begin_, __vb.__end_, size()),
+                                      "valarray::operator[](valarray<bool>) selects elements out of bounds");
   return mask_array<value_type>(std::move(__vb), *this);
 }
 
@@ -2283,6 +2321,8 @@ valarray<_Tp>::operator[](const valarray<size_t>& __vs) const {
 
 template <class _Tp>
 inline indirect_array<_Tp> valarray<_Tp>::operator[](const valarray<size_t>& __vs) {
+  _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(std::__indices_in_bounds(__vs.__begin_, __vs.__end_, size()),
+                                      "valarray::operator[](valarray<size_t>) selects elements out of bounds");
   return indirect_array<value_type>(__vs, *this);
 }
 
@@ -2295,6 +2335,8 @@ inline __val_expr<__indirect_expr<const valarray<_Tp>&> > valarray<_Tp>::operato
 
 template <class _Tp>
 inline indirect_array<_Tp> valarray<_Tp>::operator[](valarray<size_t>&& __vs) {
+  _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(std::__indices_in_bounds(__vs.__begin_, __vs.__end_, size()),
+                                      "valarray::operator[](valarray<size_t>) selects elements out of bounds");
   return indirect_array<value_type>(std::move(__vs), *this);
 }
 
diff --git a/libcxx/test/libcxx/numerics/numarray/assert.subscript.pass.cpp b/libcxx/test/libcxx/numerics/numarray/assert.subscript.pass.cpp
new file mode 100644
index 00000000000000..38d20f0c655aeb
--- /dev/null
+++ b/libcxx/test/libcxx/numerics/numarray/assert.subscript.pass.cpp
@@ -0,0 +1,115 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: can-test-hardening-assertions-fast
+
+// <valarray>
+
+// template<class T> class valarray;
+
+// slice_array<value_type> operator[](slice s);
+// gslice_array<value_type> operator[](const gslice& gs);
+// gslice_array<value_type> operator[](gslice&& gs);
+// mask_array<value_type> operator[](const valarray<bool>& vb);
+// mask_array<value_type> operator[](valarray<bool>&& vb);
+// indirect_array<value_type> operator[](const valarray<size_t>& vs);
+// indirect_array<value_type> operator[](valarray<size_t>&& vs);
+
+// Test that the selected elements must exist.
+
+#include <cstddef>
+#include <limits>
+#include <valarray>
+
+#include "check_assertion.h"
+
+std::gslice make_gslice(std::size_t start, std::size_t len0, std::size_t len1, std::size_t str0, std::size_t str1) {
+  std::size_t lengths[] = {len0, len1};
+  std::size_t strides[] = {str0, str1};
+  return std::gslice(start, std::valarray<std::size_t>(lengths, 2), std::valarray<std::size_t>(strides, 2));
+}
+
+int main(int, char**) {
+  std::valarray<int> array(10);
+
+  { // slice
+    // Check that there's no assertion on valid slices.
+    (void)array[std::slice(0, 10, 1)];
+    (void)array[std::slice(1, 5, 2)];
+    (void)array[std::slice(9, 3, 0)];
+    (void)array[std::slice(20, 0, 1)];
+
+    TEST_LIBCPP_ASSERT_FAILURE(
+        array[std::slice(0, 11, 1)], "valarray::operator[](slice) selects elements out of bounds");
+    TEST_LIBCPP_ASSERT_FAILURE(
+        array[std::slice(10, 1, 1)], "valarray::operator[](slice) selects elements out of bounds");
+    TEST_LIBCPP_ASSERT_FAILURE(
+        array[std::slice(4, 4, 2)], "valarray::operator[](slice) selects elements out of bounds");
+    TEST_LIBCPP_ASSERT_FAILURE(
+        array[std::slice(10, 3, 0)], "valarray::operator[](slice) selects elements out of bounds");
+
+    // (size() - 1) * stride() wraps around to 0, but the second element is far out of bounds.
+    const std::size_t half = std::size_t(1) << (std::numeric_limits<std::size_t>::digits - 1);
+    TEST_LIBCPP_ASSERT_FAILURE(
+        array[std::slice(0, 3, half)], "valarray::operator[](slice) selects elements out of bounds");
+  }
+
+  { // gslice
+    // Check that there's no assertion on valid generalized slices.
+    const std::gslice all = make_gslice(0, 2, 5, 5, 1); // 0 to 9
+    (void)array[all];
+    (void)array[make_gslice(3, 2, 2, 5, 1)];  // 3, 4, 8, 9
+    (void)array[make_gslice(9, 3, 2, 0, 0)];  // 9 six times
+    (void)array[make_gslice(20, 0, 2, 1, 1)]; // empty
+
+    const std::gslice past_end = make_gslice(6, 2, 2, 4, 1); // 6, 7, 10, 11
+    TEST_LIBCPP_ASSERT_FAILURE(array[past_end], "valarray::operator[](gslice) selects elements out of bounds");
+    TEST_LIBCPP_ASSERT_FAILURE(
+        array[make_gslice(1, 2, 5, 5, 1)], "valarray::operator[](gslice) selects elements out of bounds"); // 1 to 10
+  }
+
+  { // valarray<bool>
+    // Check that there's no assertion on valid masks, including masks of a different size that select no element
+    // past the end.
+    const std::valarray<bool> all(true, 10);
+    (void)array[all];
+    (void)array[std::valarray<bool>(true, 4)];
+    (void)array[std::valarray<bool>(false, 20)];
+    std::valarray<bool> longer(false, 12);
+    longer[9] = true;
+    (void)array[longer];
+
+    std::valarray<bool> one_past(false, 11);
+    one_past[10] = true;
+    TEST_LIBCPP_ASSERT_FAILURE(array[one_past], "valarray::operator[](valarray<bool>) selects elements out of bounds");
+    TEST_LIBCPP_ASSERT_FAILURE(
+        array[std::valarray<bool>(true, 12)], "valarray::operator[](valarray<bool>) selects elements out of bounds");
+
+    std::valarray<int> empty;
+    TEST_LIBCPP_ASSERT_FAILURE(
+        empty[std::valarray<bool>(true, 1)], "valarray::operator[](valarray<bool>) selects elements out of bounds");
+  }
+
+  { // valarray<size_t>
+    // Check that there's no assertion on valid indices.
+    std::size_t valid[] = {0, 9, 9, 3};
+    const std::valarray<std::size_t> indices(valid, 4);
+    (void)array[indices];
+    (void)array[std::valarray<std::size_t>()];
+
+    std::size_t past_end[] = {0, 9, 10, 3};
+    const std::valarray<std::size_t> past_end_indices(past_end, 4);
+    TEST_LIBCPP_ASSERT_FAILURE(
+        array[past_end_indices], "valarray::operator[](valarray<size_t>) selects elements out of bounds");
+    std::size_t max[] = {static_cast<std::size_t>(-1)};
+    TEST_LIBCPP_ASSERT_FAILURE(array[std::valarray<std::size_t>(max, 1)],
+                               "valarray::operator[](valarray<size_t>) selects elements out of bounds");
+  }
+
+  return 0;
+}



More information about the libcxx-commits mailing list