From: Luc Grosheintz <[email protected]>
Implements `submdspan` and `submdspan_mapping` for layout_left as
described in P3663 (Future proofing mdspan).
When computing the offset of the submdspan, one must check that the
lower bound of the slice range isn't out-of-range. There's a few
cases when the lower bound is never out-of-range:
- full_extent and exts.extent(k) != 0,
- collapsing slice types.
If those conditions are known to hold, no checks are generated.
Similarly, if all slices are full_extent, there's no need to call
mapping(0,...,0) for standardized mappings.
The implementation prepares to use the symmetry between layout_left and
layout_right and introduces concepts like a "layout side", i.e. left,
right or unknown/strided.
The tests use an iterator to replace nested for-loops. Which also makes
it easier to write the core test logic in a rank-independent manner.
PR libstdc++/110352
libstdc++-v3/ChangeLog:
* include/std/mdspan (__mdspan::__is_submdspan_mapping_result)
(__mdspan::__submdspan_mapping_result, __mdspan::__fwd_prod)
(__mdspan::__acceptable_slice_type, __mdspan::__slice_begin)
(__mdspan::__suboffset, __mdspan::_LayoutSide, __mdspan::__mapping_side)
(__mdspan::_StridesTrait, __mdspan::__substrides_generic)
(__mdspan::__substrides_standardized, __mdspan::__substrides)
(__mdspan::__is_unit_stride_slice, __mdspan::_SliceKind)
(__mdspan::__make_slice_kind, __mdspan::__make_slice_kind_array)
(__mdspan::__is_block, __mdspan::__padded_block_begin_generic)
(__mdspan::__padded_block_begin, __mpdspan::_SubMdspanMapping)
(__mdspan::__submdspan_mapping_impl): Define.
(__mdspan::__dynamic_slice_extent, __mdspan::__static_slice_extent)
(__mdspan::__subextents): Move eariel in the file.
(layout_left::mapping::submdspan_mapping, __mdspan::__sliceable_mapping)
(__mdspan::__submapping, submdspan): Define.
* src/c++23/std.cc.in: Add submdspan.
* testsuite/23_containers/mdspan/submdspan/generic.cc: New test.
* testsuite/23_containers/mdspan/submdspan/selections/left.cc:
Instantiate selection tests for layout_left.
* testsuite/23_containers/mdspan/submdspan/selections/testcases.h:
Generic
tests different selections.
* testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc: New
test.
* testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc: New test.
Reviewed-by: Tomasz Kamiński <[email protected]>
Signed-off-by: Luc Grosheintz <[email protected]>
---
v7:
- list all new entries in changes.
- move submdspan_mapping poison pill before concept,
- use const mapping as argument to concept
- refactor __submdspan into __submapping funciton.
Tested *mdspan* locally.
libstdc++-v3/include/std/mdspan | 559 ++++++++++++++++--
libstdc++-v3/src/c++23/std.cc.in | 2 +-
.../23_containers/mdspan/submdspan/generic.cc | 71 +++
.../mdspan/submdspan/selections/left.cc | 9 +
.../mdspan/submdspan/selections/testcases.h | 360 +++++++++++
.../mdspan/submdspan/submdspan_mapping.cc | 136 +++++
.../mdspan/submdspan/submdspan_neg.cc | 104 ++++
7 files changed, 1183 insertions(+), 58 deletions(-)
create mode 100644
libstdc++-v3/testsuite/23_containers/mdspan/submdspan/generic.cc
create mode 100644
libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/left.cc
create mode 100644
libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/testcases.h
create mode 100644
libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc
create mode 100644
libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc
diff --git a/libstdc++-v3/include/std/mdspan b/libstdc++-v3/include/std/mdspan
index f0f6630b472..0d477b7fa9e 100644
--- a/libstdc++-v3/include/std/mdspan
+++ b/libstdc++-v3/include/std/mdspan
@@ -374,6 +374,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
[[no_unique_address]] _Mapping mapping = _Mapping();
size_t offset{};
};
+
+ template<typename _Tp>
+ constexpr bool __is_submdspan_mapping_result = false;
+
+ template<typename _Mapping>
+ constexpr bool
__is_submdspan_mapping_result<submdspan_mapping_result<_Mapping>> = true;
+
+ template<typename _Mapping>
+ concept __submdspan_mapping_result =
__is_submdspan_mapping_result<_Mapping>;
+
#endif // __glibcxx_submdspan
template<typename _IndexType, size_t... _Extents>
@@ -589,6 +599,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
}
+ template<typename _IndexType, size_t _Nm>
+ consteval _IndexType
+ __fwd_prod(span<const _IndexType, _Nm> __values)
+ {
+ _IndexType __ret = 1;
+ for(auto __value : __values)
+ __ret *= __value;
+ return __ret;
+ }
+
// Preconditions: _r < _Extents::rank()
template<typename _Extents>
constexpr typename _Extents::index_type
@@ -866,6 +886,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__exts.extent(_Index)};
}
+ template<typename _Slice, typename _IndexType>
+ concept __acceptable_slice_type = same_as<_Slice, full_extent_t>
+ || same_as<_Slice, _IndexType> || __is_constant_wrapper<_Slice>
+ || __is_strided_slice<_Slice>;
+
template<typename _IndexType, typename... _Slices>
consteval auto
__subrank()
@@ -890,6 +915,443 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__map[__i++] = __k;
return __map;
}
+
+ template<typename _Slice>
+ constexpr auto
+ __slice_begin(_Slice __slice)
+ {
+ if constexpr (same_as<_Slice, full_extent_t>)
+ return 0;
+ else if constexpr (__is_strided_slice<_Slice>)
+ return __slice.offset;
+ else
+ return __slice; // collapsing slice
+ }
+
+ template<typename _Mapping, typename... _Slices>
+ constexpr size_t
+ __suboffset(const _Mapping& __mapping, const _Slices&... __slices)
+ {
+ using _IndexType = typename _Mapping::index_type;
+ auto __any_past_the_end = [&]<size_t... _Is>(index_sequence<_Is...>)
+ {
+ auto __is_past_the_end = [](const auto& __slice, const auto& __ext)
+ {
+ using _Slice = remove_cvref_t<decltype(__slice)>;
+ if constexpr (is_convertible_v<_Slice, _IndexType>)
+ return false;
+ else if constexpr (same_as<_Slice, full_extent_t>
+ && __ext.static_extent(0) != 0
+ && __ext.static_extent(0) != dynamic_extent)
+ return false;
+ else
+ return __mdspan::__slice_begin(__slice) == __ext.extent(0);
+ };
+
+ const auto& __exts = __mapping.extents();
+ return ((__is_past_the_end(__slices...[_Is],
+ __mdspan::__extract_extent<_Is>(__exts)))
|| ...);
+ };
+
+ if constexpr ((same_as<_Slices, full_extent_t> && ...))
+ return __mdspan::__offset(__mapping);
+
+ if (__any_past_the_end(std::make_index_sequence<sizeof...(__slices)>()))
+ return __mapping.required_span_size();
+ return __mapping(__mdspan::__slice_begin(__slices)...);
+ }
+
+ template<typename _IndexType, size_t _Extent, typename _Slice>
+ consteval size_t
+ __static_slice_extent()
+ {
+ if constexpr (same_as<_Slice, full_extent_t>)
+ return _Extent;
+ else if constexpr (same_as<_Slice, constant_wrapper<_IndexType(0)>>)
+ return 0;
+ else if constexpr (__is_constant_wrapper<typename _Slice::extent_type>
+ && __is_constant_wrapper<typename _Slice::stride_type>)
+ return 1 + ((typename _Slice::extent_type{}) - 1)
+ / (typename _Slice::stride_type{});
+ else
+ return dynamic_extent;
+ }
+
+ template<size_t _K, typename _Extents, typename _Slice>
+ constexpr typename _Extents::index_type
+ __dynamic_slice_extent(const _Extents& __exts, _Slice __slice)
+ {
+ if constexpr (__is_strided_slice<_Slice>)
+ return __slice.extent == 0 ? 0 : 1 + (__slice.extent - 1) /
__slice.stride;
+ else
+ return __exts.extent(_K);
+ }
+
+ template<typename _IndexType, size_t... _Extents, typename... _Slices>
+ requires (sizeof...(_Slices) == sizeof...(_Extents))
+ constexpr auto
+ __subextents(const extents<_IndexType, _Extents...>& __exts,
+ _Slices... __slices)
+ {
+ constexpr auto __inv_map = __mdspan::__inv_map_rank<_IndexType,
_Slices...>();
+ auto __impl = [&]<size_t... _Indices>(std::index_sequence<_Indices...>)
+ {
+ using _SubExts = extents<_IndexType,
+ __mdspan::__static_slice_extent<_IndexType,
+ _Extents...[__inv_map[_Indices]],
+ _Slices...[__inv_map[_Indices]]>()...>;
+ if constexpr (_SubExts::rank_dynamic() == 0)
+ return _SubExts{};
+ else
+ {
+ using _StaticSubExtents = __mdspan::_StaticExtents<
+ __mdspan::__static_extents<_SubExts>()>;
+ auto __create = [&]<size_t... _Is>(std::index_sequence<_Is...>)
+ {
+ constexpr auto __slice_idx = [__inv_map](size_t __i) consteval
+ {
+ return
__inv_map[_StaticSubExtents::_S_dynamic_index_inv(__i)];
+ };
+
+ return
_SubExts{__mdspan::__dynamic_slice_extent<__slice_idx(_Is)>(
+ __exts, __slices...[__slice_idx(_Is)])...};
+ };
+ constexpr auto __dyn_subrank = _SubExts::rank_dynamic();
+ return __create(std::make_index_sequence<__dyn_subrank>());
+ }
+ };
+
+ return __impl(std::make_index_sequence<__inv_map.size()>());
+ }
+
+ enum class _LayoutSide
+ {
+ __left,
+ __right,
+ __unknown
+ };
+
+ template<typename _Mapping>
+ consteval _LayoutSide
+ __mapping_side()
+ {
+ if constexpr (__is_left_padded_mapping<_Mapping>
+ || __mapping_of<layout_left, _Mapping>)
+ return _LayoutSide::__left;
+ if constexpr (__is_right_padded_mapping<_Mapping>
+ || __mapping_of<layout_right, _Mapping>)
+ return _LayoutSide::__right;
+ else
+ return _LayoutSide::__unknown;
+ }
+
+ template<_LayoutSide _Side, size_t _Rank>
+ struct _StridesTrait
+ {
+ static constexpr const _LayoutSide _S_side = _Side;
+
+ static constexpr size_t
+ _S_idx(size_t __k) noexcept
+ {
+ if constexpr (_Side == _LayoutSide::__left)
+ return __k;
+ else
+ return _Rank - 1 - __k;
+ }
+
+ // Unifies the formulas for computing strides for padded and unpadded
+ // layouts.
+ template<typename _Mapping>
+ static constexpr typename _Mapping::index_type
+ _S_padded_extent(const _Mapping& __mapping, size_t __k)
+ {
+ if (__k == 0)
+ return __mapping.stride(_S_idx(1));
+ else
+ return __mapping.extents().extent(_S_idx(__k));
+ }
+
+ template<typename _IndexType, typename... _Slices>
+ static consteval auto
+ _S_inv_map()
+ {
+ static_assert(_Side != _LayoutSide::__unknown);
+ auto __impl = [&]<size_t... _Is>(std::index_sequence<_Is...>)
+ {
+ return __mdspan::__inv_map_rank<_IndexType,
_Slices...[_S_idx(_Is)]...>();
+ };
+ return __impl(std::make_index_sequence<_Rank>());
+ }
+ };
+
+ template<typename _SubExts, typename _Mapping, typename... _Slices>
+ constexpr auto
+ __substrides_generic(const _Mapping& __mapping, const _Slices&...
__slices)
+ {
+ using _IndexType = typename _Mapping::index_type;
+ if constexpr (_SubExts::rank() == 0)
+ return array<_IndexType, _SubExts::rank()>{};
+ else
+ {
+ auto __stride = [&__mapping](size_t __k, auto __slice) -> _IndexType
+ {
+ if constexpr (__is_strided_slice<decltype(__slice)>)
+ if (__slice.stride < __slice.extent)
+ return __mapping.stride(__k) * __slice.stride;
+ return __mapping.stride(__k);
+ };
+
+ auto __impl = [&]<size_t... _Is>(std::index_sequence<_Is...>)
+ {
+ constexpr auto __inv_map
+ = __mdspan::__inv_map_rank<_IndexType, _Slices...>();
+ return array<_IndexType, _SubExts::rank()>{
+ __stride(__inv_map[_Is], __slices...[__inv_map[_Is]])...};
+ };
+ return __impl(std::make_index_sequence<_SubExts::rank()>());
+ }
+ };
+
+ template<typename _SubExts, typename _Mapping, typename... _Slices>
+ constexpr auto
+ __substrides_standardized(const _Mapping& __mapping,
+ const _Slices&... __slices)
+ {
+ using _IndexType = typename _Mapping::index_type;
+ using _Trait = _StridesTrait<__mapping_side<_Mapping>(),
+ _Mapping::extents_type::rank()>;
+ using _SubTrait = _StridesTrait<__mapping_side<_Mapping>(),
_SubExts::rank()>;
+
+ constexpr size_t __sub_rank = _SubExts::rank();
+
+ std::array<_IndexType, __sub_rank> __ret;
+ if constexpr (__sub_rank > 0)
+ {
+ constexpr auto __inv_map
+ = _Trait::template _S_inv_map<_IndexType, _Slices...>();
+ auto __loop = [&]<size_t... _Ks>(std::index_sequence<_Ks...>)
+ {
+ size_t __i0 = 0;
+ size_t __stride = 1;
+ auto __body = [&](size_t __k, auto __slice)
+ {
+ for (size_t __i = __i0; __i < __inv_map[__k]; ++__i)
+ __stride *= _Trait::_S_padded_extent(__mapping, __i);
+
+ size_t __krev = _SubTrait::_S_idx(__k);
+ if constexpr (__is_strided_slice<decltype(__slice)>)
+ {
+ if (__slice.stride < __slice.extent)
+ __ret[__krev] = __stride * __slice.stride;
+ else
+ __ret[__krev] = __stride;
+ }
+ else
+ __ret[__krev] = __stride;
+
+ __i0 = __inv_map[__k];
+ };
+
+ ((__body(_Ks, __slices...[_Trait::_S_idx(__inv_map[_Ks])])),...);
+ };
+ __loop(std::make_index_sequence<__sub_rank>());
+ }
+ return __ret;
+ }
+
+
+ template<typename _SubExts, typename _Mapping, typename... _Slices>
+ constexpr auto
+ __substrides(const _Mapping& __mapping, const _Slices&... __slices)
+ {
+ if constexpr (__mdspan::__mapping_side<_Mapping>() ==
_LayoutSide::__unknown)
+ return __mdspan::__substrides_generic<_SubExts>(__mapping,
__slices...);
+ else
+ return __mdspan::__substrides_standardized<_SubExts>(__mapping,
__slices...);
+ }
+
+ template<typename _Slice>
+ concept __is_unit_stride_slice = (__mdspan::__is_strided_slice<_Slice>
+ && __mdspan::__is_constant_wrapper<typename _Slice::stride_type>
+ && _Slice::stride_type::value == 1)
+ || std::same_as<_Slice, full_extent_t>;
+
+ // These are (forced) exclusive categories:
+ // - full & collapsing: obvious,
+ // - unit_strided_slice: strided_slice{a, b, cw<1>}, but not `full`,
+ // - strided_slice: strided_slice{a, b, c} with c != cw<1>.
+ enum class _SliceKind
+ {
+ __strided_slice,
+ __unit_strided_slice,
+ __full,
+ __collapsing
+ };
+
+ template<typename _Slice>
+ consteval _SliceKind
+ __make_slice_kind()
+ {
+ if constexpr (std::same_as<_Slice, full_extent_t>)
+ return _SliceKind::__full;
+ else if constexpr (__mdspan::__is_strided_slice<_Slice>)
+ {
+ if constexpr (__mdspan::__is_unit_stride_slice<_Slice>)
+ return _SliceKind::__unit_strided_slice;
+ else
+ return _SliceKind::__strided_slice;
+ }
+ else
+ return _SliceKind::__collapsing;
+ }
+
+ template<typename... _Slices>
+ consteval array<_SliceKind, sizeof...(_Slices)>
+ __make_slice_kind_array()
+ {
+ return array<_SliceKind, sizeof...(_Slices)>{
+ __mdspan::__make_slice_kind<_Slices>()...};
+ }
+
+ // __block_size - 1
+ // [full, ..., full, unit_slice , *]
+ consteval bool
+ __is_block(span<const _SliceKind> __slice_kinds, size_t __block_size)
+ {
+ if (__block_size == 0)
+ return false;
+
+ if (__block_size > __slice_kinds.size())
+ return false;
+
+ for (size_t __i = 0; __i < __block_size - 1; ++__i)
+ if (__slice_kinds[__i] != _SliceKind::__full)
+ return false;
+
+ auto __last = __slice_kinds[__block_size - 1];
+ return __last == _SliceKind::__full
+ || __last == _SliceKind::__unit_strided_slice;
+ }
+
+ // __u __u + __sub_rank-2
+ // [unit_slice, i, ..., k, full, ..., full, unit_slice, *]
+ static consteval size_t
+ __padded_block_begin_generic(span<const _SliceKind> __slice_kinds,
+ size_t __sub_rank)
+ {
+ if (__slice_kinds[0] != _SliceKind::__full
+ && __slice_kinds[0] != _SliceKind::__unit_strided_slice)
+ return dynamic_extent;
+ else if (__slice_kinds.size() == 1)
+ return dynamic_extent;
+ else
+ {
+ size_t __u = 1;
+ while(__u < __slice_kinds.size()
+ && __slice_kinds[__u] == _SliceKind::__collapsing)
+ ++__u;
+
+ if (__mdspan::__is_block(__slice_kinds.subspan(__u), __sub_rank -1))
+ return __u;
+ return dynamic_extent;
+ }
+ }
+
+ template<_LayoutSide _Side, size_t _Nm>
+ static consteval size_t
+ __padded_block_begin(span<const _SliceKind, _Nm> __slice_kinds, size_t
__sub_rank)
+ {
+ if constexpr (_Side == _LayoutSide::__left)
+ return __mdspan::__padded_block_begin_generic(__slice_kinds,
__sub_rank);
+ else
+ {
+ std::array<_SliceKind, _Nm> __rev_slices;
+ for(size_t __i = 0; __i < _Nm; ++__i)
+ __rev_slices[__i] = __slice_kinds[_Nm - 1 - __i];
+ auto __rev_slice_kinds = span<const _SliceKind>(__rev_slices);
+
+ auto __u = __mdspan::__padded_block_begin_generic(__rev_slice_kinds,
+ __sub_rank);
+ return __u == dynamic_extent ? dynamic_extent : _Nm - 1 - __u;
+ }
+ }
+
+ template<_LayoutSide _Side>
+ struct _SubMdspanMapping;
+
+ template<>
+ struct _SubMdspanMapping<_LayoutSide::__left>
+ {
+ using _Layout = layout_left;
+ template<size_t _Pad> using _PaddedLayout = layout_left_padded<_Pad>;
+
+ template<typename _Mapping, size_t _Us>
+ static consteval size_t
+ _S_pad()
+ {
+ using _Extents = typename _Mapping::extents_type;
+ constexpr auto __sta_exts = __mdspan::__static_extents<_Extents>(0,
_Us);
+ if constexpr (!__mdspan::__all_static(__sta_exts))
+ return dynamic_extent;
+ else
+ return __mdspan::__fwd_prod(__sta_exts);
+ }
+
+ template<size_t _Nm>
+ static consteval bool
+ _S_is_unpadded_submdspan(span<const _SliceKind, _Nm> __slice_kinds,
size_t __sub_rank)
+ { return __mdspan::__is_block(__slice_kinds, __sub_rank); }
+ };
+
+ template<typename _Mapping>
+ constexpr auto
+ __submdspan_mapping_impl(const _Mapping& __mapping)
+ { return submdspan_mapping_result{__mapping, 0}; }
+
+ template<typename _Mapping, typename... _Slices>
+ requires (sizeof...(_Slices) > 0)
+ constexpr auto
+ __submdspan_mapping_impl(const _Mapping& __mapping, _Slices... __slices)
+ {
+ using _IndexType = typename _Mapping::index_type;
+ static_assert((__acceptable_slice_type<_Slices, _IndexType> && ...));
+
+ constexpr auto __side = __mdspan::__mapping_side<_Mapping>();
+ constexpr auto __rank = sizeof...(_Slices);
+ using _Trait = _SubMdspanMapping<__side>;
+ using _SliceView = span<const _SliceKind, __rank>;
+
+ constexpr auto __slice_kinds =
__mdspan::__make_slice_kind_array<_Slices...>();
+ auto __offset = __mdspan::__suboffset(__mapping, __slices...);
+ auto __sub_exts = __mdspan::__subextents(__mapping.extents(),
__slices...);
+ using _SubExts = decltype(__sub_exts);
+ constexpr auto __sub_rank = _SubExts::rank();
+ if constexpr (__sub_rank == 0)
+ return submdspan_mapping_result{
+ typename _Trait::_Layout::mapping(__sub_exts), __offset};
+ else if constexpr (_Trait::_S_is_unpadded_submdspan(
+ _SliceView(__slice_kinds), __sub_rank))
+ return submdspan_mapping_result{
+ typename _Trait::_Layout::mapping(__sub_exts), __offset};
+ else if constexpr (
+ constexpr auto __u = __padded_block_begin<__side>(
+ _SliceView(__slice_kinds), __sub_rank);
+ __u != dynamic_extent)
+ {
+ constexpr auto __pad = _Trait::template _S_pad<_Mapping, __u>();
+ using _Layout = typename _Trait::template _PaddedLayout<__pad>;
+ return submdspan_mapping_result{
+ typename _Layout::mapping(__sub_exts, __mapping.stride(__u)),
+ __offset};
+ }
+ else
+ {
+ auto __sub_strides
+ = __mdspan::__substrides<_SubExts>(__mapping, __slices...);
+ return submdspan_mapping_result{
+ layout_stride::mapping(__sub_exts, __sub_strides), __offset};
+ }
+ }
#endif // __glibcxx_submdspan
}
@@ -1032,6 +1494,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__glibcxx_assert(__mdspan::__is_representable_extents(_M_extents));
}
+#if __glibcxx_submdspan
+ template<typename... _Slices>
+ requires (extents_type::rank() == sizeof...(_Slices))
+ friend constexpr auto
+ submdspan_mapping(const mapping& __mapping, _Slices... __slices)
+ { return __mdspan::__submdspan_mapping_impl(__mapping, __slices...); }
+#endif // __glibcxx_submdspan
+
[[no_unique_address]] extents_type _M_extents{};
};
@@ -2700,68 +3170,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__impl(make_index_sequence<__rank>());
}
- template<typename _IndexType, size_t _Extent, typename _Slice>
- consteval size_t
- __static_slice_extent()
- {
- if constexpr (same_as<_Slice, full_extent_t>)
- return _Extent;
- else if constexpr (same_as<_Slice, constant_wrapper<_IndexType(0)>>)
- return 0;
- else if constexpr (__is_constant_wrapper<typename _Slice::extent_type>
- && __is_constant_wrapper<typename _Slice::stride_type>)
- return 1 + ((typename _Slice::extent_type{}) - 1)
- / (typename _Slice::stride_type{});
- else
- return dynamic_extent;
- }
+ template<typename _Slice>
+ using __full_extent_t = std::full_extent_t;
- template<size_t _K, typename _Extents, typename _Slice>
- constexpr typename _Extents::index_type
- __dynamic_slice_extent(const _Extents& __exts, _Slice __slice)
+ // Enables ADL-only calls from submdspan.
+ void submdspan_mapping() = delete;
+
+ template<typename _Mapping, typename... _Slices>
+ concept __sliceable_mapping = requires(const _Mapping __m, _Slices...
__slices)
{
- if constexpr (__is_strided_slice<_Slice>)
- return __slice.extent == 0 ? 0 : 1 + (__slice.extent - 1) /
__slice.stride;
- else
- return __exts.extent(_K);
- }
+ { submdspan_mapping(__m, __slices...) } -> __submdspan_mapping_result;
+ };
- template<typename _IndexType, size_t... _Extents, typename... _Slices>
- requires (sizeof...(_Slices) == sizeof...(_Extents))
+ template<typename _Mapping, typename... _Slices>
constexpr auto
- __subextents(const extents<_IndexType, _Extents...>& __exts,
- _Slices... __slices)
+ __submapping(const _Mapping& __mapping, _Slices... __slices)
{
- constexpr auto __inv_map = __mdspan::__inv_map_rank<_IndexType,
_Slices...>();
- auto __impl = [&]<size_t... _Indices>(index_sequence<_Indices...>)
- {
- using _SubExtents = extents<_IndexType,
- (__mdspan::__static_slice_extent<_IndexType,
- _Extents...[__inv_map[_Indices]],
- _Slices...[__inv_map[_Indices]]>())...>;
- if constexpr (_SubExtents::rank_dynamic() == 0)
- return _SubExtents{};
- else
- {
- using _StaticSubExtents = __mdspan::_StaticExtents<
- __mdspan::__static_extents<_SubExtents>()>;
- auto __create = [&]<size_t... _Is>(index_sequence<_Is...>)
- {
- constexpr auto __slice_idx = [__inv_map](size_t __i) consteval
- {
- return
__inv_map[_StaticSubExtents::_S_dynamic_index_inv(__i)];
- };
-
- return _SubExtents{
- (__mdspan::__dynamic_slice_extent<__slice_idx(_Is)>(
- __exts, __slices...[__slice_idx(_Is)]))...};
- };
- constexpr auto __dyn_subrank = _SubExtents::rank_dynamic();
- return __create(make_index_sequence<__dyn_subrank>());
- }
- };
-
- return __impl(make_index_sequence<__inv_map.size()>());
+ __mdspan::__check_valid_slices(__mapping.extents(), __slices...);
+ return submdspan_mapping(__mapping, __slices...);
}
}
@@ -2792,6 +3218,25 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
};
return __impl(__mdspan::__slice_cast<_IndexType>(__raw_slices)...);
}
+
+ template<typename _ElementType, typename _Extents, typename _Layout,
+ typename _Accessor, typename... _RawSlices>
+ requires (sizeof...(_RawSlices) == _Extents::rank()
+ && __mdspan::__sliceable_mapping<typename _Layout::mapping<_Extents>,
+
__mdspan::__full_extent_t<_RawSlices>...>)
+ constexpr auto
+ submdspan(
+ const mdspan<_ElementType, _Extents, _Layout, _Accessor>& __md,
+ _RawSlices... __raw_slices)
+ {
+ using _IndexType = typename _Extents::index_type;
+ auto [__mapping, __offset] = __mdspan::__submapping(
+ __md.mapping(), __mdspan::__slice_cast<_IndexType>(__raw_slices)...);
+ return std::mdspan(
+ __md.accessor().offset(__md.data_handle(), __offset),
+ std::move(__mapping),
+ typename _Accessor::offset_policy(__md.accessor()));
+ }
#endif // __glibcxx_submdspan
_GLIBCXX_END_NAMESPACE_VERSION
diff --git a/libstdc++-v3/src/c++23/std.cc.in b/libstdc++-v3/src/c++23/std.cc.in
index 4962d4fc598..ef0da5d685a 100644
--- a/libstdc++-v3/src/c++23/std.cc.in
+++ b/libstdc++-v3/src/c++23/std.cc.in
@@ -1899,8 +1899,8 @@ export namespace std
using std::submdspan_mapping_result;
using std::submdspan_canonicalize_slices;
using std::submdspan_extents;
+ using std::submdspan;
#endif
- // FIXME mdsubspan
}
#endif
diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/generic.cc
b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/generic.cc
new file mode 100644
index 00000000000..682ff62254c
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/generic.cc
@@ -0,0 +1,71 @@
+// { dg-do compile { target c++26 } }
+#include <mdspan>
+
+namespace adl
+{
+ struct NoFriend
+ {
+ template<typename Extents>
+ class mapping
+ {
+ public:
+ using extents_type = Extents;
+ using index_type = typename extents_type::index_type;
+ };
+ };
+
+ struct NoFull
+ {
+ template<typename Extents>
+ class mapping
+ {
+ public:
+ using extents_type = Extents;
+ using index_type = typename extents_type::index_type;
+
+ private:
+ friend constexpr auto
+ submdspan_mapping(mapping, int)
+ { return std::submdspan_mapping_result{mapping{}, 0}; }
+ };
+ };
+
+ struct WrongReturnValue
+ {
+ template<typename Extents>
+ class mapping
+ {
+ public:
+ using extents_type = Extents;
+ using index_type = typename extents_type::index_type;
+
+ private:
+ friend constexpr int
+ submdspan_mapping(mapping, std::full_extent_t)
+ { return 42; }
+ };
+ };
+}
+
+template<typename MdSpan, typename... Slices>
+ concept submdspan_exists = requires (MdSpan md, Slices... slices)
+ {
+ std::submdspan(md, slices...);
+ };
+
+template<typename Layout, bool Expected>
+constexpr bool
+test_invalid_mapping()
+{
+ using Extents = std::extents<int, 3>;
+ using MdSpan = std::mdspan<double, Extents, Layout>;
+ static_assert(submdspan_exists<MdSpan, int> == Expected);
+ static_assert(submdspan_exists<MdSpan, std::full_extent_t> == Expected);
+ static_assert(!submdspan_exists<MdSpan>);
+ static_assert(!submdspan_exists<MdSpan, int, int>);
+ return true;
+}
+static_assert(test_invalid_mapping<std::layout_left, true>());
+static_assert(test_invalid_mapping<adl::NoFriend, false>());
+static_assert(test_invalid_mapping<adl::NoFull, false>());
+static_assert(test_invalid_mapping<adl::WrongReturnValue, false>());
diff --git
a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/left.cc
b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/left.cc
new file mode 100644
index 00000000000..d6a85d0380b
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/left.cc
@@ -0,0 +1,9 @@
+// { dg-do run { target c++26 } }
+#include "testcases.h"
+
+int
+main()
+{
+ test_all<std::layout_left>();
+ return 0;
+}
diff --git
a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/testcases.h
b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/testcases.h
new file mode 100644
index 00000000000..d7b751d700c
--- /dev/null
+++
b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/testcases.h
@@ -0,0 +1,360 @@
+#include <mdspan>
+
+#include <vector>
+#include <numeric>
+#include "../../layout_traits.h"
+#include <testsuite_hooks.h>
+
+constexpr size_t dyn = std::dynamic_extent;
+constexpr auto all = std::full_extent;
+
+template<typename T>
+ constexpr bool is_strided_slice = false;
+
+template<typename O, typename E, typename S>
+ constexpr bool is_strided_slice<std::strided_slice<O, E, S>> = true;
+
+template<typename MDSpan>
+ constexpr void
+ fill(const MDSpan& md)
+ {
+ using IndexType = typename MDSpan::index_type;
+ auto exts = md.extents();
+ if constexpr (exts.rank() == 3)
+ for(IndexType i = 0; i < exts.extent(0); ++i)
+ for(IndexType j = 0; j < exts.extent(1); ++j)
+ for(IndexType k = 0; k < exts.extent(2); ++k)
+ md[i, j, k] = 100 * i + 10 * j + k;
+ }
+
+template<typename Int, size_t Rank>
+ class multi_index_generator
+ {
+ struct sentinel
+ { };
+
+ class iterator
+ {
+ public:
+ constexpr
+ iterator(const std::array<Int, Rank>& shape)
+ : M_shape(shape)
+ { }
+
+ constexpr iterator&
+ operator++()
+ {
+ if constexpr (Rank > 0)
+ {
+ ++M_indices[Rank-1];
+ for(size_t i = Rank; i > 1; --i)
+ if (M_indices[i-1] == M_shape[i-1])
+ {
+ M_indices[i-1] = 0;
+ ++M_indices[i-2];
+ }
+ }
+ return *this;
+ }
+
+ constexpr auto
+ operator*()
+ { return M_indices; }
+
+ private:
+ friend constexpr bool
+ operator==(const iterator& it, sentinel)
+ {
+ if constexpr (Rank > 0)
+ return it.M_indices[0] == it.M_shape[0];
+ else
+ return true;
+ }
+
+ std::array<Int, Rank> M_indices{};
+ std::array<Int, Rank> M_shape;
+ };
+
+ public:
+ constexpr
+ multi_index_generator(std::array<Int, Rank> shape)
+ : M_shape(shape)
+ { }
+
+ constexpr iterator
+ begin() const
+ { return iterator(M_shape); }
+
+ constexpr sentinel
+ end() const
+ { return sentinel{}; }
+
+ private:
+ std::array<Int, Rank> M_shape;
+ };
+
+constexpr bool
+test_multi_index()
+{
+ auto shape = std::array{3, 5, 7, 1};
+ auto gen = multi_index_generator(shape);
+ auto it = gen.begin();
+ auto end = gen.end();
+ for (int i = 0; i < shape[0]; ++i)
+ for (int j = 0; j < shape[1]; ++j)
+ for (int k = 0; k < shape[2]; ++k)
+ for (int l = 0; l < shape[3]; ++l)
+ {
+ VERIFY(it != end);
+ VERIFY(*it == std::array{i, j, k, l});
+ ++it;
+ }
+ return true;
+}
+static_assert(test_multi_index());
+
+struct
+collapse
+{ };
+
+template<typename... Slices>
+ consteval auto
+ inv_collapsed_index_map()
+ {
+ constexpr size_t rank = sizeof...(Slices);
+ auto is_collapsing = std::array{std::same_as<Slices, collapse>...};
+ constexpr auto collapsed_rank = ((!std::same_as<Slices, collapse>) + ... +
0);
+
+ std::array<size_t, collapsed_rank> ret;
+ if constexpr (collapsed_rank > 0)
+ for(size_t k = 0, i = 0; i < rank; ++i)
+ if (!is_collapsing[i])
+ ret[k++] = i;
+ return ret;
+ }
+
+static_assert(inv_collapsed_index_map<collapse, collapse, collapse>()
+ == std::array<size_t, 0>{});
+
+static_assert(inv_collapsed_index_map<collapse, decltype(all), collapse>()
+ == std::array<size_t, 1>{1});
+
+template<typename IndexType, typename Slice>
+ constexpr std::vector<IndexType>
+ make_selection(IndexType extent, const Slice& slice)
+ {
+ if constexpr (std::convertible_to<Slice, IndexType>)
+ return {static_cast<IndexType>(slice)};
+ else if constexpr (std::same_as<Slice, std::full_extent_t>)
+ {
+ auto ret = std::vector<IndexType>(static_cast<size_t>(extent));
+ std::ranges::iota(ret, 0);
+ return ret;
+ }
+ else if constexpr (is_strided_slice<Slice>)
+ {
+ auto ret = std::vector<IndexType>{};
+ size_t n = static_cast<size_t>(slice.extent);
+ for(size_t i = 0; i < n; i += slice.stride)
+ ret.push_back(slice.offset + i);
+ return ret;
+ }
+ else
+ {
+ auto [begin, end] = slice;
+ auto ret = std::vector<IndexType>(static_cast<size_t>(end - begin));
+ std::ranges::iota(ret, begin);
+ return ret;
+ }
+ }
+
+template<typename Layout, size_t... I, typename... Slices>
+ constexpr bool
+ check_selection(std::index_sequence<I...>, auto md, Slices... slices)
+ {
+ auto exts = md.extents();
+ auto outer_shape = std::array{exts.extent(0), exts.extent(1),
exts.extent(2)};
+
+ constexpr auto full_index = inv_collapsed_index_map<Slices...>();
+ auto make_slice = [](size_t i, auto slice)
+ {
+ if constexpr (std::same_as<decltype(slice), collapse>)
+ return i;
+ else
+ return slice;
+ };
+
+ auto loop_body = [&]<size_t... J>(std::index_sequence<J...>, auto ijk,
+ auto... slices)
+ {
+ auto submd = submdspan(md, slices...[I]...);
+ auto selection = std::tuple{make_selection(exts.extent(I),
slices...[I])...};
+ auto inner_shape = std::array<size_t, full_index.size()>{
+ std::get<full_index[J]>(selection).size()...
+ };
+
+ for (auto ij : multi_index_generator(inner_shape))
+ {
+ ((ijk[full_index[J]] = get<full_index[J]>(selection)[ij[J]]),...);
+ VERIFY(submd[ij] == md[ijk]);
+ }
+ };
+
+ for (auto ijk : multi_index_generator(outer_shape))
+ loop_body(std::make_index_sequence<full_index.size()>(), ijk,
+ make_slice(ijk[I], slices...[I])...);
+ return true;
+ }
+
+template<typename Layout, typename...MD, typename... Slices>
+ constexpr bool
+ check_selection(std::mdspan<MD...> md, Slices... slices)
+ {
+ auto indices = std::make_index_sequence<sizeof...(slices)>();
+ return check_selection<Layout>(indices, md, slices...);
+ }
+
+template<typename Layout, typename IndexType, size_t... Extents,
+ typename... Slices>
+ constexpr bool
+ check_selection(std::extents<IndexType, Extents...>exts, Slices... slices)
+ {
+ auto run = [&](auto m)
+ {
+ auto storage = std::vector<double>(m.required_span_size());
+ auto md = std::mdspan(storage.data(), m);
+ fill(md);
+ return check_selection<Layout>(md, slices...);
+ };
+
+ if constexpr (std::same_as<Layout, std::layout_stride>)
+ {
+ auto m = typename Layout::mapping(exts, std::array{15, 2, 50});
+ return run(m);
+ }
+ else
+ {
+ auto m = typename Layout::mapping(exts);
+ return run(m);
+ }
+ }
+
+template<typename Layout>
+ constexpr bool
+ test_scalar_selection(auto exts)
+ {
+ check_selection<Layout>(exts, collapse{}, collapse{}, collapse{});
+ return true;
+ }
+
+template<typename Layout>
+ constexpr bool
+ test_full_lines(auto exts)
+ {
+ check_selection<Layout>(exts, all, collapse{}, collapse{});
+ check_selection<Layout>(exts, collapse{}, all, collapse{});
+ check_selection<Layout>(exts, collapse{}, collapse{}, all);
+ return true;
+ }
+
+template<typename Layout>
+ constexpr bool
+ test_full_blocks(auto exts)
+ {
+ check_selection<Layout>(exts, all, all, collapse{});
+ check_selection<Layout>(exts, all, collapse{}, all);
+ check_selection<Layout>(exts, collapse{}, all, all);
+ return true;
+ }
+
+template<typename Layout>
+ constexpr bool
+ test_cubes(auto exts)
+ {
+ auto s0 = std::pair{0, 2};
+ auto s1 = std::pair{1, 4};
+ auto s2 = std::pair{3, 7};
+
+ check_selection<Layout>(exts, all, all, all);
+ check_selection<Layout>(exts, all, all, s2);
+ check_selection<Layout>(exts, s0, all, all);
+ check_selection<Layout>(exts, s0, all, s2);
+ check_selection<Layout>(exts, s0, s1, s2);
+ return true;
+ }
+
+template<typename Layout>
+ constexpr bool
+ test_strided_line_selection(auto exts)
+ {
+ auto check = [&](auto s)
+ {
+ check_selection<Layout>(exts, collapse{}, s, collapse{});
+ };
+
+ check(std::strided_slice(0, 2, 2));
+ check(std::strided_slice(0, 3, 2));
+ check(std::strided_slice(1, 3, 2));
+ check(std::strided_slice(1, std::cw<3>, std::cw<2>));
+ return true;
+ }
+
+template<typename Layout>
+ constexpr bool
+ test_strided_box_selection(auto exts)
+ {
+ auto s0 = std::strided_slice(0, 3, 2);
+ auto s1 = std::strided_slice(1, 4, 2);
+ auto s2 = std::strided_slice(0, 7, 3);
+
+ check_selection<Layout>(exts, s0, s1, s2);
+ return true;
+ }
+
+template<typename Layout>
+ constexpr bool
+ test_all_cheap()
+ {
+ constexpr auto dyn_exts = std::extents(3, 5, 7);
+ constexpr auto sta_exts = std::extents<int, 3, 5, 7>{};
+
+ test_scalar_selection<Layout>(dyn_exts);
+ test_scalar_selection<Layout>(sta_exts);
+ static_assert(test_scalar_selection<Layout>(dyn_exts));
+ static_assert(test_scalar_selection<Layout>(sta_exts));
+
+ test_full_lines<Layout>(dyn_exts);
+ test_full_lines<Layout>(sta_exts);
+ static_assert(test_full_lines<Layout>(dyn_exts));
+ static_assert(test_full_lines<Layout>(sta_exts));
+
+ test_strided_box_selection<Layout>(dyn_exts);
+ test_strided_box_selection<Layout>(sta_exts);
+ static_assert(test_strided_box_selection<Layout>(dyn_exts));
+ static_assert(test_strided_box_selection<Layout>(sta_exts));
+ return true;
+ }
+
+template<typename Layout>
+ constexpr bool
+ test_all_expensive()
+ {
+ auto run = [](auto exts)
+ {
+ test_full_blocks<Layout>(exts);
+ test_cubes<Layout>(exts);
+ };
+
+ run(std::extents(3, 5, 7));
+ run(std::extents<int, 3, 5, 7>{});
+ return true;
+ }
+
+template<typename Layout>
+ constexpr bool
+ test_all()
+ {
+ test_all_cheap<Layout>();
+ test_all_expensive<Layout>();
+ return true;
+ }
diff --git
a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc
b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc
new file mode 100644
index 00000000000..a37d3cd588f
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc
@@ -0,0 +1,136 @@
+// { dg-do run { target c++26 } }
+#include <mdspan>
+
+#include <iostream> // TODO remove
+#include "../layout_traits.h"
+#include <testsuite_hooks.h>
+
+constexpr size_t dyn = std::dynamic_extent;
+
+template<typename Mapping, typename... Slices>
+ constexpr auto
+ call_submdspan_mapping(const Mapping& m, std::tuple<Slices...> slices)
+ {
+ auto impl = [&]<size_t... I>(std::index_sequence<I...>)
+ { return submdspan_mapping(m, get<I>(slices)...); };
+ return impl(std::make_index_sequence<sizeof...(Slices)>());
+ }
+
+template<typename Layout>
+ constexpr bool
+ test_layout_unpadded_return_types()
+ {
+ constexpr auto padding_side = DeducePaddingSide::from_typename<Layout>();
+ using Traits = LayoutTraits<padding_side>;
+
+ {
+ auto m0 = typename Layout::mapping(std::extents());
+ auto result = submdspan_mapping(m0);
+ using layout_type = typename decltype(result.mapping)::layout_type;
+ static_assert(std::same_as<layout_type, Layout>);
+ }
+
+ auto exts = Traits::make_extents(std::dims<5, int>(3, 5, 7, 11, 13));
+ auto m = typename Layout::mapping(exts);
+ auto all = std::full_extent;
+ auto s251 = std::strided_slice{2, 5, std::cw<1>};
+
+ {
+ auto slices = std::tuple{0, 0, 0, 0, 0};
+ auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
+ using layout_type = typename decltype(result.mapping)::layout_type;
+ static_assert(std::same_as<layout_type, Layout>);
+ }
+
+ {
+ auto slices = std::tuple{all, all, all, s251, 0};
+ auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
+ using layout_type = typename decltype(result.mapping)::layout_type;
+ static_assert(std::same_as<layout_type, Layout>);
+ }
+
+ {
+ auto s0 = std::strided_slice{1, 1, std::cw<1>};
+ auto slices = std::tuple{s0, all, all, s251, 0};
+ auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
+ using layout_type = typename decltype(result.mapping)::layout_type;
+ static_assert(is_same_padded<padding_side, layout_type>);
+ }
+
+ {
+ auto s0 = std::strided_slice{1, 2, std::cw<1>};
+ auto slices = std::tuple{s0, all, all, s251, 0};
+ auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
+ using layout_type = typename decltype(result.mapping)::layout_type;
+ static_assert(is_same_padded<padding_side, layout_type>);
+ }
+
+ {
+ auto s0 = std::strided_slice{1, 2, std::cw<1>};
+ auto slices = std::tuple{s0, 0, all, s251, 0};
+ auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
+ using layout_type = typename decltype(result.mapping)::layout_type;
+ static_assert(is_same_padded<padding_side, layout_type>);
+ }
+
+ {
+ auto s0 = std::strided_slice{1, 2, 1};
+ auto slices = std::tuple{s0, all, all, s251, 0};
+ auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
+ using layout_type = decltype(result.mapping)::layout_type;
+ static_assert(std::same_as<layout_type, std::layout_stride>);
+ }
+
+ {
+ auto slices = std::tuple{1, all, all, s251, 0};
+ auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
+ using layout_type = decltype(result.mapping)::layout_type;
+ static_assert(std::same_as<layout_type, std::layout_stride>);
+ }
+
+ {
+ auto s3 = std::strided_slice{2, std::cw<7>, std::cw<2>};
+ auto slices = std::tuple{all, all, all, s3, 0};
+ auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
+ using layout_type = decltype(result.mapping)::layout_type;
+ static_assert(std::same_as<layout_type, std::layout_stride>);
+ }
+ return true;
+ }
+
+template<typename Layout>
+ constexpr bool
+ test_layout_unpadded_padding_value()
+ {
+ using Traits = LayoutTraits<DeducePaddingSide::from_typename<Layout>()>;
+ auto s0 = std::strided_slice{size_t(1), size_t(2), std::cw<size_t(1)>};
+ auto s3 = std::strided_slice{size_t(2), size_t(5), std::cw<size_t(1)>};
+ auto all = std::full_extent;
+
+ auto check = [&](auto exts, size_t expected)
+ {
+ auto m = typename Layout::mapping(Traits::make_extents(exts));
+ auto slices = std::tuple{s0, size_t(0), all, s3, size_t(0)};
+ auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
+ auto padding_value = decltype(result.mapping)::padding_value;
+ VERIFY(padding_value == expected);
+ };
+
+ check(std::extents(std::cw<3>, std::cw<5>, std::cw<7>, 11, 13), 3*5);
+ check(std::extents(std::cw<3>, std::cw<5>, 7, 11, 13), 3*5);
+ check(std::extents(std::cw<3>, 5, 7, 11, 13), dyn);
+ check(std::extents(3, 5, 7, 11, 13), dyn);
+ return true;
+ }
+
+int
+main()
+{
+ test_layout_unpadded_return_types<std::layout_left>();
+ static_assert(test_layout_unpadded_return_types<std::layout_left>());
+
+ test_layout_unpadded_padding_value<std::layout_left>();
+ static_assert(test_layout_unpadded_padding_value<std::layout_left>());
+ return 0;
+}
+
diff --git
a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc
b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc
new file mode 100644
index 00000000000..1fc10a832eb
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc
@@ -0,0 +1,104 @@
+// { dg-do compile { target c++26 } }
+#include <mdspan>
+
+#include <vector>
+
+template<typename Layout, typename... Slices>
+ constexpr bool
+ check_slice_range(Slices... slices)
+ {
+ auto m = typename Layout::mapping<std::extents<int, 3, 5, 7>>{};
+ auto storage = std::vector<double>(m.required_span_size());
+ auto md = std::mdspan(storage.data(), m);
+
+ auto submd = submdspan(md, slices...); // { dg-error "expansion
of" }
+ (void) submd;
+ return true;
+ }
+
+template<typename Layout>
+ constexpr bool
+ test_int_under()
+ {
+ check_slice_range<Layout>(1, -1, 2); // { dg-error "expansion
of" }
+ return true;
+ }
+static_assert(test_int_under<std::layout_left>()); // { dg-error "expansion
of" }
+
+template<typename Layout>
+ constexpr bool
+ test_int_over()
+ {
+ check_slice_range<Layout>(1, 5, 2); // { dg-error "expansion
of" }
+ return true;
+ }
+static_assert(test_int_over<std::layout_left>()); // { dg-error "expansion
of" }
+
+template<typename Layout>
+ constexpr bool
+ test_tuple_under()
+ {
+ check_slice_range<Layout>(1, std::tuple{-1, 2}, 2); // { dg-error
"expansion of" }
+ return true;
+ }
+static_assert(test_tuple_under<std::layout_left>()); // { dg-error
"expansion of" }
+
+template<typename Layout>
+ constexpr bool
+ test_tuple_reversed()
+ {
+ check_slice_range<Layout>(1, std::tuple{3, 2}, 2); // { dg-error
"expansion of" }
+ return true;
+ }
+static_assert(test_tuple_reversed<std::layout_left>()); // { dg-error
"expansion of" }
+
+template<typename Layout>
+ constexpr bool
+ test_tuple_over()
+ {
+ check_slice_range<Layout>(1, std::tuple{0, 6}, 2); // { dg-error
"expansion of" }
+ return true;
+ }
+static_assert(test_tuple_over<std::layout_left>()); // { dg-error "expansion
of" }
+
+template<typename Layout>
+ constexpr bool
+ test_strided_slice_zero()
+ {
+ check_slice_range<Layout>(1, std::strided_slice{1, 1, 0}, 2); // {
dg-error "expansion of" }
+ return true;
+ }
+static_assert(test_strided_slice_zero<std::layout_left>()); // { dg-error
"expansion of" }
+
+template<typename Layout>
+ constexpr bool
+ test_strided_slice_offset_under()
+ {
+ check_slice_range<Layout>(1, std::strided_slice{-1, 1, 1}, 2); // {
dg-error "expansion of" }
+ return true;
+ }
+static_assert(test_strided_slice_offset_under<std::layout_left>()); // {
dg-error "expansion of" }
+
+template<typename Layout>
+ constexpr bool
+ test_strided_slice_offset_over()
+ {
+ check_slice_range<Layout>(1, std::strided_slice{6, 0, 1}, 2); // {
dg-error "expansion of" }
+ return true;
+ }
+static_assert(test_strided_slice_offset_over<std::layout_left>()); // {
dg-error "expansion of" }
+
+template<typename Layout>
+ constexpr bool
+ test_strided_slice_extent_over()
+ {
+ check_slice_range<Layout>(1, std::strided_slice{1, 5, 1}, 2); // {
dg-error "expansion of" }
+ return true;
+ }
+static_assert(test_strided_slice_extent_over<std::layout_left>()); // {
dg-error "expansion of" }
+
+// { dg-prune-output "static assertion failed" }
+// { dg-prune-output "__glibcxx_assert_fail" }
+// { dg-prune-output "non-constant condition" }
+// { dg-prune-output "no matching function" }
+// { dg-prune-output "does not satisfy placeholder constraints" }
--
2.52.0