From 801d75905188ca9fe26c5c63cf586379c2d4ef3d Mon Sep 17 00:00:00 2001 From: Nicolas Morales Date: Wed, 7 Oct 2026 18:03:30 -0400 Subject: [PATCH 01/12] initial work on adding range_slice --- .../__p2630_bits/strided_slice.hpp | 7 ++ .../submdspan_canonicalize_slices.hpp | 82 +++++++++++++++++-- .../__p2630_bits/submdspan_extents.hpp | 22 +++++ 3 files changed, 103 insertions(+), 8 deletions(-) diff --git a/include/experimental/__p2630_bits/strided_slice.hpp b/include/experimental/__p2630_bits/strided_slice.hpp index 538b7a55..b849aa15 100644 --- a/include/experimental/__p2630_bits/strided_slice.hpp +++ b/include/experimental/__p2630_bits/strided_slice.hpp @@ -51,4 +51,11 @@ struct extent_slice { static_assert(detail::mdspan_is_index_like_v); }; +template> +struct range_slice { + MDSPAN_IMPL_NO_UNIQUE_ADDRESS FirstType first{}; + MDSPAN_IMPL_NO_UNIQUE_ADDRESS LastType last{}; + MDSPAN_IMPL_NO_UNIQUE_ADDRESS StrideType stride{}; +}; + } // MDSPAN_IMPL_STANDARD_NAMESPACE diff --git a/include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp b/include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp index fad36410..50ab259c 100644 --- a/include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp +++ b/include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp @@ -3,6 +3,7 @@ #pragma once +#include "experimental/__p2630_bits/constant_wrapper.hpp" #include "submdspan_extents.hpp" #include @@ -270,6 +271,64 @@ constexpr bool check_submdspan_slice_mandates( return (check_submdspan_slice_mandate(slices) && ... && true); } + +template +struct calculate_extents +{ + static constexpr auto value(SpanType span, StrideType stride) + { + return span != 0 ? 1 + divide(span - 1, stride) : 0; + } +}; + +template +struct calculate_extents, constant_wrapper> +{ + static constexpr auto value(constant_wrapper, constant_wrapper) + { + return cw(Span - 1, Stride) : 0>; + } +}; + + +template +struct calculate_stride +{ + template< class SpanType > + static constexpr auto value(SpanType span, StrideType stride) + { + return span != 0 ? stride : IndexType(1); + } +}; + +template +struct calculate_stride> +{ + template< class SpanType > + static constexpr auto value(SpanType, constant_wrapper) + { + return cw; + } +}; + + + +template +constexpr auto canonical_range_slice(OffsetType offset, SpanType span, StrideType stride) +{ + return extent_slice{ + offset, + calculate_extents::value(span, stride), + calculate_stride::value(span, stride) + }; +} + +template +constexpr auto canonical_range_slice(OffsetType offset, SpanType span) +{ + return canonical_range_slice(offset, span, cw); +} + // ============================================================ // canonical_slice: canonicalize a single slice // @@ -301,6 +360,15 @@ constexpr auto canonical_slice([[maybe_unused]] Slice s) /* .extent = */ extent, /* .stride = */ stride }; + } + else if constexpr (is_range_slice::value) { + auto c_first = canonical_index(std::move(s.first)); + auto c_last = canonical_index(std::move(s.last)); + return canonical_range_slice( + c_first, + subtract_ice(c_last, c_first), + canonical_index(std::move(s.stride)) + ); } else { // General pair-like case: structured binding into [first, last) auto [s_k0, s_k1] = std::move(s); @@ -309,14 +377,12 @@ constexpr auto canonical_slice([[maybe_unused]] Slice s) static_assert(std::is_convertible_v); static_assert(std::is_convertible_v); - auto offset = canonical_index(s_k0); - auto extent = subtract_ice(s_k0, s_k1); - auto stride = cw; - return extent_slice{ - /* .offset = */ offset, - /* .extent = */ extent, - /* .stride = */ stride - }; + auto c_first = canonical_index(s_k0); + auto c_last = canonical_index(s_k1); + return canonical_range_slice( + c_first, + subtract_ice(c_last, c_first) + ); } } diff --git a/include/experimental/__p2630_bits/submdspan_extents.hpp b/include/experimental/__p2630_bits/submdspan_extents.hpp index 0f096427..b8662b99 100644 --- a/include/experimental/__p2630_bits/submdspan_extents.hpp +++ b/include/experimental/__p2630_bits/submdspan_extents.hpp @@ -42,6 +42,12 @@ template struct is_extent_slice< extent_slice> : std::true_type {}; +template struct is_range_slice : std::false_type {}; + +template +struct is_range_slice< + range_slice> : std::true_type {}; + // first_of(slice): getting begin of slice specifier range MDSPAN_TEMPLATE_REQUIRES( class Integral, @@ -71,6 +77,13 @@ first_of(const extent_slice &r) { return r.offset; } +template +MDSPAN_INLINE_FUNCTION +constexpr FirstType +first_of(const range_slice &r) { + return r.first; +} + // last_of(slice): getting end of slice specifier range // We need however not just the slice but also the extents // of the original view and which rank from the extents. @@ -139,6 +152,15 @@ last_of(std::integral_constant, const Extents &, return r.extent; } +template +MDSPAN_INLINE_FUNCTION +constexpr auto +last_of(std::integral_constant, const Extents &, + const range_slice &r) { + return r.last - r.first; +} + // get stride of slices template MDSPAN_INLINE_FUNCTION From 58118863a5cfb1d9a6fda97976b4c3e862e54b52 Mon Sep 17 00:00:00 2001 From: Nicolas Morales Date: Wed, 7 Oct 2026 18:12:55 -0400 Subject: [PATCH 02/12] fix bug in subtract_ice, add stride_of and other utility for range_slice --- .../__p2630_bits/submdspan_canonicalize_slices.hpp | 4 ++-- include/experimental/__p2630_bits/submdspan_extents.hpp | 7 +++++++ include/experimental/__p2630_bits/submdspan_mapping.hpp | 9 +++++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp b/include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp index 50ab259c..0e2cd4d5 100644 --- a/include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp +++ b/include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp @@ -366,7 +366,7 @@ constexpr auto canonical_slice([[maybe_unused]] Slice s) auto c_last = canonical_index(std::move(s.last)); return canonical_range_slice( c_first, - subtract_ice(c_last, c_first), + subtract_ice(c_first, c_last), canonical_index(std::move(s.stride)) ); } else { @@ -381,7 +381,7 @@ constexpr auto canonical_slice([[maybe_unused]] Slice s) auto c_last = canonical_index(s_k1); return canonical_range_slice( c_first, - subtract_ice(c_last, c_first) + subtract_ice(c_first, c_last) ); } } diff --git a/include/experimental/__p2630_bits/submdspan_extents.hpp b/include/experimental/__p2630_bits/submdspan_extents.hpp index b8662b99..16c9cdd7 100644 --- a/include/experimental/__p2630_bits/submdspan_extents.hpp +++ b/include/experimental/__p2630_bits/submdspan_extents.hpp @@ -175,6 +175,13 @@ stride_of(const extent_slice &r) { return r.stride; } +template +MDSPAN_INLINE_FUNCTION +constexpr auto +stride_of(const range_slice &r) { + return r.stride; +} + // compute new static extent from range, preserving static knowledge template struct StaticExtentFromRange { constexpr static size_t value = dynamic_extent; diff --git a/include/experimental/__p2630_bits/submdspan_mapping.hpp b/include/experimental/__p2630_bits/submdspan_mapping.hpp index c0a7619e..5e9f7627 100644 --- a/include/experimental/__p2630_bits/submdspan_mapping.hpp +++ b/include/experimental/__p2630_bits/submdspan_mapping.hpp @@ -103,6 +103,15 @@ constexpr bool is_range_like_slice_v< IndexType > = (constant_wrapper::value == IndexType(1)); +template +constexpr bool is_range_like_slice_v< + range_slice< + FirstType, + LastType, + constant_wrapper>, + IndexType + > = (constant_wrapper::value == IndexType(1)); + template struct is_index_slice { constexpr static bool value = std::is_convertible_v; From ce6ee38343391af315963147e701a249ba24c3d7 Mon Sep 17 00:00:00 2001 From: Nicolas Morales Date: Mon, 12 Oct 2026 16:55:37 -0400 Subject: [PATCH 03/12] make submdspan_extents canonicalize slices; add static bounds checking for range_slice; add tests for range_slice --- .../submdspan_canonicalize_slices.hpp | 46 ++++++++++++++++++- .../__p2630_bits/submdspan_extents.hpp | 16 +------ tests/test_submdspan.cpp | 33 +++++++++++++ 3 files changed, 79 insertions(+), 16 deletions(-) diff --git a/include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp b/include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp index 0e2cd4d5..ec317f9f 100644 --- a/include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp +++ b/include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp @@ -3,8 +3,7 @@ #pragma once -#include "experimental/__p2630_bits/constant_wrapper.hpp" -#include "submdspan_extents.hpp" +#include "constant_wrapper.hpp" #include namespace MDSPAN_IMPL_STANDARD_NAMESPACE { @@ -13,6 +12,19 @@ namespace MDSPAN_IMPL_STANDARD_NAMESPACE { namespace detail { +// Helper for identifying extent_slice and range_slice +template struct is_extent_slice : std::false_type {}; + +template +struct is_extent_slice< + extent_slice> : std::true_type {}; + +template struct is_range_slice : std::false_type {}; + +template +struct is_range_slice< + range_slice> : std::true_type {}; + // ============================================================ // de_ice: extract the value of an integral-constant-like type // ============================================================ @@ -186,6 +198,36 @@ constexpr bool check_static_bounds() else { return true; } + } else if constexpr (is_range_slice::value) { + using first_type = decltype(std::declval().first); + using last_type = decltype(std::declval().last); + + if constexpr (is_integral_constant_like_v) { + if constexpr (de_ice(first_type{}) < 0) { + return false; + } + else if constexpr ( + Exts_k != dynamic_extent && + Exts_k < static_cast(de_ice(first_type{}))) + { + return false; + } + } + + if constexpr (is_integral_constant_like_v) { + if constexpr ((!is_integral_constant_like_v || (de_ice(first_type()) <= de_ice(last_type{}))) + && de_ice(last_type{}) < 0) { + return false; + } + else if constexpr ( + Exts_k != dynamic_extent && + Exts_k < static_cast(de_ice(last_type{}))) + { + return false; + } + } + + return true; } else { // General pair-like case: attempt to get the first and second elements. // If S_k cannot be structured-bound into two elements, this is ill-formed, diff --git a/include/experimental/__p2630_bits/submdspan_extents.hpp b/include/experimental/__p2630_bits/submdspan_extents.hpp index 16c9cdd7..081036cd 100644 --- a/include/experimental/__p2630_bits/submdspan_extents.hpp +++ b/include/experimental/__p2630_bits/submdspan_extents.hpp @@ -8,6 +8,7 @@ #include "constant_wrapper.hpp" #include "strided_slice.hpp" #include "../__p0009_bits/utility.hpp" +#include "submdspan_canonicalize_slices.hpp" namespace MDSPAN_IMPL_STANDARD_NAMESPACE { namespace detail { @@ -35,19 +36,6 @@ constexpr auto inv_map_rank(std::integral_constant, std::index_ slices...); } -// Helper for identifying strided_slice -template struct is_extent_slice : std::false_type {}; - -template -struct is_extent_slice< - extent_slice> : std::true_type {}; - -template struct is_range_slice : std::false_type {}; - -template -struct is_range_slice< - range_slice> : std::true_type {}; - // first_of(slice): getting begin of slice specifier range MDSPAN_TEMPLATE_REQUIRES( class Integral, @@ -304,6 +292,6 @@ constexpr auto submdspan_extents(const extents &src_exts, using ext_t = extents; return detail::extents_constructor::next_extent( - src_exts, slices...); + src_exts, detail::canonical_slice(slices)...); } } // namespace MDSPAN_IMPL_STANDARD_NAMESPACE diff --git a/tests/test_submdspan.cpp b/tests/test_submdspan.cpp index ffa86737..bff7fd4c 100644 --- a/tests/test_submdspan.cpp +++ b/tests/test_submdspan.cpp @@ -154,6 +154,11 @@ using submdspan_test_types = , std::tuple, args_t<10,20>, Kokkos::dextents, std::pair, Kokkos::extent_slice> , std::tuple, args_t<10,20>, Kokkos::dextents, Kokkos::extent_slice, std::pair> , std::tuple, args_t<10,20>, Kokkos::dextents, Kokkos::extent_slice, Kokkos::extent_slice> + , std::tuple, args_t<10>, Kokkos::dextents, Kokkos::range_slice> + , std::tuple, args_t<10,20>, Kokkos::dextents, Kokkos::range_slice, int> + , std::tuple, args_t<10,20>, Kokkos::dextents, std::pair, Kokkos::range_slice> + , std::tuple, args_t<10,20>, Kokkos::dextents, Kokkos::range_slice, std::pair> + , std::tuple, args_t<10,20>, Kokkos::dextents, Kokkos::range_slice, Kokkos::range_slice> , std::tuple, args_t<6,4,5,6,7,8>, Kokkos::extents, Kokkos::full_extent_t, int, std::pair, int, int, Kokkos::full_extent_t> , std::tuple, args_t<6,4,5,6,7,8>, Kokkos::extents, int, Kokkos::full_extent_t, std::pair, int, Kokkos::full_extent_t, int> // layout_right to layout_right_padded @@ -169,6 +174,12 @@ using submdspan_test_types = , std::tuple, args_t<10,20>, Kokkos::dextents, std::pair, Kokkos::extent_slice> , std::tuple, args_t<10,20>, Kokkos::dextents, Kokkos::extent_slice, std::pair> , std::tuple, args_t<10,20>, Kokkos::dextents, Kokkos::extent_slice, Kokkos::extent_slice> + , std::tuple, args_t<10>, Kokkos::dextents, Kokkos::range_slice> + , std::tuple, args_t<10>, Kokkos::extents, Kokkos::range_slice,std::integral_constant,std::integral_constant>> + , std::tuple, args_t<10,20>, Kokkos::dextents, Kokkos::range_slice, int> + , std::tuple, args_t<10,20>, Kokkos::dextents, std::pair, Kokkos::range_slice> + , std::tuple, args_t<10,20>, Kokkos::dextents, Kokkos::range_slice, std::pair> + , std::tuple, args_t<10,20>, Kokkos::dextents, Kokkos::range_slice, Kokkos::range_slice> , std::tuple, args_t<6,4,5,6,7,8>, Kokkos::extents, Kokkos::full_extent_t, int, std::pair, int, int, Kokkos::full_extent_t> , std::tuple, args_t<6,4,5,6,7,8>, Kokkos::extents, int, Kokkos::full_extent_t, std::pair, int, Kokkos::full_extent_t, int> // layout_left_padded to layout_left @@ -279,6 +290,15 @@ struct TestSubMDSpan< return Kokkos::extent_slice, std::integral_constant>{1,std::integral_constant(), std::integral_constant()}; } MDSPAN_INLINE_FUNCTION + static auto create_slice_arg(Kokkos::range_slice) { + return Kokkos::range_slice{1,8,2}; + } + template + MDSPAN_INLINE_FUNCTION + static auto create_slice_arg(Kokkos::range_slice,std::integral_constant, std::integral_constant>) { + return Kokkos::range_slice{std::integral_constant(),std::integral_constant(), std::integral_constant()}; + } + MDSPAN_INLINE_FUNCTION static auto create_slice_arg(Kokkos::full_extent_t) { return Kokkos::full_extent; } @@ -315,6 +335,19 @@ struct TestSubMDSpan< } template MDSPAN_INLINE_FUNCTION + static bool check_submdspan_match(int src_idx, int sub_idx, SrcMDSpan src_mds, SubMDSpan sub_mds, std::index_sequence, std::index_sequence, + Kokkos::range_slice p, SliceArgs ... slices) { + using idx_t = typename SubMDSpan::index_type; + return (sub_mds.extent(sub_idx)==static_cast(1 + (p.last - p.first - 1) / p.stride)) && check_submdspan_match(++src_idx, ++sub_idx, src_mds, sub_mds, std::index_sequence(), std::index_sequence(), slices...); + } + template + MDSPAN_INLINE_FUNCTION + static bool check_submdspan_match(int src_idx, int sub_idx, SrcMDSpan src_mds, SubMDSpan sub_mds, std::index_sequence, std::index_sequence, + Kokkos::range_slice,std::integral_constant,std::integral_constant>, SliceArgs ... slices) { + return (sub_mds.extent(sub_idx)==0) && check_submdspan_match(++src_idx, ++sub_idx, src_mds, sub_mds, std::index_sequence(), std::index_sequence(), slices...); + } + template + MDSPAN_INLINE_FUNCTION static bool check_submdspan_match(int src_idx, int sub_idx, SrcMDSpan src_mds, SubMDSpan sub_mds, std::index_sequence, std::index_sequence, Kokkos::full_extent_t, SliceArgs ... slices) { return (sub_mds.extent(sub_idx)==src_mds.extent(src_idx)) && check_submdspan_match(++src_idx, ++sub_idx, src_mds, sub_mds, std::index_sequence(), std::index_sequence(), slices...); } From c7e211997a96f05a30d342cbeb768fdf58bbe744 Mon Sep 17 00:00:00 2001 From: Nicolas Morales Date: Mon, 12 Oct 2026 17:02:18 -0400 Subject: [PATCH 04/12] add test for canonical slices with range_slice, rename Rank*_stride_slice_* to Rank*_extent_slice_* --- tests/test_canonicalize_slices.cpp | 45 ++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/tests/test_canonicalize_slices.cpp b/tests/test_canonicalize_slices.cpp index 7b60b1e9..8fd1cc2c 100644 --- a/tests/test_canonicalize_slices.cpp +++ b/tests/test_canonicalize_slices.cpp @@ -276,7 +276,7 @@ TEST(CanonicalizeSlices, Rank2_full) { MDSPAN_IMPL_STANDARD_NAMESPACE::extents{}, full, full); } -TEST(CanonicalizeSlices, Rank1_strided_slice_dynamic) { +TEST(CanonicalizeSlices, Rank1_extent_slice_dynamic) { using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::cw; using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::tuple; @@ -296,7 +296,27 @@ TEST(CanonicalizeSlices, Rank1_strided_slice_dynamic) { test_canonicalize_slices(expected_slices, exts, slice0); } -TEST(CanonicalizeSlices, Rank1_strided_slice_static) { +TEST(CanonicalizeSlices, Rank1_range_slice_dynamic) { + using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::cw; + using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::tuple; + + // strided_slice with all dynamic values + const auto slice0 = MDSPAN_IMPL_STANDARD_NAMESPACE::range_slice{ + /* .first = */ 1, + /* .last = */ 11, + /* .stride = */ 3 + }; + + const auto expected_slices = tuple{ + MDSPAN_IMPL_STANDARD_NAMESPACE::extent_slice{ + size_t(1), size_t(4), size_t(3) + } + }; + const auto exts = MDSPAN_IMPL_STANDARD_NAMESPACE::extents{}; + test_canonicalize_slices(expected_slices, exts, slice0); +} + +TEST(CanonicalizeSlices, Rank1_extent_slice_static) { using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::cw; using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::tuple; @@ -317,4 +337,25 @@ TEST(CanonicalizeSlices, Rank1_strided_slice_static) { test_canonicalize_slices(expected_slices, exts, slice0); } +TEST(CanonicalizeSlices, Rank1_range_slice_static) { + using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::cw; + using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::tuple; + + using offset_t = std::integral_constant; + using extent_t = std::integral_constant; + using stride_t = std::integral_constant; + const auto slice0 = + MDSPAN_IMPL_STANDARD_NAMESPACE::range_slice{}; + + const auto expected_slices = tuple{ + MDSPAN_IMPL_STANDARD_NAMESPACE::extent_slice< + decltype(cw), + decltype(cw), + decltype(cw) + >{cw, cw, cw} + }; + const auto exts = MDSPAN_IMPL_STANDARD_NAMESPACE::extents{}; + test_canonicalize_slices(expected_slices, exts, slice0); +} + } // namespace (anonymous) From 3b0253e3f7d0b758e386eca14d30db84fea6bd68 Mon Sep 17 00:00:00 2001 From: Nicolas Morales Date: Mon, 12 Oct 2026 17:09:26 -0400 Subject: [PATCH 05/12] add test for range_slice member, rename strided_slice tests to extent_slice --- tests/test_strided_slice.cpp | 63 ++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 13 deletions(-) diff --git a/tests/test_strided_slice.cpp b/tests/test_strided_slice.cpp index a9bd23a4..1da13804 100644 --- a/tests/test_strided_slice.cpp +++ b/tests/test_strided_slice.cpp @@ -9,7 +9,7 @@ namespace { template -void test_strided_slice(OffsetType offset, ExtentType extent, StrideType stride) +void test_extent_slice(OffsetType offset, ExtentType extent, StrideType stride) { // Some compilers are bad at CTAD for aggregates. Kokkos::extent_slice s{offset, extent, stride}; @@ -60,23 +60,60 @@ static_assert( my_integral_constant >); -TEST(StridedSlice, WellFormed) { - test_strided_slice(int(1), unsigned(10), long(3)); - test_strided_slice((signed char)(1), (unsigned short)(10), (unsigned long long)(3)); +TEST(ExtentSlice, WellFormed) { + test_extent_slice(int(1), unsigned(10), long(3)); + test_extent_slice((signed char)(1), (unsigned short)(10), (unsigned long long)(3)); - test_strided_slice(IC, unsigned(10), long(3)); - test_strided_slice(int(1), IC, long(3)); - test_strided_slice(int(1), unsigned(10), IC); + test_extent_slice(IC, unsigned(10), long(3)); + test_extent_slice(int(1), IC, long(3)); + test_extent_slice(int(1), unsigned(10), IC); using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::cw; - test_strided_slice(cw<1>, unsigned(10), long(3)); - test_strided_slice(int(1), cw, long(3)); - test_strided_slice(int(1), unsigned(10), cw); + test_extent_slice(cw<1>, unsigned(10), long(3)); + test_extent_slice(int(1), cw, long(3)); + test_extent_slice(int(1), unsigned(10), cw); - test_strided_slice(IC2, unsigned(10), long(3)); - test_strided_slice(int(1), IC2, long(3)); - test_strided_slice(int(1), unsigned(10), IC2); + test_extent_slice(IC2, unsigned(10), long(3)); + test_extent_slice(int(1), IC2, long(3)); + test_extent_slice(int(1), unsigned(10), IC2); +} + +template +void test_range_slice(FirstType first, LastType last, StrideType stride) +{ + // Some compilers are bad at CTAD for aggregates. + Kokkos::range_slice s{first, last, stride}; + + auto first2 = s.first; + static_assert(std::is_same_v); + auto last2 = s.last; + static_assert(std::is_same_v); + auto stride2 = s.stride; + static_assert(std::is_same_v); + + ASSERT_EQ(first2, first); + ASSERT_EQ(last2, last); + ASSERT_EQ(stride2, stride); +} + +TEST(RangeSlice, WellFormed) { + test_range_slice(int(1), unsigned(10), long(3)); + test_range_slice((signed char)(1), (unsigned short)(10), (unsigned long long)(3)); + + test_range_slice(IC, unsigned(10), long(3)); + test_range_slice(int(1), IC, long(3)); + test_range_slice(int(1), unsigned(10), IC); + + using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::cw; + + test_range_slice(cw<1>, unsigned(10), long(3)); + test_range_slice(int(1), cw, long(3)); + test_range_slice(int(1), unsigned(10), cw); + + test_range_slice(IC2, unsigned(10), long(3)); + test_range_slice(int(1), IC2, long(3)); + test_range_slice(int(1), unsigned(10), IC2); } } // namespace (anonymous) From 1b27de78f99f3b9222cec88865fa05a0818dea3b Mon Sep 17 00:00:00 2001 From: Nicolas Morales Date: Mon, 12 Oct 2026 17:16:02 -0400 Subject: [PATCH 06/12] remove unneeded first_of/last_of/stride_of for range_slice, remove use of size_t literal in submdspan_extents --- .../__p2630_bits/strided_slice.hpp | 2 +- .../__p2630_bits/submdspan_extents.hpp | 23 ------------------- 2 files changed, 1 insertion(+), 24 deletions(-) diff --git a/include/experimental/__p2630_bits/strided_slice.hpp b/include/experimental/__p2630_bits/strided_slice.hpp index b849aa15..d5e29b61 100644 --- a/include/experimental/__p2630_bits/strided_slice.hpp +++ b/include/experimental/__p2630_bits/strided_slice.hpp @@ -51,7 +51,7 @@ struct extent_slice { static_assert(detail::mdspan_is_index_like_v); }; -template> +template> struct range_slice { MDSPAN_IMPL_NO_UNIQUE_ADDRESS FirstType first{}; MDSPAN_IMPL_NO_UNIQUE_ADDRESS LastType last{}; diff --git a/include/experimental/__p2630_bits/submdspan_extents.hpp b/include/experimental/__p2630_bits/submdspan_extents.hpp index 081036cd..99c306fd 100644 --- a/include/experimental/__p2630_bits/submdspan_extents.hpp +++ b/include/experimental/__p2630_bits/submdspan_extents.hpp @@ -65,13 +65,6 @@ first_of(const extent_slice &r) { return r.offset; } -template -MDSPAN_INLINE_FUNCTION -constexpr FirstType -first_of(const range_slice &r) { - return r.first; -} - // last_of(slice): getting end of slice specifier range // We need however not just the slice but also the extents // of the original view and which rank from the extents. @@ -140,15 +133,6 @@ last_of(std::integral_constant, const Extents &, return r.extent; } -template -MDSPAN_INLINE_FUNCTION -constexpr auto -last_of(std::integral_constant, const Extents &, - const range_slice &r) { - return r.last - r.first; -} - // get stride of slices template MDSPAN_INLINE_FUNCTION @@ -163,13 +147,6 @@ stride_of(const extent_slice &r) { return r.stride; } -template -MDSPAN_INLINE_FUNCTION -constexpr auto -stride_of(const range_slice &r) { - return r.stride; -} - // compute new static extent from range, preserving static knowledge template struct StaticExtentFromRange { constexpr static size_t value = dynamic_extent; From 7e8a1eaff317aa14010c71c7ebf11197dafe4190 Mon Sep 17 00:00:00 2001 From: Nicolas Morales Date: Mon, 12 Oct 2026 17:30:43 -0400 Subject: [PATCH 07/12] try workaround for CTAD failure on some compliers --- .../__p2630_bits/submdspan_canonicalize_slices.hpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp b/include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp index ec317f9f..f8cee2bd 100644 --- a/include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp +++ b/include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp @@ -4,6 +4,7 @@ #pragma once #include "constant_wrapper.hpp" +#include "strided_slice.hpp" #include namespace MDSPAN_IMPL_STANDARD_NAMESPACE { @@ -354,15 +355,21 @@ struct calculate_stride> }; +// Some compilers do not do ctad properly with canonical_range_slice +template +constexpr auto make_extent_slice(OffsetType offset, ExtentType extent, StrideType stride) +{ + return extent_slice{offset, extent, stride}; +} template constexpr auto canonical_range_slice(OffsetType offset, SpanType span, StrideType stride) { - return extent_slice{ + return make_extent_slice( offset, calculate_extents::value(span, stride), calculate_stride::value(span, stride) - }; + ); } template From 0eadb6d4723b024e00630196182e5a8262fba878 Mon Sep 17 00:00:00 2001 From: Nicolas Morales Date: Mon, 12 Oct 2026 17:35:38 -0400 Subject: [PATCH 08/12] workaround CTAD issue with range_slice on some compilers --- tests/test_submdspan.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_submdspan.cpp b/tests/test_submdspan.cpp index bff7fd4c..54e09c61 100644 --- a/tests/test_submdspan.cpp +++ b/tests/test_submdspan.cpp @@ -296,7 +296,7 @@ struct TestSubMDSpan< template MDSPAN_INLINE_FUNCTION static auto create_slice_arg(Kokkos::range_slice,std::integral_constant, std::integral_constant>) { - return Kokkos::range_slice{std::integral_constant(),std::integral_constant(), std::integral_constant()}; + return Kokkos::range_slice, std::integral_constant, std::integral_constant>{std::integral_constant(),std::integral_constant(), std::integral_constant()}; } MDSPAN_INLINE_FUNCTION static auto create_slice_arg(Kokkos::full_extent_t) { From 3332576335a42e60bbe7a4aad0ae7c624ca215c1 Mon Sep 17 00:00:00 2001 From: Nicolas Morales Date: Mon, 12 Oct 2026 17:40:14 -0400 Subject: [PATCH 09/12] add some missing test cases for range_slice --- tests/test_submdspan.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/test_submdspan.cpp b/tests/test_submdspan.cpp index 54e09c61..9ca5eb08 100644 --- a/tests/test_submdspan.cpp +++ b/tests/test_submdspan.cpp @@ -209,6 +209,12 @@ using submdspan_test_types = , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20>, Kokkos::dextents, Kokkos::full_extent_t, Kokkos::extent_slice> , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20,30>, Kokkos::dextents, Kokkos::full_extent_t, Kokkos::extent_slice, Kokkos::full_extent_t> , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20,30>, Kokkos::dextents, Kokkos::full_extent_t, int, Kokkos::extent_slice> + , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10>, Kokkos::dextents, Kokkos::range_slice> + , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20>, Kokkos::dextents, Kokkos::range_slice, int> + , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20>, Kokkos::dextents, Kokkos::range_slice, Kokkos::full_extent_t> + , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20>, Kokkos::dextents, Kokkos::full_extent_t, Kokkos::range_slice> + , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20,30>, Kokkos::dextents, Kokkos::full_extent_t, Kokkos::range_slice, Kokkos::full_extent_t> + , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20,30>, Kokkos::dextents, Kokkos::full_extent_t, int, Kokkos::range_slice> , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20,30,40>, Kokkos::dextents, Kokkos::full_extent_t, Kokkos::full_extent_t, int, Kokkos::full_extent_t> // layout_right_padded to layout_right , std::tuple, Kokkos::layout_right, Kokkos::dextents, args_t<10,20>, Kokkos::dextents, int, int> @@ -237,6 +243,12 @@ using submdspan_test_types = , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20>, Kokkos::dextents, Kokkos::full_extent_t, Kokkos::extent_slice> , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20,30>, Kokkos::dextents, Kokkos::full_extent_t, Kokkos::extent_slice, Kokkos::full_extent_t> , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20,30>, Kokkos::dextents, Kokkos::extent_slice, int, Kokkos::full_extent_t> + , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10>, Kokkos::dextents, Kokkos::range_slice> + , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20>, Kokkos::dextents, int, Kokkos::range_slice> + , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20>, Kokkos::dextents, Kokkos::range_slice, Kokkos::full_extent_t> + , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20>, Kokkos::dextents, Kokkos::full_extent_t, Kokkos::range_slice> + , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20,30>, Kokkos::dextents, Kokkos::full_extent_t, Kokkos::range_slice, Kokkos::full_extent_t> + , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20,30>, Kokkos::dextents, Kokkos::range_slice, int, Kokkos::full_extent_t> , std::tuple, Kokkos::layout_stride, Kokkos::dextents, args_t<10,20,30,40>, Kokkos::dextents, Kokkos::full_extent_t, int, Kokkos::full_extent_t, Kokkos::full_extent_t> // Testing of customization point design , std::tuple, args_t<10>, Kokkos::dextents, Kokkos::full_extent_t> @@ -247,6 +259,8 @@ using submdspan_test_types = , std::tuple, args_t<10,20>, Kokkos::dextents, int, Kokkos::full_extent_t> , std::tuple, args_t<10>, Kokkos::dextents, Kokkos::extent_slice> , std::tuple, args_t<10,20>, Kokkos::dextents, Kokkos::extent_slice, int> + , std::tuple, args_t<10>, Kokkos::dextents, Kokkos::range_slice> + , std::tuple, args_t<10,20>, Kokkos::dextents, Kokkos::range_slice, int> >; template struct TestSubMDSpan; From 5a637f9dac508bb03c56677dd8f24dce8ff71b1e Mon Sep 17 00:00:00 2001 From: Nicolas Morales Date: Tue, 13 Oct 2026 16:33:49 -0400 Subject: [PATCH 10/12] make return type of canonical_range_slice more explicit --- .../__p2630_bits/submdspan_canonicalize_slices.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp b/include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp index f8cee2bd..c23d2701 100644 --- a/include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp +++ b/include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp @@ -318,9 +318,9 @@ constexpr bool check_submdspan_slice_mandates( template struct calculate_extents { - static constexpr auto value(SpanType span, StrideType stride) + static constexpr IndexType value(SpanType span, StrideType stride) { - return span != 0 ? 1 + divide(span - 1, stride) : 0; + return static_cast(span != 0 ? 1 + divide(span - 1, stride) : 0); } }; @@ -329,7 +329,7 @@ struct calculate_extents, constant_wrapper, constant_wrapper) { - return cw(Span - 1, Stride) : 0>; + return cw(Span != 0 ? 1 + divide(Span - 1, Stride) : 0)>; } }; From d71c1b2a55df365f0969fa56b2ce9856f5ea2d25 Mon Sep 17 00:00:00 2001 From: Nicolas Morales Date: Wed, 14 Oct 2026 15:24:59 -0400 Subject: [PATCH 11/12] fix accidental inversion of condition for range_slice static bounds check --- .../submdspan_canonicalize_slices.hpp | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp b/include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp index c23d2701..bae74f29 100644 --- a/include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp +++ b/include/experimental/__p2630_bits/submdspan_canonicalize_slices.hpp @@ -216,8 +216,8 @@ constexpr bool check_static_bounds() } if constexpr (is_integral_constant_like_v) { - if constexpr ((!is_integral_constant_like_v || (de_ice(first_type()) <= de_ice(last_type{}))) - && de_ice(last_type{}) < 0) { + if constexpr ((is_integral_constant_like_v && (de_ice(first_type()) > de_ice(last_type{}))) + || de_ice(last_type{}) < 0) { return false; } else if constexpr ( @@ -357,7 +357,8 @@ struct calculate_stride> // Some compilers do not do ctad properly with canonical_range_slice template -constexpr auto make_extent_slice(OffsetType offset, ExtentType extent, StrideType stride) +constexpr extent_slice +make_extent_slice(OffsetType offset, ExtentType extent, StrideType stride) { return extent_slice{offset, extent, stride}; } @@ -426,12 +427,14 @@ constexpr auto canonical_slice([[maybe_unused]] Slice s) static_assert(std::is_convertible_v); static_assert(std::is_convertible_v); - auto c_first = canonical_index(s_k0); - auto c_last = canonical_index(s_k1); - return canonical_range_slice( - c_first, - subtract_ice(c_first, c_last) - ); + auto offset = canonical_index(s_k0); + auto extent = subtract_ice(s_k0, s_k1); + auto stride = cw; + return extent_slice{ + /* .offset = */ offset, + /* .extent = */ extent, + /* .stride = */ stride + }; } } From 4b66ff95c722a31aa2fd72f98a54c02b0679da4a Mon Sep 17 00:00:00 2001 From: Nicolas Morales Date: Wed, 14 Oct 2026 15:30:53 -0400 Subject: [PATCH 12/12] fix linter errors --- include/experimental/__p2630_bits/strided_slice.hpp | 4 ++++ tests/test_canonicalize_slices.cpp | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/include/experimental/__p2630_bits/strided_slice.hpp b/include/experimental/__p2630_bits/strided_slice.hpp index d5e29b61..57ca5aba 100644 --- a/include/experimental/__p2630_bits/strided_slice.hpp +++ b/include/experimental/__p2630_bits/strided_slice.hpp @@ -56,6 +56,10 @@ struct range_slice { MDSPAN_IMPL_NO_UNIQUE_ADDRESS FirstType first{}; MDSPAN_IMPL_NO_UNIQUE_ADDRESS LastType last{}; MDSPAN_IMPL_NO_UNIQUE_ADDRESS StrideType stride{}; + + static_assert(detail::mdspan_is_index_like_v); + static_assert(detail::mdspan_is_index_like_v); + static_assert(detail::mdspan_is_index_like_v); }; } // MDSPAN_IMPL_STANDARD_NAMESPACE diff --git a/tests/test_canonicalize_slices.cpp b/tests/test_canonicalize_slices.cpp index 8fd1cc2c..fc20e7ae 100644 --- a/tests/test_canonicalize_slices.cpp +++ b/tests/test_canonicalize_slices.cpp @@ -280,7 +280,7 @@ TEST(CanonicalizeSlices, Rank1_extent_slice_dynamic) { using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::cw; using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::tuple; - // strided_slice with all dynamic values + // extent_slice with all dynamic values const auto slice0 = MDSPAN_IMPL_STANDARD_NAMESPACE::extent_slice{ /* .offset = */ 1, /* .extent = */ 4, @@ -300,7 +300,7 @@ TEST(CanonicalizeSlices, Rank1_range_slice_dynamic) { using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::cw; using MDSPAN_IMPL_STANDARD_NAMESPACE::detail::tuple; - // strided_slice with all dynamic values + // range_slice with all dynamic values const auto slice0 = MDSPAN_IMPL_STANDARD_NAMESPACE::range_slice{ /* .first = */ 1, /* .last = */ 11,