[PATCH v4 1/5] libstdc++: Implement submdspan and submdspan_mapping for layout_left. [PR110352]

Tomasz Kaminski tkaminsk@redhat.com
Fri Dec 5 15:33:41 GMT 2025


On Fri, Dec 5, 2025 at 4:26 PM Luc Grosheintz <luc.grosheintz@gmail.com>
wrote:

>
>
> On 12/5/25 11:04, Tomasz Kaminski wrote:
> > On Thu, Dec 4, 2025 at 10:34 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>
> >>
> > This really looks solid to me now, thank you for the update. I will go
> over
> > the remaining ones in the afternoon.
> > Comments below:
> >   - slice-mapping is not SFINEAing correctly (body inside lambda inside
> > concept is not checked),
> >     left some suggestions
> > - we can eliminate few <size_t Size> parameters, and make it normal
> > function parameters now
> > - some comment about extracting span declaration
> > - submdspan.cc test needs to be parallelized, already is timeint out,
> left
> > some suggestion bellow,
> >    this apply to remaining patches also
> >
> >> ---
> >>   libstdc++-v3/include/std/mdspan               | 557 ++++++++++++++++--
> >>   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         | 161 +++++
> >>   5 files changed, 1163 insertions(+), 62 deletions(-)
> >>   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 bfec288aded..54739c0008d 100644
> >> --- a/libstdc++-v3/include/std/mdspan
> >> +++ b/libstdc++-v3/include/std/mdspan
> >> @@ -357,40 +357,50 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>         static_assert(__is_standard_integer<_ExtentType>::value
> >>          || __detail::__integral_constant_like<_ExtentType>);
> >>         static_assert(__is_standard_integer<_StrideType>::value
> >>          || __detail::__integral_constant_like<_StrideType>);
> >>
> >>         using offset_type = _OffsetType;
> >>         using extent_type = _ExtentType;
> >>         using stride_type = _StrideType;
> >>
> >>         [[no_unique_address]] offset_type offset{};
> >>         [[no_unique_address]] extent_type extent{};
> >>         [[no_unique_address]] stride_type stride{};
> >>       };
> >>
> >>     template<typename _Mapping>
> >>       struct submdspan_mapping_result
> >>       {
> >>         [[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>
> >>       class extents
> >>       {
> >>         static_assert(__is_standard_integer<_IndexType>::value,
> >>                      "IndexType must be a signed or unsigned integer
> type");
> >>         static_assert(
> >>            (__mdspan::__valid_static_extent<_Extents, _IndexType> &&
> ...),
> >>            "Extents must either be dynamic or representable as
> IndexType");
> >>
> >>         using _Storage = __mdspan::_ExtentsStorage<
> >>          _IndexType, array<size_t, sizeof...(_Extents)>{_Extents...}>;
> >>         [[no_unique_address]] _Storage _M_exts;
> >>
> >>       public:
> >>         using index_type = _IndexType;
> >>         using size_type = make_unsigned_t<index_type>;
> >>         using rank_type = size_t;
> >>
> >> @@ -572,40 +582,50 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>
> >>       template<typename _Extents>
> >>         constexpr typename _Extents::index_type
> >>         __fwd_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)
> >>            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)
> >>            return __r == 0 ? __exts.extent(1) : 1;
> >>          else if constexpr
> >> (__all_dynamic(std::span(__sta_exts).last(__rank-1)))
> >>            return __extents_prod(__exts, 1, __r + 1, __rank);
> >>          else
> >>            {
> >>              size_t __sta_prod = __rev_partial_prods<__sta_exts>[__r];
> >>              return __extents_prod(__exts, __sta_prod, __r + 1, __rank);
> >>            }
> >>         }
> >>
> >> @@ -849,64 +869,503 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>          return std::cmp_less_equal(__min, __value)
> >>              && std::cmp_less_equal(__value, __max);
> >>         }
> >>
> >>       template<typename _Tp>
> >>         constexpr bool __is_constant_wrapper = false;
> >>
> >>       template<_CwFixedValue _Xv, typename _Tp>
> >>         constexpr bool __is_constant_wrapper<constant_wrapper<_Xv, _Tp>>
> >>          = true;
> >>
> >>       template<size_t _Index, typename _Extents>
> >>         constexpr auto
> >>         __extract_extent(const _Extents& __exts)
> >>         {
> >>          using _IndexType = typename _Extents::index_type;
> >>          return extents<_IndexType, _Extents::static_extent(_Index)>{
> >>            __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()
> >>         {
> >>          return (static_cast<size_t>(!convertible_to<_Slices,
> _IndexType>)
> >>                  + ... + 0);
> >>         }
> >>
> >>       template<typename _IndexType, typename... _Slices>
> >>         consteval auto
> >>         __inv_map_rank()
> >>         {
> >>          constexpr auto __rank = sizeof...(_Slices);
> >>          constexpr auto __sub_rank = __subrank<_IndexType,
> _Slices...>();
> >>          auto __map = std::array<size_t, __sub_rank>{};
> >>          auto __is_int_like = std::array<bool, __rank>{
> >>            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 _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)>)
> >> +                 __ret[__krev] = __stride * __slice.stride;
> >>
> > We seem to be missing the check that __slice.stride <  __slice.extent.
> >
> >> +               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>()...};
> >> +      }
> >> +
> >> +    //                   _BlockSize - 1
> >> +    // [full, ..., full, unit_slice    , *]
> >> +    template<size_t _BlockSize>
> >>
> > Make this a normal function parameter, i.e. size_t _BlockSioe.
> >
> >> +      consteval bool
> >> +      __is_block(span<const _SliceKind> __slice_kinds)
> >> +      {
> >> +       if (_BlockSize == 0)
> >> +         return false;
> >> +
> >> +       if (_BlockSize > __slice_kinds.size())
> >> +         return false;
> >> +
> >> +       for (size_t __i = 0; __i < _BlockSize - 1; ++__i)
> >> +         if (__slice_kinds[__i] != _SliceKind::__full)
> >> +           return false;
> >> +
> >> +       auto __last = __slice_kinds[_BlockSize - 1];
> >> +       return __last == _SliceKind::__full
> >> +           || __last == _SliceKind::__unit_strided_slice;
> >> +      }
> >> +
> >> +    //                         __u              __u + _SubRank-2
> >> +    // [unit_slice, i, ..., k, full, ..., full, unit_slice, *]
> >> +    template<size_t _SubRank>
> >>
> > // Same here for _SubRank
> >
> >> +      static consteval size_t
> >> +      __padded_block_begin_generic(span<const _SliceKind>
> __slice_kinds)
> >> +      {
> >> +       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
> >> +         {
> >> +           static_assert(_SubRank != dynamic_extent,
> >> +             "The implementation can't handle submdspans with rank ==
> >> size_t(-1)");
> >> +
> >> +           size_t __u = 1;
> >> +           while(__u < __slice_kinds.size()
> >> +                 && __slice_kinds[__u] == _SliceKind::__collapsing)
> >> +             ++__u;
> >> +
> >> +           if
> >> (__mdspan::__is_block<_SubRank-1>(__slice_kinds.subspan(__u)))
> >> +             return __u;
> >> +           return dynamic_extent;
> >>
> > That looks really nice.
> >
> >> +         }
> >> +      }
> >> +
> >> +    template<_LayoutSide _Side, size_t _SubRank, size_t _Nm>
> >>
> > Again, make _SubRank a template parameter.
> >
> >> +      static consteval size_t
> >> +      __padded_block_begin(span<const _SliceKind, _Nm> __slice_kinds)
> >> +      {
> >> +       if constexpr (_Side == _LayoutSide::__left)
> >> +         return
> >> __mdspan::__padded_block_begin_generic<_SubRank>(__slice_kinds);
> >> +       else
> >> +         {
> >> +           std::array<_SliceKind, _Nm> __rev_slice_kinds;
> >>
> > I would name it __rev_slice_array
> >
> >> +           for(size_t __i = 0; __i < _Nm; ++__i)
> >> +             __rev_slice_kinds[__i] = __slice_kinds[_Nm - 1 - __i];
> >>
> > And declare below.
> >                  std::span<const _SliceKind, _Nm>
> > __rev_slick_kinds(__rev_slice_array);
> >
> >> +
> >> +           auto __u = __mdspan::__padded_block_begin_generic<_SubRank>(
> >> +               std::span<const _SliceKind>(__rev_slice_kinds));
> >> +           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 _SubRank, size_t _Nm>
> >> +         static consteval bool
> >> +         _S_is_unpadded_submdspan(span<const _SliceKind, _Nm>
> >> __slice_kinds)
> >> +         { return __mdspan::__is_block<_SubRank>(__slice_kinds); }
> >> +      };
> >> +
> >> +    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)
> >> +      {
> >> +       constexpr auto __side = __mdspan::__mapping_side<_Mapping>();
> >> +       using _Trait = _SubMdspanMapping<__side>;
> >> +
> >> +       constexpr auto __slice_kinds =
> >> __mdspan::__make_slice_kind_array<_Slices...>();
> >>
> > Again here, declaring __slice_kinds_array and __slice_kind as span below
> > it, we are creating
> > the span 3 times below.
> >
>
> I might be doing it wrong, but the compiler is adamant that this isn't
> permitted. Essentially, it says that __slice_kinds_span isn't a constant
> expression. Clang expands: it's because the pointer in __slice_kinds_array
> can be different on different invocations.
>
> Godbolt: https://godbolt.org/z/vPPhPxva6

Ah, yes they are right for C++23 (C++26 changed the rules here, but we do
not implement them). I think using ctad for these few examples is fine, so
std::span(__slice_kinds), as this will deduce the static-size. We could also
have typedef for the span, to make calls simpler.

>
>
> Easy fix: pass the array by value (just this once). We anyway need to know
> the size, passing as an array, ensures that the size isn't `size_t(-1)`,
> accidentally. Since we never need subspans of the array (at this point)
> it should be fine. (There's no copying because it's always passed to
> consteval functions).
>
There is copying during compile time, which makes the compilation slower.

>
> >> +       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::template _S_is_unpadded_submdspan<__sub_rank>(
> >> +             std::span<const _SliceKind,
> >> __slice_kinds.size()>(__slice_kinds)))
> >> +         return submdspan_mapping_result{
> >> +           typename _Trait::_Layout::mapping(__sub_exts), __offset};
> >> +       else if constexpr (
> >> +           constexpr auto __u = __padded_block_begin<__side,
> __sub_rank>(
> >> +             span<const _SliceKind,
> __slice_kinds.size()>(__slice_kinds));
> >> +           __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};
> >> +         }
> >> +      }
> >> +
> >> +    // Enables ADL-only calls from submdspan.
> >> +    void submdspan_mapping() = delete;
> >>
> > Please put it before the concept __sliceable_mapping definition.
> >
> >>   #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;
> >>         using rank_type = typename extents_type::rank_type;
> >>         using layout_type = layout_left;
> >>
> >>         static_assert(__mdspan::__representable_size<extents_type,
> >> index_type>,
> >>          "The size of extents_type must be representable as
> index_type");
> >>
> >>         constexpr
> >>         mapping() noexcept = default;
> >>
> >>         constexpr
> >> @@ -1014,40 +1473,48 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>         }
> >>
> >>         template<typename _OExtents>
> >>          requires (extents_type::rank() == _OExtents::rank())
> >>          friend constexpr bool
> >>          operator==(const mapping& __self, const mapping<_OExtents>&
> >> __other)
> >>          noexcept
> >>          { return __self.extents() == __other.extents(); }
> >>
> >>       private:
> >>         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::__acceptable_slice_type<index_type>...
> _Slices>
> >>
> > Make this a static assert inside submdspan_impl, we do not need a concept
> > check here.
> >
> >> +       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
> >>         {
> >>          using _IndexType = typename _Extents::index_type;
> >>          array<_IndexType, sizeof...(__indices)>
> __ind_arr{__indices...};
> >>          _IndexType __res = 0;
> >>          if constexpr (sizeof...(__indices) > 0)
> >>            {
> >>              _IndexType __mult = 1;
> >>              auto __update = [&, __pos = __exts.rank()](_IndexType)
> mutable
> >>                {
> >>                  --__pos;
> >>                  _GLIBCXX_DEBUG_ASSERT(cmp_less(__ind_arr[__pos],
> >> @@ -2673,118 +3140,86 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>          else if constexpr (__is_constant_wrapper<_Slice>
> >>                             && _Extent != dynamic_extent)
> >>            static_assert(std::cmp_less(_Slice::value, _Extent));
> >>          else if constexpr (convertible_to<_Slice, _IndexType>)
> >>            __glibcxx_assert(__slice < __ext.extent(0));
> >>         }
> >>
> >>       template<typename _Extents, typename... _Slices>
> >>         constexpr void
> >>         __check_valid_slices(const _Extents& __exts, const _Slices&...
> >> __slices)
> >>         {
> >>          constexpr auto __rank = _Extents::rank();
> >>          auto __impl = [&]<size_t... _Is>(index_sequence<_Is...>)
> >>          {
> >>
> ((__mdspan::__check_valid_slice(__extract_extent<_Is>(__exts),
> >>                                            __slices...[_Is])),...);
> >>          };
> >>          __impl(make_index_sequence<__rank>());
> >>         }
> >>
> >> -    template<typename _IndexType, size_t _Extent, typename _Slice>
> >> -      consteval size_t
> >> -      __static_slice_extent()
> >>
> >
> >
> >> +    template<typename _Mapping>
> >> +      concept __sliceable_mapping = requires(_Mapping __m)
> >>         {
> >> -       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>(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()>());
> >> -      }
> >> +         [__m]<size_t... _Is>(index_sequence<_Is...>)
> >> +         {
> >> +           return submdspan_mapping(__m, ((void) _Is, full_extent)...);
> >> +         }(make_index_sequence<_Mapping::extents_type::rank()>())
> >>
> > This is not doing the check correctly, as the lambda body is not
> considered
> > part of SFINAE.
> > (Please add a test that on mdspan with custom mapping without submapping,
> > doing
> >    require { submdspan(m, slices....); }
> > returns false.
> >
> > To bring this as part of SFINAE, we should put the call inside the return
> > type of lamba:
> >           [__m]<size_t... _Is>(index_sequence<_Is...>)
> >           -> decltype(submdspan_mapping(__m, ((void) _Is,
> full_extent)...))
> >           {  return submdspan_mapping(__m, ((void) _Is,
> full_extent)...); }
> >
> >    template<typename _Mapping, typename... Slices>
> >      concept __sliceable_mapping = requires(_Mapping __m, Slices...
> slices)
> >     {
> >         { submdspan_mapping(__m, slices...); } ->
> __submdspan_mapping_result;
> >     }
> >
> > And then checking __mdspan::__sliceable_mapping<typename
> > _Layout::mapping<_Extents>, __mdspan::__full_extent_t<Slices>...>.
> > This will give much more readable error message.
> >
> >> +       } -> __submdspan_mapping_result;
> >> +      };
> >>     }
> >>
> >>     template<typename _IndexType, size_t... _Extents, typename...
> >> _RawSlices>
> >>       requires (sizeof...(_RawSlices) == sizeof...(_Extents))
> >>       constexpr auto
> >>       submdspan_extents(const extents<_IndexType, _Extents...>& __exts,
> >>                        _RawSlices... __raw_slices)
> >>       {
> >>         auto __impl = [&__exts](auto... __slices)
> >>         {
> >>          __mdspan::__check_valid_slices(__exts, __slices...);
> >>          return __mdspan::__subextents(__exts, __slices...);
> >>         };
> >>         return
> __impl(__mdspan::__slice_cast<_IndexType>(__raw_slices)...);
> >>       }
> >>
> >>     template<typename _IndexType, size_t... _Extents, typename...
> >> _RawSlices>
> >>       requires (sizeof...(_Extents) == sizeof...(_RawSlices))
> >>       constexpr auto
> >>       submdspan_canonicalize_slices(const extents<_IndexType,
> _Extents...>&
> >> __exts,
> >>                                    _RawSlices... __raw_slices)
> >>       {
> >>         auto __impl = [&__exts](auto... __slices)
> >>         {
> >>          __mdspan::__check_valid_slices(__exts, __slices...);
> >>          return std::make_tuple(__slices...);
> >>         };
> >>         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>>)
> >> +    constexpr auto
> >> +    submdspan(
> >> +       const mdspan<_ElementType, _Extents, _Layout, _Accessor>& __md,
> >> +       _RawSlices... __raw_slices)
> >> +    {
> >> +      using _IndexType = typename _Extents::index_type;
> >> +      auto __impl = [&__md](auto... __slices)
> >> +      {
> >> +       using __mdspan::submdspan_mapping;
> >>
> > I would define the __subbmapping function inside __mdspan below the
> concet
> > that will return submdspan_mapping_result.
> > This will avoid the need for this using:
> > template<typename _Mapping, typename... Slices>
> > constexpr auto __submapping(const _Mapping& __map, _Slices... slices)
> >   {
> >       __mdspan::__check_valid_slices(__md.extents(), __slices...);
> >      return __result = submdspan_mapping(__md.mapping(), __slices...);
> > }
> >
> >
> >
> >> +       __mdspan::__check_valid_slices(__md.extents(), __slices...);
> >> +       auto __result = submdspan_mapping(__md.mapping(), __slices...);
> >>
> > I would use auto [__mapping, __offset] =
> submdspan_mapping(__md.mapping(),
> > __slices...) here.
> >
> >> +       return std::mdspan(__md.accessor().offset(__md.data_handle(),
> >> __result.offset),
> >> +           __result.mapping, typename
> >> _Accessor::offset_policy(__md.accessor()));
> >>
> > I would move mapping here, the user defined one may have more efficient
> > moves.
> >
> >> +      };
> >> +      return
> __impl(__mdspan::__slice_cast<_IndexType>(__raw_slices)...);
> >> +    }
> >>   #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
> >> @@ -1868,42 +1868,42 @@ export namespace std
> >>   #endif
> >>     using std::layout_left;
> >>     using std::layout_right;
> >>     using std::layout_stride;
> >>     using std::default_accessor;
> >>   #if __glibcxx_aligned_accessor
> >>     using std::aligned_accessor;
> >>   #endif
> >>     using std::mdspan;
> >>   #if __glibcxx_padded_layouts
> >>     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;
> >>     using std::allocator_traits;
> >>     using std::assume_aligned;
> >>   #if __glibcxx_is_sufficiently_aligned
> >>     using std::is_sufficiently_aligned;
> >>   #endif
> >>     using std::make_obj_using_allocator;
> >>     using std::pointer_traits;
> >>     using std::to_address;
> >>     using std::uninitialized_construct_using_allocator;
> >>     using std::uses_allocator;
> >> 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 } }
> >>
> > This test is already timeout out in the debug mode, my suggestion would
> be,
> > create and mdspan dir, where you will have the test machinery in header,
> > and then inside files for instantiating for each layout, with separation
> > for
> > cheap and expensive tests. I will later in separate commit.
> > We should also add, to the test.
> > // { dg-timeout-factor 2.0 }
> >
> > I will then increase value as needed.
> >
> > +#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
> >>
> > These are usually named sentinel.
> > I would just name it struct sentinel
> >
> >> +    { };
> >> +
> >> +    class BeginIt
> >>
> > // And this, just name the iterator.
> >
> >> +    {
> >> +    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)
> >>
> > Making this hidden friend:
> > constexpr friend bool operator==(const BeginIt&, _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)
> >
> > Instead of creating vector of array, I would do:
> > multi_index_generator gen(shape);
> > auto it = gen.begin();
> > auto end = gen.end();
> >
> > And then inside loop:
> >       {
> >          VERIFY(it != end);
> >          VERIFY(*it == std::array{i, j, k, l});
> >         ++it;
> >      }
> >
> >
> >> +         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..cdc8a2b7e23
> >> --- /dev/null
> >> +++
> >> b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc
> >> @@ -0,0 +1,161 @@
> >> +// { 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" }
> >> +
> >> +namespace adl
> >> +{
> >> +  struct NoFull
> >>
> > Add test with no submdpsan_mapping at all,
> > it should then be checkable with requires { submdspan(mapping, slice,
> > slice); }
> > Test will only one dimension should be fine.
> >
> >> +  {
> >> +    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; }
> >> +      };
> >> +  };
> >> +}
> >> +
> >> +constexpr bool
> >> +test_invalid_mapping1()
> >> +{
> >> +  using Extents = std::extents<int, 3>;
> >> +  auto map = adl::NoFull::mapping<Extents>{};
> >> +  std::mdspan<double, Extents, adl::NoFull> m(nullptr, map);
> >> +  std::submdspan(m, 1); // { dg-error "no matching function" }
> >> +  return true;
> >> +}
> >> +static_assert(test_invalid_mapping1());
> >> +
> >> +constexpr bool
> >> +test_invalid_mapping2()
> >> +{
> >> +  using Extents = std::extents<int, 3>;
> >> +  auto map = adl::WrongReturnValue::mapping<Extents>{};
> >> +  std::mdspan<double, Extents, adl::WrongReturnValue>  m(nullptr, map);
> >> +  std::submdspan(m, std::full_extent); // { dg-error "no matching
> >> function" }
> >> +  return true;
> >> +}
> >> +static_assert(test_invalid_mapping2());
> >> +
> >> +// { 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
> >>
> >>
> >
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20251205/1ee79cff/attachment-0001.htm>


More information about the Libstdc++ mailing list