[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:48:24 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/5] [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/5] 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:
>From 1cb2c1c1b555aa300b837b0226cb9f104ee0bdbf Mon Sep 17 00:00:00 2001
From: eiytoq <eiytoq at outlook.com>
Date: Fri, 15 May 2026 12:27:29 +0800
Subject: [PATCH 3/5] Drop as_const
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 8fbaa84df0caa..52a742a976370 100644
--- a/libcxx/include/__mdspan/extents.h
+++ b/libcxx/include/__mdspan/extents.h
@@ -315,7 +315,7 @@ class extents {
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])...);
+ return __representability_checked_cast(__exts[_Idxs]...);
}
template <class _OtherIndexType, size_t _Size, size_t... _Idxs>
>From 3cca47ec51e557b0c1b109a29e0384ad8f095ebe Mon Sep 17 00:00:00 2001
From: eiytoq <eiytoq at outlook.com>
Date: Fri, 15 May 2026 12:45:27 +0800
Subject: [PATCH 4/5] Avoid make_unsigned_t<bool>
---
libcxx/include/__mdspan/extents.h | 2 +-
.../views/mdspan/extents/ctor_from_integral.pass.cpp | 11 +++++++++++
2 files changed, 12 insertions(+), 1 deletion(-)
diff --git a/libcxx/include/__mdspan/extents.h b/libcxx/include/__mdspan/extents.h
index 52a742a976370..b41ee45fce478 100644
--- a/libcxx/include/__mdspan/extents.h
+++ b/libcxx/include/__mdspan/extents.h
@@ -291,7 +291,7 @@ class extents {
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>) {
+ if constexpr (integral<_OtherType> && !std::same_as<_OtherType, bool>) {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
__mdspan_detail::__is_representable_as<index_type>(__value),
"extents ctor: arguments must be representable as index_type and nonnegative");
diff --git a/libcxx/test/std/containers/views/mdspan/extents/ctor_from_integral.pass.cpp b/libcxx/test/std/containers/views/mdspan/extents/ctor_from_integral.pass.cpp
index 24eb53b199338..f7ae8f130a4ce 100644
--- a/libcxx/test/std/containers/views/mdspan/extents/ctor_from_integral.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/extents/ctor_from_integral.pass.cpp
@@ -47,9 +47,20 @@ struct IntegralCtorTest {
}
};
+constexpr bool test_ctor_from_bool() {
+ constexpr size_t D = std::dynamic_extent;
+ std::extents<int, D, D> e(false, true);
+ assert(e.extent(0) == 0);
+ assert(e.extent(1) == 1);
+
+ return true;
+}
+
int main(int, char**) {
test_index_type_combo<IntegralCtorTest>();
static_assert(test_index_type_combo<IntegralCtorTest>());
+ test_ctor_from_bool();
+ static_assert(test_ctor_from_bool());
constexpr size_t D = std::dynamic_extent;
using E = std::extents<int, 1, D, 3, D>;
>From 39996af6fbccd0d9a84717a71a22e872dcdc2c61 Mon Sep 17 00:00:00 2001
From: eiytoq <eiytoq at outlook.com>
Date: Fri, 15 May 2026 12:48:08 +0800
Subject: [PATCH 5/5] Remove std::
---
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 b41ee45fce478..f2a23b5c1cba8 100644
--- a/libcxx/include/__mdspan/extents.h
+++ b/libcxx/include/__mdspan/extents.h
@@ -291,7 +291,7 @@ class extents {
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> && !std::same_as<_OtherType, bool>) {
+ if constexpr (integral<_OtherType> && !same_as<_OtherType, bool>) {
_LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
__mdspan_detail::__is_representable_as<index_type>(__value),
"extents ctor: arguments must be representable as index_type and nonnegative");
More information about the libcxx-commits
mailing list