[PATCH v2 04/10] libstdc++: Implement submdspan and submdspan_mapping for layout_left. [PR110352]
Tomasz Kaminski
tkaminsk@redhat.com
Tue Dec 2 08:36:04 GMT 2025
On Mon, Dec 1, 2025 at 3:25 PM Luc Grosheintz <luc.grosheintz@gmail.com>
wrote:
>
>
> On 11/20/25 17:08, Tomasz Kaminski wrote:
> > On Tue, Nov 18, 2025 at 3:37 PM Luc Grosheintz <luc.grosheintz@gmail.com
> >
> > 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/submdspan.cc: New
> test.
> >> *
> 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 <luc.grosheintz@gmail.com>
> >>
> > Posted some idea on how to classify the slices without so much
> > metaprogramming,
> > the idea would be to have function, that would have constexpr array of
> > slice kind,
> > would go over it either left to right or right to left, and return if we
> > have matching
> > blocks. Could accept rank as a parameter. We most likely want this
> function
> > to simply
> > accept span, subrank, and direction, and not be templated.
> >
> > Could you let me know what you think about the idea?
> > I think I will stop review here, as this will affect the rest of the
> code.
>
> I have a version drafted up: it works reasonably well, only I'd rather
> reverse the array of SliceKind than pass in a direction. It feels easier
> to express: same as left; but from the back. Makes it explicit that there
> is no difference between left and right. Since, it's all consteval, there's
> no cost at runtime; and at compile time we save instantiating once for
> each direction.
>
> ---------------
>
> Should all standardized mapping have `_M_strides`? I don't object, but
> the usage is slightly different: _M_strides creates an array of length
> `rank`; while the loop in question only needs an array of length
> `subrank`, it only needs the strides at non-collapsing slices.
>
> ---------------
>
> About qualified calls (to avoid ADL). Is this also required for uglified
> names, e.g. __subextents? Since users aren't permitted to use those names
> they can't cause ADL-related issues.
>
The ADL related issues are not limited to calling the user-defined
function,
if ADL is performed for the class that is incomplete, that will cause
problems.
For example:
struct Incomplete;
std::unique_ptr<Incomplete> u1, u2;
using std::swap();
swap(u1, u2); // ADL also searches for associated namespace of template
arguments, i.e. Incomplete here.
> If we know that the types of all arguments are "ours", i.e. defined in
> std:: or builtins like `int`, do we still need to protect against ADL?
> After canonicalization, we're left with: standarsized mapping, extents,
> full_extent_t, strided_slice, constant_wrapper and integers.
>
Users are not allowed to add declarations to std, so yes the set would be
limited,
but we may still have internal ADL-related problems (picking deleted
functions,
e.t.c).
>
> Sofar I've been implementing everything as if the answer were: "no" to
> both questions. If that's wrong, let me know, and I'll do a pass to
> clean up all of <mdspan>.
>
I think it would be safer, and it will also improve the compile time (with
qualified name
compilers can just skip ADL) if we qualify all names.
>
> >
> >
> >> ---
> >> libstdc++-v3/include/std/mdspan | 387 ++++++++++++++++++
> >> libstdc++-v3/src/c++23/std.cc.in | 2 +-
> >> .../mdspan/submdspan/submdspan.cc | 369 +++++++++++++++++
> >> .../mdspan/submdspan/submdspan_mapping.cc | 136 ++++++
> >> .../mdspan/submdspan/submdspan_neg.cc | 102 +++++
> >> 5 files changed, 995 insertions(+), 1 deletion(-)
> >> create mode 100644
> >> libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc
> >> 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 36e04f7e1b5..712826ea7e7 100644
> >> --- a/libstdc++-v3/include/std/mdspan
> >> +++ b/libstdc++-v3/include/std/mdspan
> >> @@ -578,20 +578,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >> return __r == 0 ? 1 : __exts.extent(0);
> >> else if constexpr
> >> (__all_dynamic(std::span(__sta_exts).first(__rank-1)))
> >> return __extents_prod(__exts, 1, 0, __r);
> >> else
> >> {
> >> size_t __sta_prod = __fwd_partial_prods<__sta_exts>[__r];
> >> return __extents_prod(__exts, __sta_prod, 0, __r);
> >> }
> >> }
> >>
> >> + 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
> >> __rev_prod(const _Extents& __exts, size_t __r) noexcept
> >> {
> >> constexpr size_t __rank = _Extents::rank();
> >> constexpr auto& __sta_exts = __static_extents<_Extents>();
> >> if constexpr (__rank == 1)
> >> return 1;
> >> else if constexpr (__rank == 2)
> >> @@ -1027,20 +1037,374 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >> constexpr auto __sub_rank = __subrank<_IndexType,
> _Slices...>();
> >> auto __map = std::array<size_t, __sub_rank>{};
> >> auto __is_int_like = std::array{convertible_to<_Slices,
> >> _IndexType>...};
> >>
> >> size_t __i = 0;
> >> for (size_t __k = 0; __k < __rank; ++__k)
> >> if (!__is_int_like[__k])
> >> __map[__i++] = __k;
> >> return __map;
> >> }
> >> +
> >> + template<typename _IndexType, typename _Slice>
> >> + constexpr _IndexType
> >> + __slice_begin(_Slice __slice)
> >> + {
> >>
> > Please consider implementing this as:
> > if constexpr (is_same_v<_Slice, _full_extent_t>)
> > return 0;
> > else if constexpr (__is_strided_slice<_Slice>)
> > return __slice.offset;
> > else
> > return __slice;
> > This should be lighter to check that using concept,
> > and you do not need to pass _IndexType.
> >
> >
> > + if constexpr (convertible_to<_Slice, _IndexType>)
> >> + return __slice;
> >> + else if constexpr (__is_strided_slice<_Slice>)
> >> + return __slice.offset;
> >> + else
> >> + return 0; // full_extent
> >> + }
> >> +
> >> + 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 (__ext.static_extent(
> >
> >
> > I will suggest expressing this as:
> > if constexpr (is_same_v<_Slice, __full_extent_t>)
> > return true; // we checked that before
> > else if constexpr (!_is_strided_slice<_Slice>)
> > return false;
> > else
> > // Compiler wii probably optimize if this two are constant.
> > return __slice.offset = __ext.extent(0);
> >
> >
> >> + if constexpr (is_convertible_v<_Slice, _IndexType>)
> >>
> > I do not think this is correct, we single indices to operators.
> >
> >> + return false;
> >> + else if (same_as<_Slice, full_extent_t>
> >> + && __ext.static_extent(0) > 0
> >> + && __ext.static_extent(0) != dynamic_extent)
> >>
> >
> > + return false;
> >> + else
> >> + return __slice_begin<_IndexType>(__slice) ==
> __ext.extent(0);
> >>
> > We seem to this for __full_extent, before the previous if is not
> constexpr.
> >
> >> + };
> >> +
> >> + const auto& __exts = __mapping.extents();
> >>
> > + return ((__is_past_the_end(__slices...[_Is],
> >> + __extract_extent<_Is>(__exts))) ||
> >> ...);
> >> + };
> >> +
> >> + if constexpr ((same_as<_Slices, full_extent_t> && ...))
> >> + return __offset(__mapping);
> >> +
> >>
> > I think I would check empty mdspan separately here, so if
> > empty(__mapping.extents())
> > the return __mapping.required_span_size(), this will also simply
> > __any_past_the_end.
> >
> >
> >> + if constexpr (!((convertible_to<_Slices, _IndexType>) && ...))
> >
> > + if
> >> (__any_past_the_end(make_index_sequence<sizeof...(__slices)>()))
> >> + return __mapping.required_span_size();
> >> + return __mapping(__slice_begin<_IndexType>(__slices)...);
> >>
> > Because we do not pass _IndexType to slice begin, use
> > static_cast<_IndexType> here.
> >
> >> + }
> >> +
> >> + enum class _LayoutSide
> >> + {
> >> + __left,
> >> + __right,
> >> + __unknown
> >> + };
> >> +
> >> + template<typename _Mapping>
> >> + consteval _LayoutSide
> >> + __deduce_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;
> >> + }
> >> +
> >> + template<typename _Mapping>
> >> + static constexpr typename _Mapping::index_type
> >> + _S_extent(const _Mapping& __mapping, size_t __k)
> >> + {
> >> + if (__k == 0)
> >> + return __mapping.stride(_S_idx(1));
> >>
> > Nice trick of handling padded and not padded, but comment on it.
> >
> >> + 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>(index_sequence<_Is...>)
> >> + {
> >> + return __inv_map_rank<_IndexType,
> >> _Slices...[_S_idx(_Is)]...>();
> >> + };
> >> + return __impl(make_index_sequence<_Rank>());
> >> + }
> >> + };
> >> +
> >> + template<typename _SubExtents, typename _Mapping, typename...
> _Slices>
> >> + constexpr auto
> >> + __substrides_generic(const _Mapping& __mapping, const _Slices&...
> >> __slices)
> >> + {
> >> + using _IndexType = typename _Mapping::index_type;
> >> + if constexpr (_SubExtents::rank() == 0)
> >> + return array<_IndexType, _SubExtents::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>(index_sequence<_Is...>)
> >> + {
> >> + constexpr auto __inv_map = __inv_map_rank<_IndexType,
> >> _Slices...>();
> >> + return array<_IndexType, _SubExtents::rank()>{
> >> + __stride(__inv_map[_Is],
> __slices...[__inv_map[_Is]])...};
> >> + };
> >> + return __impl(make_index_sequence<_SubExtents::rank()>());
> >> + }
> >> + };
> >> +
> >> + template<typename _SubExtents, typename _Mapping, typename...
> _Slices>
> >> + constexpr auto
> >> + __substrides_standardized(const _Mapping& __mapping,
> >> + const _Slices&... __slices)
> >> + {
> >> + using _IndexType = typename _Mapping::index_type;
> >> + using _Trait = _StridesTrait<__deduce_mapping_side<_Mapping>(),
> >> + _Mapping::extents_type::rank()>;
> >> + using _SubTrait =
> _StridesTrait<__deduce_mapping_side<_Mapping>(),
> >> + _SubExtents::rank()>;
> >> +
> >> + constexpr size_t __sub_rank = _SubExtents::rank();
> >> +
> >> + 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>(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_extent(__mapping, __i);
> >
> > I wonder if for all standardized mappings, we should not add _M_strides
> > (public) function to access that. This would be useful.
> >
> >> +
> >> + size_t __krev = _SubTrait::_S_idx(__k);
> >> + if constexpr (__is_strided_slice<decltype(__slice)>)
> >> + __ret[__krev] = __stride * __slice.stride;
> >> + else
> >> + __ret[__krev] = __stride;
> >> +
> >> + __i0 = __inv_map[__k];
> >> + };
> >> +
> >> + ((__body(_Ks,
> >> __slices...[_Trait::_S_idx(__inv_map[_Ks])])),...);
> >> + };
> >> + __loop(make_index_sequence<__sub_rank>());
> >> + }
> >> + return __ret;
> >> + }
> >> +
> >> +
> >> + template<typename _SubExtents, typename _Mapping, typename...
> _Slices>
> >> + constexpr auto
> >> + __substrides(const _Mapping& __mapping, const _Slices&...
> __slices)
> >> + {
> >> + if constexpr (__deduce_mapping_side<_Mapping>() ==
> >> _LayoutSide::__unknown)
> >> + return __substrides_generic<_SubExtents>(__mapping,
> __slices...);
> >> + else
> >> + return __substrides_standardized<_SubExtents>(__mapping,
> >> __slices...);
> >> + }
> >> +
> >> + template<typename _Slice, typename _IndexType>
> >> + concept __is_unit_stride_slice =
> >
> >
> > (__is_strided_slice<_Slice>
> >> + && __detail::__integral_constant_like<typename
> >> _Slice::stride_type>
> >>
> > Usig is_constant_wrapper instead of __integral_constant_like.
> >
> >> + && _Slice::stride_type::value == 1)
> >> + || same_as<_Slice, full_extent_t>;
> >> +
> >> + // _BlockSize - 1
> >> + // [full, ..., full, unit_slice , ...]
> >> + template<typename _IndexType, size_t _BlockSize, typename...
> _Slices>
> >>
> > I think this funciton would read much better if we do normal template
> > parameter recursion,
> > i.e. have typename _Slice, typename... _RemSlices, we should never
> search a
> > block
> > with one element.
> >
> >> + consteval bool
> >> + __is_block()
> >> + {
> >> + if constexpr (_BlockSize == 0 || _BlockSize >
> sizeof...(_Slices))
> >> + return false;
> >> + else if constexpr (_BlockSize == 1)
> >> + return __is_unit_stride_slice<_Slices...[0], _IndexType>;
> >> + else if constexpr (same_as<_Slices...[0], full_extent_t>)
> >>
> > This will be much simpler.
> >
> >> + {
> >> + auto __recurse = []<size_t... _Is>(index_sequence<_Is...>)
> >> + {
> >> + return __is_block<_IndexType, _BlockSize - 1,
> >> + _Slices...[_Is + 1]...>();
> >> + };
> >> + return __recurse(make_index_sequence<sizeof...(_Slices) -
> >> 1>());
> >> + }
> >> + else
> >> + return false;
> >>
> > + }
> >> +
> >> + // __u __u + _BlockSize - 1
> >> + // [*, full, ..., full, unit_slice, *]
> >> + template<typename _IndexType, size_t _Start, size_t _BlockSize,
> >> + typename... _Slices>
> >> + consteval size_t
> >> + __find_block()
> >>
> > Hmm, I think that this function could be implemented much easier by
> having:
> > bool full_map[sizeof...(Slices)]{ is_same_v<full_exent_t,
> Slices>....
> > };
> > Or even better having an array of classification of slices at the
> > begining of the functo
> > constexpr SliceKind kinds[sizeof...(Slices)]{ function to
> classify,
> > full_extent -> full, strided -> stride, unit };
> > And then pass it to classification functions. That will go over what
> could
> > be returned.
> >
> >
> >> + {
> >> + static_assert(_BlockSize != dynamic_extent,
> >> + "The implementation can't handle submdspans with rank ==
> >> size_t(-1)");
> >> +
> >> + if constexpr (sizeof...(_Slices) == 0)
> >> + return dynamic_extent;
> >> + else if constexpr (__is_block<_IndexType, _BlockSize,
> >> _Slices...>())
> >> + return _Start;
> >> + else
> >> + {
> >> + auto __recurse = []<size_t... _Is>(index_sequence<_Is...>)
> >> + {
> >> + return __find_block<_IndexType, _Start + 1, _BlockSize,
> >> + _Slices...[_Is + 1]...>();
> >> + };
> >> + return __recurse(make_index_sequence<sizeof...(_Slices) -
> >> 1>());
> >> + }
> >> + }
> >> +
> >> + template<typename _IndexType, size_t _SubRank, typename... _Slices>
> >> + static consteval bool
> >> + __is_compact_block()
> >> + {
> >> + if constexpr (_SubRank == 0)
> >> + return false;
> >> + else
> >> + return __find_block<_IndexType, 0, _SubRank, _Slices...>()
> == 0;
> >> + }
> >> +
> >> + // __u
> >> + // [unit_slice, i, ..., k, full, ..., full, unit_slice, *]
> >> + template<typename _IndexType, size_t _SubRank, typename _Slice,
> >> + typename... _Slices>
> >> + static consteval size_t
> >> + __padded_block_begin_generic()
> >> + {
> >> + if constexpr (!__mdspan::__is_unit_stride_slice<_Slice,
> >> _IndexType>)
> >> + return dynamic_extent;
> >> + else if constexpr (sizeof...(_Slices) == 0)
> >> + return dynamic_extent;
> >> + else
> >> + {
> >> + constexpr auto __u = __find_block<_IndexType, 0, _SubRank -
> 1,
> >> + _Slices...>();
> >> + if constexpr (__u != dynamic_extent)
> >> + return __u + 1;
> >> + else
> >> + return dynamic_extent;
> >> + }
> >> + }
> >> +
> >> + template<_LayoutSide _Side, typename _IndexType, size_t _SubRank,
> >> + typename... _Slices>
> >> + static consteval size_t
> >> + __padded_block_begin()
> >> + {
> >> + if constexpr (_Side == _LayoutSide::__left)
> >> + return __padded_block_begin_generic<_IndexType, _SubRank,
> >> + _Slices...>();
> >> + }
> >> +
> >> + 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 = __static_extents<_Extents>(0,
> _Us);
> >> + if constexpr (!__all_static(__sta_exts))
> >> + return dynamic_extent;
> >> + else
> >> + return __fwd_prod(__sta_exts);
> >> + }
> >> +
> >> + template<typename _IndexType, size_t _SubRank, typename...
> _Slices>
> >> + static consteval bool
> >> + _S_is_unpadded_submdspan()
> >> + { return __is_compact_block<_IndexType, _SubRank,
> _Slices...>();
> >> }
> >> + };
> >> +
> >> + 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;
> >> + constexpr auto __side = __deduce_mapping_side<_Mapping>();
> >> + using _Trait = _SubMdspanMapping<__side>;
> >> +
> >> + auto __offset = __suboffset(__mapping, __slices...);
> >>
> > The calls need to be qualified (and one above) and everywhere in general.
> > + auto __sub_exts = submdspan_extents(__mapping.
> > We should call __mdspan::__subextents as we already canonicalized
> > slices.
> >
> >> + using _SubExtents = decltype(__sub_exts);
> >> + constexpr auto __sub_rank = _SubExtents::rank();
> >> + if constexpr (_SubExtents::rank() == 0)
> >> + return submdspan_mapping_result{
> >> + typename _Trait::_Layout::mapping(__sub_exts), __offset};
> >> + else if constexpr (
> >> + _Trait::template _S_is_unpadded_submdspan<_IndexType,
> >> __sub_rank,
> >> + _Slices...>())
> >> + return submdspan_mapping_result{
> >> + typename _Trait::_Layout::mapping(__sub_exts), __offset};
> >> + else if constexpr (
> >> + constexpr auto __u = __padded_block_begin<__side,
> _IndexType,
> >> + __sub_rank,
> >> _Slices...>();
> >> + __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
> >> + = __substrides<_SubExtents>(__mapping, __slices...);
> >> + return submdspan_mapping_result{
> >> + layout_stride::mapping(__sub_exts, __sub_strides),
> >> __offset};
> >> + }
> >> + }
> >> #endif // __glibcxx_submdspan
> >> }
> >>
> >> template<typename _Extents>
> >> class layout_left::mapping
> >> {
> >> public:
> >> using extents_type = _Extents;
> >> using index_type = typename extents_type::index_type;
> >> using size_type = typename extents_type::size_type;
> >> @@ -1168,20 +1532,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >> template<typename _OExtents>
> >> constexpr explicit
> >> mapping(const _OExtents& __oexts, __mdspan::__internal_ctor)
> >> noexcept
> >> : _M_extents(__oexts)
> >> {
> >> static_assert(__mdspan::__representable_size<_OExtents,
> >> index_type>,
> >> "The size of OtherExtents must be representable as
> >> index_type");
> >>
> >> __glibcxx_assert(__mdspan::__is_representable_extents(_M_extents));
> >> }
> >>
> >> +#if __glibcxx_submdspan
> >> + template<__mdspan::__valid_canonical_slice_type<index_type>...
> >> _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{};
> >> };
> >>
> >> namespace __mdspan
> >> {
> >> template<typename _Extents, typename... _Indices>
> >> constexpr typename _Extents::index_type
> >> __linear_index_right(const _Extents& __exts, _Indices...
> __indices)
> >> noexcept
> >> {
> >> @@ -2824,16 +3196,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >> requires (sizeof...(_Extents) == sizeof...(_Slices))
> >> constexpr auto
> >> submdspan_canonicalize_slices(const extents<_IndexType,
> _Extents...>&
> >> __exts,
> >> _Slices... __raw_slices)
> >> {
> >> auto [...__slices]
> >> =
> make_tuple(__mdspan::__slice_cast<_IndexType>(__raw_slices)...);
> >> __mdspan::__assert_valid_slices(__exts, __slices...);
> >> return make_tuple(__slices...);
> >> }
> >> +
> >> + template<typename _ElementType, typename _Extents, typename _Layout,
> >> + typename _Accessor, typename... _Slices>
> >> + requires (sizeof...(_Slices) == _Extents::rank())
> >> + constexpr auto
> >> + submdspan(
> >> + const mdspan<_ElementType, _Extents, _Layout, _Accessor>& __md,
> >> + _Slices... __raw_slices)
> >> + {
> >> + auto [...__slices] =
> submdspan_canonicalize_slices(__md.extents(),
> >> +
> __raw_slices...);
> >>
> > Again, we do not want to instantiate tuples, so _would do __impl lambda
> > trick here,
> > were we pass canonicalized slices as arguments.
> >
> >> + auto __result = submdspan_mapping(__md.mapping(), __slices...);
> >> + return mdspan(__md.accessor().offset(__md.data_handle(),
> >> __result.offset),
> >> + __result.mapping, typename
> >> _Accessor::offset_policy(__md.accessor()));
> >> + }
> >> #endif // __glibcxx_submdspan
> >>
> >> _GLIBCXX_END_NAMESPACE_VERSION
> >> }
> >> #endif
> >> #endif
> >> 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
> >> @@ -1878,22 +1878,22 @@ export namespace std
> >> using std::layout_left_padded;
> >> using std::layout_right_padded;
> >> #endif
> >> #if __glibcxx_submdspan
> >> using std::strided_slice;
> >> using std::full_extent_t;
> >> using std::full_extent;
> >> using std::submdspan_mapping_result;
> >> using std::submdspan_canonicalize_slices;
> >> using std::submdspan_extents;
> >> + using std::submdspan;
> >> #endif
> >> - // FIXME mdsubspan
> >> }
> >> #endif
> >>
> >> // 20.2 <memory>
> >> export namespace std
> >> {
> >> using std::align;
> >> using std::allocator;
> >> using std::allocator_arg;
> >> using std::allocator_arg_t;
> >> diff --git
> >> a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc
> >> b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc
> >> new file mode 100644
> >> index 00000000000..53e91407a9c
> >> --- /dev/null
> >> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc
> >> @@ -0,0 +1,369 @@
> >> +// { dg-do run { target c++26 } }
> >> +#include <mdspan>
> >> +
> >> +#include <iostream> // TODO remove
> >> +#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
> >> + {
> >> + class EndIt
> >> + { };
> >> +
> >> + class BeginIt
> >> + {
> >> + public:
> >> + constexpr
> >> + BeginIt(const std::array<Int, Rank>& shape)
> >> + : M_shape(shape)
> >> + { }
> >> +
> >> + constexpr BeginIt&
> >> + 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; }
> >> +
> >> + constexpr bool
> >> + operator==(EndIt)
> >> + {
> >> + if constexpr (Rank > 0)
> >> + return M_indices[0] == M_shape[0];
> >> + else
> >> + return true;
> >> + }
> >> +
> >> + private:
> >> + 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 BeginIt
> >> + begin() const
> >> + { return BeginIt(M_shape); }
> >> +
> >> + constexpr EndIt
> >> + end() const
> >> + { return EndIt{}; }
> >> +
> >> + private:
> >> + std::array<Int, Rank> M_shape;
> >> + };
> >> +
> >> +constexpr bool
> >> +test_multi_index()
> >> +{
> >> + auto shape = std::array{3, 5, 7, 1};
> >> +
> >> + std::vector<std::array<int, 4>> expected;
> >> + 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)
> >> + expected.push_back(std::array{i, j, k, l});
> >> +
> >> + size_t i = 0;
> >> + for (auto actual : multi_index_generator(shape))
> >> + VERIFY(expected[i++] == actual);
> >> + 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;
> >> + }
> >> +
> >> +int
> >> +main()
> >> +{
> >> + test_all<std::layout_left>();
> >> + return 0;
> >> +}
> >> 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..4f9aad81cb7
> >> --- /dev/null
> >> +++
> >> b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc
> >> @@ -0,0 +1,102 @@
> >> +// { 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" }
> >> --
> >> 2.51.2
> >>
> >>
> >
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20251202/eef16d20/attachment-0001.htm>
More information about the Libstdc++
mailing list