On Mon, Dec 8, 2025 at 9:21 PM Luc Grosheintz <[email protected]> wrote:
> 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 (layout_left::mapping::submdspan_mapping): > New friend function. > (submdspan): New function. > * 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. > > Signed-off-by: Luc Grosheintz <[email protected]> > --- > Two very small cases, I will implement, add them locally and post a updated patch. > libstdc++-v3/include/std/mdspan | 562 ++++++++++++++++-- > 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, 1187 insertions(+), 57 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 cef658da470..4c004335289 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,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > __impl(make_index_sequence<__rank>()); > } > > - template<typename _IndexType, size_t _Extent, typename _Slice> > - consteval size_t > - __static_slice_extent() > + template<typename _Mapping, typename... _Slices> > + concept __sliceable_mapping = requires(_Mapping __m, _Slices... > __slices) > { > - 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; > - } > + { submdspan_mapping(__m, __slices...) } -> > __submdspan_mapping_result; > + }; > > - template<size_t _K, typename _Extents, typename _Slice> > - constexpr typename _Extents::index_type > - __dynamic_slice_extent(const _Extents& __exts, _Slice __slice) > + template<typename _Tp> > + struct _FullExtent > { > - if constexpr (__is_strided_slice<_Slice>) > - return __slice.extent == 0 ? 0 : 1 + (__slice.extent - 1) / > __slice.stride; > - else > - return __exts.extent(_K); > - } > + using __value = std::full_extent_t; > + }; > > - template<typename _IndexType, size_t... _Extents, typename... _Slices> > - requires (sizeof...(_Slices) == sizeof...(_Extents)) > + template<typename _Slice> > + using __full_extent_t = typename _FullExtent<_Slice>::__value; > This alias can just say = std::full_extent_t. I will change that locallu. > + > + // Enables ADL-only calls from submdspan. > + void submdspan_mapping() = delete; > + > + template<typename _MdSpan, typename... _Slices> > constexpr auto > - __subextents(const extents<_IndexType, _Extents...>& __exts, > - _Slices... __slices) > + __submdspan(const _MdSpan& __md, _Slices... __slices) > I would preffer to have a __submapping function that will get mapping and return submdspam_mapping_result. > { > - 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()>()); > + using _Accessor = typename _MdSpan::accessor_type; > + __mdspan::__check_valid_slices(__md.extents(), __slices...); > + auto [__mapping, __offset] = submdspan_mapping(__md.mapping(), > __slices...); > + return std::mdspan(__md.accessor().offset(__md.data_handle(), > __offset), > + std::move(__mapping), typename > _Accessor::offset_policy(__md.accessor())); > } > } > > @@ -2792,6 +3227,21 @@ _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; > + return __mdspan::__submdspan( > + __md, __mdspan::__slice_cast<_IndexType>(__raw_slices)...); > + } > #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 c2a9293b05a..2dac6a6d887 100644 > --- a/libstdc++-v3/src/c++23/std.cc.in > +++ b/libstdc++-v3/src/c++23/std.cc.in > @@ -1885,8 +1885,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 > >
