[libcxx-commits] [libcxx] [libc++][mdspan] Refactor bounds checking in `std::extents` (PR #197001)

via libcxx-commits libcxx-commits at lists.llvm.org
Thu May 14 21:27:18 PDT 2026


https://github.com/eiytoq updated https://github.com/llvm/llvm-project/pull/197001

>From 9fca71e6ffa9190f602e5e226a79265fa0c9dfef Mon Sep 17 00:00:00 2001
From: eiytoq <eiytoq at outlook.com>
Date: Tue, 12 May 2026 01:12:24 +0800
Subject: [PATCH 1/2] [libc++][mdspan] Refactor bounds checking in std::extents

---
 libcxx/include/__mdspan/extents.h | 78 ++++++++++++++++++-------------
 1 file changed, 46 insertions(+), 32 deletions(-)

diff --git a/libcxx/include/__mdspan/extents.h b/libcxx/include/__mdspan/extents.h
index 379c762ace3ed..c1e9cc6b4392d 100644
--- a/libcxx/include/__mdspan/extents.h
+++ b/libcxx/include/__mdspan/extents.h
@@ -29,6 +29,7 @@
 #include <__type_traits/is_signed.h>
 #include <__type_traits/make_unsigned.h>
 #include <__type_traits/remove_cvref.h>
+#include <__utility/as_const.h>
 #include <__utility/forward.h>
 #include <__utility/integer_sequence.h>
 #include <__utility/unreachable.h>
@@ -255,19 +256,6 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool __is_representable_as(_From __value) {
   return true;
 }
 
-template <integral _To, class... _From>
-_LIBCPP_HIDE_FROM_ABI constexpr bool __are_representable_as(_From... __values) {
-  return (__mdspan_detail::__is_representable_as<_To>(__values) && ... && true);
-}
-
-template <integral _To, class _From, size_t _Size>
-_LIBCPP_HIDE_FROM_ABI constexpr bool __are_representable_as(span<_From, _Size> __values) {
-  for (size_t __i = 0; __i < _Size; __i++)
-    if (!__mdspan_detail::__is_representable_as<_To>(__values[__i]))
-      return false;
-  return true;
-}
-
 } // namespace __mdspan_detail
 
 // ------------------------------------------------------------------
@@ -300,6 +288,42 @@ class extents {
       __mdspan_detail::__maybe_static_array<_IndexType, size_t, dynamic_extent, _Extents...>;
   [[no_unique_address]] _Values __vals_;
 
+  template <class _OtherIndexType>
+  _LIBCPP_HIDE_FROM_ABI static constexpr index_type __checked_index_cast(_OtherIndexType&& __value) noexcept {
+    using _OtherType = remove_cvref_t<_OtherIndexType>;
+    if constexpr (integral<_OtherType>) {
+      _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+          __mdspan_detail::__is_representable_as<index_type>(__value),
+          "extents ctor: arguments must be representable as index_type and nonnegative");
+      return static_cast<index_type>(__value);
+    } else {
+      auto __converted_val = static_cast<index_type>(std::forward<_OtherIndexType>(__value));
+      if constexpr (is_signed_v<index_type>) {
+        _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+            __converted_val >= 0, "extents ctor: arguments must be representable as index_type and nonnegative");
+      }
+      return __converted_val;
+    }
+  }
+
+  template <class... _OtherIndexTypes>
+  _LIBCPP_HIDE_FROM_ABI static constexpr _Values
+  __representability_checked_cast(_OtherIndexTypes&&... __values) noexcept {
+    return _Values{__checked_index_cast(std::forward<_OtherIndexTypes>(__values))...};
+  }
+
+  template <class _OtherIndexType, size_t _Size, size_t... _Idxs>
+  _LIBCPP_HIDE_FROM_ABI static constexpr _Values
+  __representability_checked_cast(const array<_OtherIndexType, _Size>& __exts, index_sequence<_Idxs...>) noexcept {
+    return __representability_checked_cast(as_const(__exts[_Idxs])...);
+  }
+
+  template <class _OtherIndexType, size_t _Size, size_t... _Idxs>
+  _LIBCPP_HIDE_FROM_ABI static constexpr _Values
+  __representability_checked_cast(const span<_OtherIndexType, _Size>& __exts, index_sequence<_Idxs...>) noexcept {
+    return __representability_checked_cast(as_const(__exts[_Idxs])...);
+  }
+
 public:
   // [mdspan.extents.obs], observers of multidimensional index space
   [[nodiscard]] _LIBCPP_HIDE_FROM_ABI static constexpr rank_type rank() noexcept { return __rank_; }
@@ -322,12 +346,9 @@ class extents {
              (is_nothrow_constructible_v<index_type, _OtherIndexTypes> && ...) &&
              (sizeof...(_OtherIndexTypes) == __rank_ || sizeof...(_OtherIndexTypes) == __rank_dynamic_))
   _LIBCPP_HIDE_FROM_ABI constexpr explicit extents(_OtherIndexTypes... __dynvals) noexcept
-      : __vals_(static_cast<index_type>(__dynvals)...) {
-    // Not catching this could lead to out of bounds errors later
-    // e.g. mdspan m(ptr, dextents<char, 1>(200u)); leads to an extent of -56 on m
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__mdspan_detail::__are_representable_as<index_type>(__dynvals...),
-                                        "extents ctor: arguments must be representable as index_type and nonnegative");
-  }
+      // Not catching this could lead to out of bounds errors later
+      // e.g. mdspan m(ptr, dextents<char, 1>(200u)); leads to an extent of -56 on m
+      : __vals_(__representability_checked_cast(__dynvals...)) {}
 
   template <class _OtherIndexType, size_t _Size>
     requires(is_convertible_v<const _OtherIndexType&, index_type> &&
@@ -335,12 +356,9 @@ class extents {
              (_Size == __rank_ || _Size == __rank_dynamic_))
   explicit(_Size != __rank_dynamic_)
       _LIBCPP_HIDE_FROM_ABI constexpr extents(const array<_OtherIndexType, _Size>& __exts) noexcept
-      : __vals_(span(__exts)) {
-    // Not catching this could lead to out of bounds errors later
-    // e.g. mdspan m(ptr, dextents<char, 1>(array<unsigned,1>(200))); leads to an extent of -56 on m
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__mdspan_detail::__are_representable_as<index_type>(span(__exts)),
-                                        "extents ctor: arguments must be representable as index_type and nonnegative");
-  }
+      // Not catching this could lead to out of bounds errors later
+      // e.g. mdspan m(ptr, dextents<char, 1>(200u)); leads to an extent of -56 on m
+      : __vals_(__representability_checked_cast(__exts, make_index_sequence<_Size>())) {}
 
   template <class _OtherIndexType, size_t _Size>
     requires(is_convertible_v<const _OtherIndexType&, index_type> &&
@@ -348,13 +366,9 @@ class extents {
              (_Size == __rank_ || _Size == __rank_dynamic_))
   explicit(_Size != __rank_dynamic_)
       _LIBCPP_HIDE_FROM_ABI constexpr extents(const span<_OtherIndexType, _Size>& __exts) noexcept
-      : __vals_(__exts) {
-    // Not catching this could lead to out of bounds errors later
-    // e.g. array a{200u}; mdspan<int, dextents<char,1>> m(ptr, extents(span<unsigned,1>(a))); leads to an extent of -56
-    // on m
-    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__mdspan_detail::__are_representable_as<index_type>(__exts),
-                                        "extents ctor: arguments must be representable as index_type and nonnegative");
-  }
+      // Not catching this could lead to out of bounds errors later
+      // e.g. mdspan m(ptr, dextents<char, 1>(200u)); leads to an extent of -56 on m
+      : __vals_(__representability_checked_cast(__exts, make_index_sequence<_Size>())) {}
 
 private:
   // Function to construct extents storage from other extents.

>From 28cb2c5b10d7fb116887f871f9ebedf354b999e1 Mon Sep 17 00:00:00 2001
From: eiytoq <eiytoq at outlook.com>
Date: Fri, 15 May 2026 12:27:10 +0800
Subject: [PATCH 2/2] Avoid ADL

Co-authored-by: A. Jiang <de34 at live.cn>
---
 libcxx/include/__mdspan/extents.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libcxx/include/__mdspan/extents.h b/libcxx/include/__mdspan/extents.h
index c1e9cc6b4392d..8fbaa84df0caa 100644
--- a/libcxx/include/__mdspan/extents.h
+++ b/libcxx/include/__mdspan/extents.h
@@ -321,7 +321,7 @@ class extents {
   template <class _OtherIndexType, size_t _Size, size_t... _Idxs>
   _LIBCPP_HIDE_FROM_ABI static constexpr _Values
   __representability_checked_cast(const span<_OtherIndexType, _Size>& __exts, index_sequence<_Idxs...>) noexcept {
-    return __representability_checked_cast(as_const(__exts[_Idxs])...);
+    return __representability_checked_cast(std::as_const(__exts[_Idxs])...);
   }
 
 public:



More information about the libcxx-commits mailing list