On Mon, Dec 8, 2025 at 9:21 PM Luc Grosheintz <[email protected]>
wrote:

> Implements `submdspan` and `submdspan_mapping` for layout_left as
> described in P3663 (Future proofing mdspan).
>
> When computing the offset of the submdspan, one must check that the
> lower bound of the slice range isn't out-of-range. There's a few
> cases when the lower bound is never out-of-range:
>
>   - full_extent and exts.extent(k) != 0,
>   - collapsing slice types.
>
> If those conditions are known to hold, no checks are generated.
>
> Similarly, if all slices are full_extent, there's no need to call
> mapping(0,...,0) for standardized mappings.
>
> The implementation prepares to use the symmetry between layout_left and
> layout_right and introduces concepts like a "layout side", i.e. left,
> right or unknown/strided.
>
> The tests use an iterator to replace nested for-loops. Which also makes
> it easier to write the core test logic in a rank-independent manner.
>
>         PR libstdc++/110352
>
> libstdc++-v3/ChangeLog:
>
>         * include/std/mdspan (layout_left::mapping::submdspan_mapping):
>         New friend function.
>         (submdspan): New function.
>         * src/c++23/std.cc.in: Add submdspan.
>         * testsuite/23_containers/mdspan/submdspan/generic.cc: New test.
>         * testsuite/23_containers/mdspan/submdspan/selections/left.cc:
>         Instantiate selection tests for layout_left.
>         * testsuite/23_containers/mdspan/submdspan/selections/testcases.h:
> Generic
>         tests different selections.
>         * testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc:
> New test.
>         * testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc: New
> test.
>
> Signed-off-by: Luc Grosheintz <[email protected]>
> ---
>
Two very small cases, I will implement, add them locally and post a updated
patch.

>  libstdc++-v3/include/std/mdspan               | 562 ++++++++++++++++--
>  libstdc++-v3/src/c++23/std.cc.in              |   2 +-
>  .../23_containers/mdspan/submdspan/generic.cc |  71 +++
>  .../mdspan/submdspan/selections/left.cc       |   9 +
>  .../mdspan/submdspan/selections/testcases.h   | 360 +++++++++++
>  .../mdspan/submdspan/submdspan_mapping.cc     | 136 +++++
>  .../mdspan/submdspan/submdspan_neg.cc         | 104 ++++
>  7 files changed, 1187 insertions(+), 57 deletions(-)
>  create mode 100644
> libstdc++-v3/testsuite/23_containers/mdspan/submdspan/generic.cc
>  create mode 100644
> libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/left.cc
>  create mode 100644
> libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/testcases.h
>  create mode 100644
> libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc
>  create mode 100644
> libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc
>
> diff --git a/libstdc++-v3/include/std/mdspan
> b/libstdc++-v3/include/std/mdspan
> index cef658da470..4c004335289 100644
> --- a/libstdc++-v3/include/std/mdspan
> +++ b/libstdc++-v3/include/std/mdspan
> @@ -374,6 +374,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>        [[no_unique_address]] _Mapping mapping = _Mapping();
>        size_t offset{};
>      };
> +
> +  template<typename _Tp>
> +    constexpr bool __is_submdspan_mapping_result = false;
> +
> +  template<typename _Mapping>
> +    constexpr bool
> __is_submdspan_mapping_result<submdspan_mapping_result<_Mapping>> = true;
> +
> +  template<typename _Mapping>
> +    concept __submdspan_mapping_result =
> __is_submdspan_mapping_result<_Mapping>;
> +
>  #endif // __glibcxx_submdspan
>
>    template<typename _IndexType, size_t... _Extents>
> @@ -589,6 +599,16 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>           }
>        }
>
> +    template<typename _IndexType, size_t _Nm>
> +      consteval _IndexType
> +      __fwd_prod(span<const _IndexType, _Nm> __values)
> +      {
> +       _IndexType __ret = 1;
> +       for(auto __value : __values)
> +         __ret *= __value;
> +       return __ret;
> +      }
> +
>      // Preconditions: _r < _Extents::rank()
>      template<typename _Extents>
>        constexpr typename _Extents::index_type
> @@ -866,6 +886,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>           __exts.extent(_Index)};
>        }
>
> +    template<typename _Slice, typename _IndexType>
> +      concept __acceptable_slice_type = same_as<_Slice, full_extent_t>
> +         || same_as<_Slice, _IndexType> || __is_constant_wrapper<_Slice>
> +         || __is_strided_slice<_Slice>;
> +
>      template<typename _IndexType, typename... _Slices>
>        consteval auto
>        __subrank()
> @@ -890,6 +915,443 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>             __map[__i++] = __k;
>         return __map;
>        }
> +
> +    template<typename _Slice>
> +      constexpr auto
> +      __slice_begin(_Slice __slice)
> +      {
> +       if constexpr (same_as<_Slice, full_extent_t>)
> +         return 0;
> +       else if constexpr (__is_strided_slice<_Slice>)
> +         return __slice.offset;
> +       else
> +         return __slice; // collapsing slice
> +      }
> +
> +    template<typename _Mapping, typename... _Slices>
> +      constexpr size_t
> +      __suboffset(const _Mapping& __mapping, const _Slices&... __slices)
> +      {
> +       using _IndexType = typename _Mapping::index_type;
> +       auto __any_past_the_end = [&]<size_t...
> _Is>(index_sequence<_Is...>)
> +       {
> +         auto __is_past_the_end = [](const auto& __slice, const auto&
> __ext)
> +         {
> +           using _Slice = remove_cvref_t<decltype(__slice)>;
> +           if constexpr (is_convertible_v<_Slice, _IndexType>)
> +             return false;
> +           else if constexpr (same_as<_Slice, full_extent_t>
> +               && __ext.static_extent(0) != 0
> +               && __ext.static_extent(0) != dynamic_extent)
> +             return false;
> +           else
> +             return __mdspan::__slice_begin(__slice) == __ext.extent(0);
> +         };
> +
> +         const auto& __exts = __mapping.extents();
> +         return ((__is_past_the_end(__slices...[_Is],
> +
> __mdspan::__extract_extent<_Is>(__exts))) || ...);
> +       };
> +
> +       if constexpr ((same_as<_Slices, full_extent_t> && ...))
> +         return __mdspan::__offset(__mapping);
> +
> +       if
> (__any_past_the_end(std::make_index_sequence<sizeof...(__slices)>()))
> +         return __mapping.required_span_size();
> +       return __mapping(__mdspan::__slice_begin(__slices)...);
> +      }
> +
> +    template<typename _IndexType, size_t _Extent, typename _Slice>
> +      consteval size_t
> +      __static_slice_extent()
> +      {
> +       if constexpr (same_as<_Slice, full_extent_t>)
> +         return _Extent;
> +       else if constexpr (same_as<_Slice,
> constant_wrapper<_IndexType(0)>>)
> +         return 0;
> +       else if constexpr (__is_constant_wrapper<typename
> _Slice::extent_type>
> +                       && __is_constant_wrapper<typename
> _Slice::stride_type>)
> +         return 1 + ((typename _Slice::extent_type{}) - 1)
> +                  / (typename _Slice::stride_type{});
> +       else
> +         return dynamic_extent;
> +      }
> +
> +    template<size_t _K, typename _Extents, typename _Slice>
> +      constexpr typename _Extents::index_type
> +      __dynamic_slice_extent(const _Extents& __exts, _Slice __slice)
> +      {
> +       if constexpr (__is_strided_slice<_Slice>)
> +         return __slice.extent == 0 ? 0 : 1 + (__slice.extent - 1) /
> __slice.stride;
> +       else
> +         return __exts.extent(_K);
> +      }
> +
> +    template<typename _IndexType, size_t... _Extents, typename... _Slices>
> +      requires (sizeof...(_Slices) == sizeof...(_Extents))
> +      constexpr auto
> +      __subextents(const extents<_IndexType, _Extents...>& __exts,
> +                  _Slices... __slices)
> +      {
> +       constexpr auto __inv_map = __mdspan::__inv_map_rank<_IndexType,
> _Slices...>();
> +       auto __impl = [&]<size_t...
> _Indices>(std::index_sequence<_Indices...>)
> +       {
> +         using _SubExts = extents<_IndexType,
> +             __mdspan::__static_slice_extent<_IndexType,
> +                _Extents...[__inv_map[_Indices]],
> +                _Slices...[__inv_map[_Indices]]>()...>;
> +         if constexpr (_SubExts::rank_dynamic() == 0)
> +           return _SubExts{};
> +         else
> +           {
> +             using _StaticSubExtents = __mdspan::_StaticExtents<
> +               __mdspan::__static_extents<_SubExts>()>;
> +             auto __create = [&]<size_t...
> _Is>(std::index_sequence<_Is...>)
> +             {
> +               constexpr auto __slice_idx = [__inv_map](size_t __i)
> consteval
> +               {
> +                 return
> __inv_map[_StaticSubExtents::_S_dynamic_index_inv(__i)];
> +               };
> +
> +               return
> _SubExts{__mdspan::__dynamic_slice_extent<__slice_idx(_Is)>(
> +                    __exts, __slices...[__slice_idx(_Is)])...};
> +             };
> +             constexpr auto __dyn_subrank = _SubExts::rank_dynamic();
> +             return __create(std::make_index_sequence<__dyn_subrank>());
> +           }
> +       };
> +
> +       return __impl(std::make_index_sequence<__inv_map.size()>());
> +      }
> +
> +    enum class _LayoutSide
> +    {
> +      __left,
> +      __right,
> +      __unknown
> +    };
> +
> +    template<typename _Mapping>
> +      consteval _LayoutSide
> +      __mapping_side()
> +      {
> +       if constexpr (__is_left_padded_mapping<_Mapping>
> +           || __mapping_of<layout_left, _Mapping>)
> +         return _LayoutSide::__left;
> +       if constexpr (__is_right_padded_mapping<_Mapping>
> +           || __mapping_of<layout_right, _Mapping>)
> +         return _LayoutSide::__right;
> +       else
> +         return _LayoutSide::__unknown;
> +      }
> +
> +    template<_LayoutSide _Side, size_t _Rank>
> +      struct _StridesTrait
> +      {
> +       static constexpr const _LayoutSide _S_side = _Side;
> +
> +       static constexpr size_t
> +       _S_idx(size_t __k) noexcept
> +       {
> +         if constexpr (_Side == _LayoutSide::__left)
> +           return __k;
> +         else
> +           return _Rank - 1 - __k;
> +       }
> +
> +       // Unifies the formulas for computing strides for padded and
> unpadded
> +       // layouts.
> +       template<typename _Mapping>
> +         static constexpr typename _Mapping::index_type
> +         _S_padded_extent(const _Mapping& __mapping, size_t __k)
> +         {
> +           if (__k == 0)
> +             return __mapping.stride(_S_idx(1));
> +           else
> +             return __mapping.extents().extent(_S_idx(__k));
> +         }
> +
> +       template<typename _IndexType, typename... _Slices>
> +         static consteval auto
> +         _S_inv_map()
> +         {
> +           static_assert(_Side != _LayoutSide::__unknown);
> +           auto __impl = [&]<size_t... _Is>(std::index_sequence<_Is...>)
> +           {
> +             return __mdspan::__inv_map_rank<_IndexType,
> _Slices...[_S_idx(_Is)]...>();
> +           };
> +           return __impl(std::make_index_sequence<_Rank>());
> +         }
> +      };
> +
> +    template<typename _SubExts, typename _Mapping, typename... _Slices>
> +      constexpr auto
> +      __substrides_generic(const _Mapping& __mapping, const _Slices&...
> __slices)
> +      {
> +       using _IndexType = typename _Mapping::index_type;
> +       if constexpr (_SubExts::rank() == 0)
> +         return array<_IndexType, _SubExts::rank()>{};
> +       else
> +         {
> +           auto __stride = [&__mapping](size_t __k, auto __slice) ->
> _IndexType
> +           {
> +             if constexpr (__is_strided_slice<decltype(__slice)>)
> +               if (__slice.stride < __slice.extent)
> +                 return __mapping.stride(__k) * __slice.stride;
> +             return __mapping.stride(__k);
> +           };
> +
> +           auto __impl = [&]<size_t... _Is>(std::index_sequence<_Is...>)
> +           {
> +             constexpr auto __inv_map
> +               = __mdspan::__inv_map_rank<_IndexType, _Slices...>();
> +             return array<_IndexType, _SubExts::rank()>{
> +               __stride(__inv_map[_Is], __slices...[__inv_map[_Is]])...};
> +           };
> +           return __impl(std::make_index_sequence<_SubExts::rank()>());
> +         }
> +      };
> +
> +    template<typename _SubExts, typename _Mapping, typename... _Slices>
>
+      constexpr auto
> +      __substrides_standardized(const _Mapping& __mapping,
> +                              const _Slices&... __slices)
> +      {
> +       using _IndexType = typename _Mapping::index_type;
> +       using _Trait = _StridesTrait<__mapping_side<_Mapping>(),
> +                                    _Mapping::extents_type::rank()>;
> +       using _SubTrait = _StridesTrait<__mapping_side<_Mapping>(),
> _SubExts::rank()>;
> +
> +       constexpr size_t __sub_rank = _SubExts::rank();
> +
> +       std::array<_IndexType, __sub_rank> __ret;
> +       if constexpr (__sub_rank > 0)
> +         {
> +           constexpr auto __inv_map
> +             = _Trait::template _S_inv_map<_IndexType, _Slices...>();
> +           auto __loop = [&]<size_t... _Ks>(std::index_sequence<_Ks...>)
> +           {
> +             size_t __i0 = 0;
> +             size_t __stride = 1;
> +             auto __body = [&](size_t __k, auto __slice)
> +             {
> +               for (size_t __i = __i0; __i < __inv_map[__k]; ++__i)
> +                 __stride *= _Trait::_S_padded_extent(__mapping, __i);
> +
> +               size_t __krev = _SubTrait::_S_idx(__k);
> +               if constexpr (__is_strided_slice<decltype(__slice)>)
> +                 {
> +                   if (__slice.stride < __slice.extent)
> +                     __ret[__krev] = __stride * __slice.stride;
> +                   else
> +                     __ret[__krev] = __stride;
> +                 }
> +               else
> +                 __ret[__krev] = __stride;
> +
> +               __i0 = __inv_map[__k];
> +             };
> +
> +             ((__body(_Ks,
> __slices...[_Trait::_S_idx(__inv_map[_Ks])])),...);
> +           };
> +           __loop(std::make_index_sequence<__sub_rank>());
> +         }
> +       return __ret;
> +      }
> +
> +
> +    template<typename _SubExts, typename _Mapping, typename... _Slices>
> +      constexpr auto
> +      __substrides(const _Mapping& __mapping, const _Slices&... __slices)
> +      {
> +       if constexpr (__mdspan::__mapping_side<_Mapping>() ==
> _LayoutSide::__unknown)
> +         return __mdspan::__substrides_generic<_SubExts>(__mapping,
> __slices...);
> +       else
> +         return __mdspan::__substrides_standardized<_SubExts>(__mapping,
> __slices...);
> +      }
> +
> +    template<typename _Slice>
> +      concept __is_unit_stride_slice =
> (__mdspan::__is_strided_slice<_Slice>
> +         && __mdspan::__is_constant_wrapper<typename _Slice::stride_type>
> +         && _Slice::stride_type::value == 1)
> +       || std::same_as<_Slice, full_extent_t>;
> +
> +    // These are (forced) exclusive categories:
> +    //  - full & collapsing: obvious,
> +    //  - unit_strided_slice: strided_slice{a, b, cw<1>}, but not `full`,
> +    //  - strided_slice: strided_slice{a, b, c} with c != cw<1>.
> +    enum class _SliceKind
> +    {
> +      __strided_slice,
> +      __unit_strided_slice,
> +      __full,
> +      __collapsing
> +    };
> +
> +    template<typename _Slice>
> +      consteval _SliceKind
> +      __make_slice_kind()
> +      {
> +       if constexpr (std::same_as<_Slice, full_extent_t>)
> +         return _SliceKind::__full;
> +       else if constexpr (__mdspan::__is_strided_slice<_Slice>)
> +         {
> +           if constexpr (__mdspan::__is_unit_stride_slice<_Slice>)
> +             return _SliceKind::__unit_strided_slice;
> +           else
> +             return _SliceKind::__strided_slice;
> +         }
> +       else
> +         return _SliceKind::__collapsing;
> +      }
> +
> +    template<typename... _Slices>
> +      consteval array<_SliceKind, sizeof...(_Slices)>
> +      __make_slice_kind_array()
> +      {
> +       return array<_SliceKind, sizeof...(_Slices)>{
> +         __mdspan::__make_slice_kind<_Slices>()...};
> +      }
> +
> +    //                   __block_size - 1
> +    // [full, ..., full, unit_slice    , *]
> +    consteval bool
> +    __is_block(span<const _SliceKind> __slice_kinds, size_t __block_size)
> +    {
> +      if (__block_size == 0)
> +       return false;
> +
> +      if (__block_size > __slice_kinds.size())
> +       return false;
> +
> +      for (size_t __i = 0; __i < __block_size - 1; ++__i)
> +       if (__slice_kinds[__i] != _SliceKind::__full)
> +         return false;
> +
> +      auto __last = __slice_kinds[__block_size - 1];
> +      return __last == _SliceKind::__full
> +         || __last == _SliceKind::__unit_strided_slice;
> +    }
> +
> +    //                         __u              __u + __sub_rank-2
> +    // [unit_slice, i, ..., k, full, ..., full, unit_slice, *]
> +    static consteval size_t
> +    __padded_block_begin_generic(span<const _SliceKind> __slice_kinds,
> +                                size_t __sub_rank)
> +    {
> +      if (__slice_kinds[0] != _SliceKind::__full
> +         && __slice_kinds[0] != _SliceKind::__unit_strided_slice)
> +       return dynamic_extent;
> +      else if (__slice_kinds.size() == 1)
> +       return dynamic_extent;
> +      else
> +       {
> +         size_t __u = 1;
> +         while(__u < __slice_kinds.size()
> +               && __slice_kinds[__u] == _SliceKind::__collapsing)
> +           ++__u;
> +
> +         if (__mdspan::__is_block(__slice_kinds.subspan(__u), __sub_rank
> -1))
> +           return __u;
> +         return dynamic_extent;
> +       }
> +    }
> +
> +    template<_LayoutSide _Side, size_t _Nm>
> +      static consteval size_t
> +      __padded_block_begin(span<const _SliceKind, _Nm> __slice_kinds,
> size_t __sub_rank)
> +      {
> +       if constexpr (_Side == _LayoutSide::__left)
> +         return __mdspan::__padded_block_begin_generic(__slice_kinds,
> __sub_rank);
> +       else
> +         {
> +           std::array<_SliceKind, _Nm> __rev_slices;
> +           for(size_t __i = 0; __i < _Nm; ++__i)
> +             __rev_slices[__i] = __slice_kinds[_Nm - 1 - __i];
> +           auto __rev_slice_kinds = span<const _SliceKind>(__rev_slices);
> +
> +           auto __u =
> __mdspan::__padded_block_begin_generic(__rev_slice_kinds,
> +                                                             __sub_rank);
> +           return __u == dynamic_extent ? dynamic_extent : _Nm - 1 - __u;
> +         }
> +      }
> +
> +    template<_LayoutSide _Side>
> +      struct _SubMdspanMapping;
> +
> +    template<>
> +      struct _SubMdspanMapping<_LayoutSide::__left>
> +      {
> +       using _Layout = layout_left;
> +       template<size_t _Pad> using _PaddedLayout =
> layout_left_padded<_Pad>;
> +
> +       template<typename _Mapping, size_t _Us>
> +         static consteval size_t
> +         _S_pad()
> +         {
> +           using _Extents = typename _Mapping::extents_type;
> +           constexpr auto __sta_exts =
> __mdspan::__static_extents<_Extents>(0, _Us);
> +           if constexpr (!__mdspan::__all_static(__sta_exts))
> +             return dynamic_extent;
> +           else
> +             return __mdspan::__fwd_prod(__sta_exts);
> +         }
> +
> +       template<size_t _Nm>
> +         static consteval bool
> +         _S_is_unpadded_submdspan(span<const _SliceKind, _Nm>
> __slice_kinds, size_t __sub_rank)
> +         { return __mdspan::__is_block(__slice_kinds, __sub_rank); }
> +      };
> +
> +    template<typename _Mapping>
> +      constexpr auto
> +      __submdspan_mapping_impl(const _Mapping& __mapping)
> +      { return submdspan_mapping_result{__mapping, 0}; }
> +
> +    template<typename _Mapping, typename... _Slices>
> +      requires (sizeof...(_Slices) > 0)
> +      constexpr auto
> +      __submdspan_mapping_impl(const _Mapping& __mapping, _Slices...
> __slices)
> +      {
> +       using _IndexType = typename _Mapping::index_type;
> +       static_assert((__acceptable_slice_type<_Slices, _IndexType> &&
> ...));
> +
> +       constexpr auto __side = __mdspan::__mapping_side<_Mapping>();
> +       constexpr auto __rank = sizeof...(_Slices);
> +       using _Trait = _SubMdspanMapping<__side>;
> +       using _SliceView = span<const _SliceKind, __rank>;
> +
> +       constexpr auto __slice_kinds =
> __mdspan::__make_slice_kind_array<_Slices...>();
> +       auto __offset = __mdspan::__suboffset(__mapping, __slices...);
> +       auto __sub_exts = __mdspan::__subextents(__mapping.extents(),
> __slices...);
> +       using _SubExts = decltype(__sub_exts);
> +       constexpr auto __sub_rank = _SubExts::rank();
> +       if constexpr (__sub_rank == 0)
> +         return submdspan_mapping_result{
> +           typename _Trait::_Layout::mapping(__sub_exts), __offset};
> +       else if constexpr (_Trait::_S_is_unpadded_submdspan(
> +             _SliceView(__slice_kinds), __sub_rank))
> +         return submdspan_mapping_result{
> +           typename _Trait::_Layout::mapping(__sub_exts), __offset};
> +       else if constexpr (
> +           constexpr auto __u = __padded_block_begin<__side>(
> +               _SliceView(__slice_kinds), __sub_rank);
> +           __u != dynamic_extent)
> +         {
> +           constexpr auto __pad = _Trait::template _S_pad<_Mapping,
> __u>();
> +           using _Layout = typename _Trait::template _PaddedLayout<__pad>;
> +           return submdspan_mapping_result{
> +             typename _Layout::mapping(__sub_exts, __mapping.stride(__u)),
> +             __offset};
> +         }
> +       else
> +         {
> +           auto __sub_strides
> +             = __mdspan::__substrides<_SubExts>(__mapping, __slices...);
> +           return submdspan_mapping_result{
> +             layout_stride::mapping(__sub_exts, __sub_strides),
> __offset};
> +         }
> +      }
>  #endif // __glibcxx_submdspan
>    }
>
> @@ -1032,6 +1494,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
> __glibcxx_assert(__mdspan::__is_representable_extents(_M_extents));
>         }
>
> +#if __glibcxx_submdspan
> +      template<typename... _Slices>
> +       requires (extents_type::rank() == sizeof...(_Slices))
> +       friend constexpr auto
> +       submdspan_mapping(const mapping& __mapping, _Slices... __slices)
> +       { return __mdspan::__submdspan_mapping_impl(__mapping,
> __slices...); }
> +#endif // __glibcxx_submdspan
> +
>         [[no_unique_address]] extents_type _M_extents{};
>      };
>
> @@ -2700,68 +3170,33 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>         __impl(make_index_sequence<__rank>());
>        }
>
> -    template<typename _IndexType, size_t _Extent, typename _Slice>
> -      consteval size_t
> -      __static_slice_extent()
> +    template<typename _Mapping, typename... _Slices>
> +      concept __sliceable_mapping = requires(_Mapping __m, _Slices...
> __slices)
>        {
> -       if constexpr (same_as<_Slice, full_extent_t>)
> -         return _Extent;
> -       else if constexpr (same_as<_Slice,
> constant_wrapper<_IndexType(0)>>)
> -         return 0;
> -       else if constexpr (__is_constant_wrapper<typename
> _Slice::extent_type>
> -                       && __is_constant_wrapper<typename
> _Slice::stride_type>)
> -         return 1 + ((typename _Slice::extent_type{}) - 1)
> -                  / (typename _Slice::stride_type{});
> -       else
> -         return dynamic_extent;
> -      }
> +       { submdspan_mapping(__m, __slices...) } ->
> __submdspan_mapping_result;
> +      };
>
> -    template<size_t _K, typename _Extents, typename _Slice>
> -      constexpr typename _Extents::index_type
> -      __dynamic_slice_extent(const _Extents& __exts, _Slice __slice)
> +    template<typename _Tp>
> +      struct _FullExtent
>        {
> -       if constexpr (__is_strided_slice<_Slice>)
> -         return __slice.extent == 0 ? 0 : 1 + (__slice.extent - 1) /
> __slice.stride;
> -       else
> -         return __exts.extent(_K);
> -      }
> +       using __value = std::full_extent_t;
> +      };
>
> -    template<typename _IndexType, size_t... _Extents, typename... _Slices>
> -      requires (sizeof...(_Slices) == sizeof...(_Extents))
> +    template<typename _Slice>
> +      using __full_extent_t = typename _FullExtent<_Slice>::__value;
>
This alias can just say = std::full_extent_t.
I will change that locallu.

> +
> +    // Enables ADL-only calls from submdspan.
> +    void submdspan_mapping() = delete;
> +
> +    template<typename _MdSpan, typename... _Slices>
>        constexpr auto
> -      __subextents(const extents<_IndexType, _Extents...>& __exts,
> -                  _Slices... __slices)
> +      __submdspan(const _MdSpan& __md, _Slices... __slices)
>
I would preffer to have a __submapping function that will get mapping and
return submdspam_mapping_result.


>        {
> -       constexpr auto __inv_map = __mdspan::__inv_map_rank<_IndexType,
> _Slices...>();
> -       auto __impl = [&]<size_t... _Indices>(index_sequence<_Indices...>)
> -       {
> -         using _SubExtents = extents<_IndexType,
> -             (__mdspan::__static_slice_extent<_IndexType,
> -                _Extents...[__inv_map[_Indices]],
> -                _Slices...[__inv_map[_Indices]]>())...>;
> -         if constexpr (_SubExtents::rank_dynamic() == 0)
> -           return _SubExtents{};
> -         else
> -           {
> -             using _StaticSubExtents = __mdspan::_StaticExtents<
> -               __mdspan::__static_extents<_SubExtents>()>;
> -             auto __create = [&]<size_t... _Is>(index_sequence<_Is...>)
> -             {
> -               constexpr auto __slice_idx = [__inv_map](size_t __i)
> consteval
> -               {
> -                 return
> __inv_map[_StaticSubExtents::_S_dynamic_index_inv(__i)];
> -               };
> -
> -               return _SubExtents{
> -                 (__mdspan::__dynamic_slice_extent<__slice_idx(_Is)>(
> -                    __exts, __slices...[__slice_idx(_Is)]))...};
> -             };
> -             constexpr auto __dyn_subrank = _SubExtents::rank_dynamic();
> -             return __create(make_index_sequence<__dyn_subrank>());
> -           }
> -       };
> -
> -       return __impl(make_index_sequence<__inv_map.size()>());
> +       using _Accessor = typename _MdSpan::accessor_type;
> +       __mdspan::__check_valid_slices(__md.extents(), __slices...);
> +       auto [__mapping, __offset] = submdspan_mapping(__md.mapping(),
> __slices...);
> +       return std::mdspan(__md.accessor().offset(__md.data_handle(),
> __offset),
> +           std::move(__mapping), typename
> _Accessor::offset_policy(__md.accessor()));
>        }
>    }
>
> @@ -2792,6 +3227,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>        };
>        return __impl(__mdspan::__slice_cast<_IndexType>(__raw_slices)...);
>      }
> +
> +  template<typename _ElementType, typename _Extents, typename _Layout,
> +          typename _Accessor, typename... _RawSlices>
> +    requires (sizeof...(_RawSlices) == _Extents::rank()
> +       && __mdspan::__sliceable_mapping<typename
> _Layout::mapping<_Extents>,
> +
> __mdspan::__full_extent_t<_RawSlices>...>)
> +    constexpr auto
> +    submdspan(
> +       const mdspan<_ElementType, _Extents, _Layout, _Accessor>& __md,
> +       _RawSlices... __raw_slices)
> +    {
> +      using _IndexType = typename _Extents::index_type;
> +      return __mdspan::__submdspan(
> +       __md, __mdspan::__slice_cast<_IndexType>(__raw_slices)...);
> +    }
>  #endif // __glibcxx_submdspan
>
>  _GLIBCXX_END_NAMESPACE_VERSION
> diff --git a/libstdc++-v3/src/c++23/std.cc.in b/libstdc++-v3/src/c++23/
> std.cc.in
> index c2a9293b05a..2dac6a6d887 100644
> --- a/libstdc++-v3/src/c++23/std.cc.in
> +++ b/libstdc++-v3/src/c++23/std.cc.in
> @@ -1885,8 +1885,8 @@ export namespace std
>    using std::submdspan_mapping_result;
>    using std::submdspan_canonicalize_slices;
>    using std::submdspan_extents;
> +  using std::submdspan;
>  #endif
> -  // FIXME mdsubspan
>  }
>  #endif
>
> diff --git
> a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/generic.cc
> b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/generic.cc
> new file mode 100644
> index 00000000000..682ff62254c
> --- /dev/null
> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/generic.cc
> @@ -0,0 +1,71 @@
> +// { dg-do compile { target c++26 } }
> +#include <mdspan>
> +
> +namespace adl
> +{
> +  struct NoFriend
> +  {
> +    template<typename Extents>
> +      class mapping
> +      {
> +      public:
> +       using extents_type = Extents;
> +       using index_type = typename extents_type::index_type;
> +      };
> +  };
> +
> +  struct NoFull
> +  {
> +    template<typename Extents>
> +      class mapping
> +      {
> +      public:
> +       using extents_type = Extents;
> +       using index_type = typename extents_type::index_type;
> +
> +      private:
> +       friend constexpr auto
> +       submdspan_mapping(mapping, int)
> +       { return std::submdspan_mapping_result{mapping{}, 0}; }
> +      };
> +  };
> +
> +  struct WrongReturnValue
> +  {
> +    template<typename Extents>
> +      class mapping
> +      {
> +      public:
> +       using extents_type = Extents;
> +       using index_type = typename extents_type::index_type;
> +
> +      private:
> +       friend constexpr int
> +       submdspan_mapping(mapping, std::full_extent_t)
> +       { return 42; }
> +      };
> +  };
> +}
> +
> +template<typename MdSpan, typename... Slices>
> +  concept submdspan_exists = requires (MdSpan md, Slices... slices)
> +  {
> +    std::submdspan(md, slices...);
> +  };
> +
> +template<typename Layout, bool Expected>
> +constexpr bool
> +test_invalid_mapping()
> +{
> +  using Extents = std::extents<int, 3>;
> +  using MdSpan = std::mdspan<double, Extents, Layout>;
> +  static_assert(submdspan_exists<MdSpan, int> == Expected);
> +  static_assert(submdspan_exists<MdSpan, std::full_extent_t> == Expected);
> +  static_assert(!submdspan_exists<MdSpan>);
> +  static_assert(!submdspan_exists<MdSpan, int, int>);
> +  return true;
> +}
> +static_assert(test_invalid_mapping<std::layout_left, true>());
> +static_assert(test_invalid_mapping<adl::NoFriend, false>());
> +static_assert(test_invalid_mapping<adl::NoFull, false>());
> +static_assert(test_invalid_mapping<adl::WrongReturnValue, false>());
> diff --git
> a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/left.cc
> b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/left.cc
> new file mode 100644
> index 00000000000..d6a85d0380b
> --- /dev/null
> +++
> b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/left.cc
> @@ -0,0 +1,9 @@
> +// { dg-do run { target c++26 } }
> +#include "testcases.h"
> +
> +int
> +main()
> +{
> +  test_all<std::layout_left>();
> +  return 0;
> +}
> diff --git
> a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/testcases.h
> b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/testcases.h
> new file mode 100644
> index 00000000000..d7b751d700c
> --- /dev/null
> +++
> b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/selections/testcases.h
> @@ -0,0 +1,360 @@
> +#include <mdspan>
> +
> +#include <vector>
> +#include <numeric>
> +#include "../../layout_traits.h"
> +#include <testsuite_hooks.h>
> +
> +constexpr size_t dyn = std::dynamic_extent;
> +constexpr auto all = std::full_extent;
> +
> +template<typename T>
> +  constexpr bool is_strided_slice = false;
> +
> +template<typename O, typename E, typename S>
> +  constexpr bool is_strided_slice<std::strided_slice<O, E, S>> = true;
> +
> +template<typename MDSpan>
> +  constexpr void
> +  fill(const MDSpan& md)
> +  {
> +    using IndexType = typename MDSpan::index_type;
> +    auto exts = md.extents();
> +    if constexpr (exts.rank() == 3)
> +      for(IndexType i = 0; i < exts.extent(0); ++i)
> +       for(IndexType j = 0; j < exts.extent(1); ++j)
> +         for(IndexType k = 0; k < exts.extent(2); ++k)
> +           md[i, j, k] = 100 * i + 10 * j + k;
> +  }
> +
> +template<typename Int, size_t Rank>
> +  class multi_index_generator
> +  {
> +    struct sentinel
> +    { };
> +
> +    class iterator
> +    {
> +    public:
> +      constexpr
> +      iterator(const std::array<Int, Rank>& shape)
> +       : M_shape(shape)
> +      { }
> +
> +      constexpr iterator&
> +      operator++()
> +      {
> +       if constexpr (Rank > 0)
> +         {
> +           ++M_indices[Rank-1];
> +           for(size_t i = Rank; i > 1; --i)
> +             if (M_indices[i-1] == M_shape[i-1])
> +               {
> +                 M_indices[i-1] = 0;
> +                 ++M_indices[i-2];
> +               }
> +         }
> +       return *this;
> +      }
> +
> +      constexpr auto
> +      operator*()
> +      { return M_indices; }
> +
> +    private:
> +      friend constexpr bool
> +      operator==(const iterator& it, sentinel)
> +      {
> +       if constexpr (Rank > 0)
> +         return it.M_indices[0] == it.M_shape[0];
> +       else
> +         return true;
> +      }
> +
> +      std::array<Int, Rank> M_indices{};
> +      std::array<Int, Rank> M_shape;
> +    };
> +
> +  public:
> +    constexpr
> +    multi_index_generator(std::array<Int, Rank> shape)
> +      : M_shape(shape)
> +    { }
> +
> +    constexpr iterator
> +    begin() const
> +    { return iterator(M_shape); }
> +
> +    constexpr sentinel
> +    end() const
> +    { return sentinel{}; }
> +
> +  private:
> +    std::array<Int, Rank> M_shape;
> +  };
> +
> +constexpr bool
> +test_multi_index()
> +{
> +  auto shape = std::array{3, 5, 7, 1};
> +  auto gen = multi_index_generator(shape);
> +  auto it = gen.begin();
> +  auto end = gen.end();
> +  for (int i = 0; i < shape[0]; ++i)
> +    for (int j = 0; j < shape[1]; ++j)
> +      for (int k = 0; k < shape[2]; ++k)
> +       for (int l = 0; l < shape[3]; ++l)
> +         {
> +           VERIFY(it != end);
> +           VERIFY(*it == std::array{i, j, k, l});
> +           ++it;
> +         }
> +  return true;
> +}
> +static_assert(test_multi_index());
> +
> +struct
> +collapse
> +{ };
> +
> +template<typename... Slices>
> +  consteval auto
> +  inv_collapsed_index_map()
> +  {
> +    constexpr size_t rank = sizeof...(Slices);
> +    auto is_collapsing = std::array{std::same_as<Slices, collapse>...};
> +    constexpr auto collapsed_rank = ((!std::same_as<Slices, collapse>) +
> ... + 0);
> +
> +    std::array<size_t, collapsed_rank> ret;
> +    if constexpr (collapsed_rank > 0)
> +      for(size_t k = 0, i = 0; i < rank; ++i)
> +       if (!is_collapsing[i])
> +         ret[k++] = i;
> +    return ret;
> +  }
> +
> +static_assert(inv_collapsed_index_map<collapse, collapse, collapse>()
> +             == std::array<size_t, 0>{});
> +
> +static_assert(inv_collapsed_index_map<collapse, decltype(all), collapse>()
> +             == std::array<size_t, 1>{1});
> +
> +template<typename IndexType, typename Slice>
> +  constexpr std::vector<IndexType>
> +  make_selection(IndexType extent, const Slice& slice)
> +  {
> +    if constexpr (std::convertible_to<Slice, IndexType>)
> +      return {static_cast<IndexType>(slice)};
> +    else if constexpr (std::same_as<Slice, std::full_extent_t>)
> +      {
> +       auto ret = std::vector<IndexType>(static_cast<size_t>(extent));
> +       std::ranges::iota(ret, 0);
> +       return ret;
> +      }
> +    else if constexpr (is_strided_slice<Slice>)
> +      {
> +       auto ret = std::vector<IndexType>{};
> +       size_t n = static_cast<size_t>(slice.extent);
> +       for(size_t i = 0; i < n; i += slice.stride)
> +         ret.push_back(slice.offset + i);
> +       return ret;
> +      }
> +    else
> +      {
> +       auto [begin, end] = slice;
> +       auto ret = std::vector<IndexType>(static_cast<size_t>(end -
> begin));
> +       std::ranges::iota(ret, begin);
> +       return ret;
> +      }
> +  }
> +
> +template<typename Layout, size_t... I, typename... Slices>
> +  constexpr bool
> +  check_selection(std::index_sequence<I...>, auto md, Slices... slices)
> +  {
> +    auto exts = md.extents();
> +    auto outer_shape = std::array{exts.extent(0), exts.extent(1),
> exts.extent(2)};
> +
> +    constexpr auto full_index = inv_collapsed_index_map<Slices...>();
> +    auto make_slice = [](size_t i, auto slice)
> +    {
> +      if constexpr (std::same_as<decltype(slice), collapse>)
> +       return i;
> +      else
> +       return slice;
> +    };
> +
> +    auto loop_body = [&]<size_t... J>(std::index_sequence<J...>, auto ijk,
> +                                     auto... slices)
> +    {
> +      auto submd = submdspan(md, slices...[I]...);
> +      auto selection = std::tuple{make_selection(exts.extent(I),
> slices...[I])...};
> +      auto inner_shape = std::array<size_t, full_index.size()>{
> +       std::get<full_index[J]>(selection).size()...
> +      };
> +
> +      for (auto ij : multi_index_generator(inner_shape))
> +      {
> +       ((ijk[full_index[J]] = get<full_index[J]>(selection)[ij[J]]),...);
> +       VERIFY(submd[ij] == md[ijk]);
> +      }
> +    };
> +
> +    for (auto ijk : multi_index_generator(outer_shape))
> +      loop_body(std::make_index_sequence<full_index.size()>(), ijk,
> +               make_slice(ijk[I], slices...[I])...);
> +    return true;
> +  }
> +
> +template<typename Layout, typename...MD, typename... Slices>
> +  constexpr bool
> +  check_selection(std::mdspan<MD...> md, Slices... slices)
> +  {
> +    auto indices = std::make_index_sequence<sizeof...(slices)>();
> +    return check_selection<Layout>(indices, md, slices...);
> +  }
> +
> +template<typename Layout, typename IndexType, size_t... Extents,
> +        typename... Slices>
> +  constexpr bool
> +  check_selection(std::extents<IndexType, Extents...>exts, Slices...
> slices)
> +  {
> +    auto run = [&](auto m)
> +    {
> +      auto storage = std::vector<double>(m.required_span_size());
> +      auto md = std::mdspan(storage.data(), m);
> +      fill(md);
> +      return check_selection<Layout>(md, slices...);
> +    };
> +
> +    if constexpr (std::same_as<Layout, std::layout_stride>)
> +      {
> +       auto m = typename Layout::mapping(exts, std::array{15, 2, 50});
> +       return run(m);
> +      }
> +    else
> +      {
> +       auto m = typename Layout::mapping(exts);
> +       return run(m);
> +      }
> +  }
> +
> +template<typename Layout>
> +  constexpr bool
> +  test_scalar_selection(auto exts)
> +  {
> +    check_selection<Layout>(exts, collapse{}, collapse{}, collapse{});
> +    return true;
> +  }
> +
> +template<typename Layout>
> +  constexpr bool
> +  test_full_lines(auto exts)
> +  {
> +    check_selection<Layout>(exts, all, collapse{}, collapse{});
> +    check_selection<Layout>(exts, collapse{}, all, collapse{});
> +    check_selection<Layout>(exts, collapse{}, collapse{}, all);
> +    return true;
> +  }
> +
> +template<typename Layout>
> +  constexpr bool
> +  test_full_blocks(auto exts)
> +  {
> +    check_selection<Layout>(exts, all, all, collapse{});
> +    check_selection<Layout>(exts, all, collapse{}, all);
> +    check_selection<Layout>(exts, collapse{}, all, all);
> +    return true;
> +  }
> +
> +template<typename Layout>
> +  constexpr bool
> +  test_cubes(auto exts)
> +  {
> +    auto s0 = std::pair{0, 2};
> +    auto s1 = std::pair{1, 4};
> +    auto s2 = std::pair{3, 7};
> +
> +    check_selection<Layout>(exts, all, all, all);
> +    check_selection<Layout>(exts, all, all, s2);
> +    check_selection<Layout>(exts, s0, all, all);
> +    check_selection<Layout>(exts, s0, all, s2);
> +    check_selection<Layout>(exts, s0, s1, s2);
> +    return true;
> +  }
> +
> +template<typename Layout>
> +  constexpr bool
> +  test_strided_line_selection(auto exts)
> +  {
> +    auto check = [&](auto s)
> +    {
> +      check_selection<Layout>(exts, collapse{}, s, collapse{});
> +    };
> +
> +    check(std::strided_slice(0, 2, 2));
> +    check(std::strided_slice(0, 3, 2));
> +    check(std::strided_slice(1, 3, 2));
> +    check(std::strided_slice(1, std::cw<3>, std::cw<2>));
> +    return true;
> +  }
> +
> +template<typename Layout>
> +  constexpr bool
> +  test_strided_box_selection(auto exts)
> +  {
> +    auto s0 = std::strided_slice(0, 3, 2);
> +    auto s1 = std::strided_slice(1, 4, 2);
> +    auto s2 = std::strided_slice(0, 7, 3);
> +
> +    check_selection<Layout>(exts, s0, s1, s2);
> +    return true;
> +  }
> +
> +template<typename Layout>
> +  constexpr bool
> +  test_all_cheap()
> +  {
> +    constexpr auto dyn_exts = std::extents(3, 5, 7);
> +    constexpr auto sta_exts = std::extents<int, 3, 5, 7>{};
> +
> +    test_scalar_selection<Layout>(dyn_exts);
> +    test_scalar_selection<Layout>(sta_exts);
> +    static_assert(test_scalar_selection<Layout>(dyn_exts));
> +    static_assert(test_scalar_selection<Layout>(sta_exts));
> +
> +    test_full_lines<Layout>(dyn_exts);
> +    test_full_lines<Layout>(sta_exts);
> +    static_assert(test_full_lines<Layout>(dyn_exts));
> +    static_assert(test_full_lines<Layout>(sta_exts));
> +
> +    test_strided_box_selection<Layout>(dyn_exts);
> +    test_strided_box_selection<Layout>(sta_exts);
> +    static_assert(test_strided_box_selection<Layout>(dyn_exts));
> +    static_assert(test_strided_box_selection<Layout>(sta_exts));
> +    return true;
> +  }
> +
> +template<typename Layout>
> +  constexpr bool
> +  test_all_expensive()
> +  {
> +    auto run = [](auto exts)
> +    {
> +      test_full_blocks<Layout>(exts);
> +      test_cubes<Layout>(exts);
> +    };
> +
> +    run(std::extents(3, 5, 7));
> +    run(std::extents<int, 3, 5, 7>{});
> +    return true;
> +  }
> +
> +template<typename Layout>
> +  constexpr bool
> +  test_all()
> +  {
> +    test_all_cheap<Layout>();
> +    test_all_expensive<Layout>();
> +    return true;
> +  }
> diff --git
> a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc
> b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc
> new file mode 100644
> index 00000000000..a37d3cd588f
> --- /dev/null
> +++
> b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc
> @@ -0,0 +1,136 @@
> +// { dg-do run { target c++26 } }
> +#include <mdspan>
> +
> +#include <iostream> // TODO remove
> +#include "../layout_traits.h"
> +#include <testsuite_hooks.h>
> +
> +constexpr size_t dyn = std::dynamic_extent;
> +
> +template<typename Mapping, typename... Slices>
> +  constexpr auto
> +  call_submdspan_mapping(const Mapping& m, std::tuple<Slices...> slices)
> +  {
> +    auto impl = [&]<size_t... I>(std::index_sequence<I...>)
> +    { return submdspan_mapping(m, get<I>(slices)...); };
> +    return impl(std::make_index_sequence<sizeof...(Slices)>());
> +  }
> +
> +template<typename Layout>
> +  constexpr bool
> +  test_layout_unpadded_return_types()
> +  {
> +    constexpr auto padding_side =
> DeducePaddingSide::from_typename<Layout>();
> +    using Traits = LayoutTraits<padding_side>;
> +
> +    {
> +      auto m0 = typename Layout::mapping(std::extents());
> +      auto result = submdspan_mapping(m0);
> +      using layout_type = typename decltype(result.mapping)::layout_type;
> +      static_assert(std::same_as<layout_type, Layout>);
> +    }
> +
> +    auto exts = Traits::make_extents(std::dims<5, int>(3, 5, 7, 11, 13));
> +    auto m = typename Layout::mapping(exts);
> +    auto all = std::full_extent;
> +    auto s251 = std::strided_slice{2, 5, std::cw<1>};
> +
> +    {
> +      auto slices = std::tuple{0, 0, 0, 0, 0};
> +      auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
> +      using layout_type = typename decltype(result.mapping)::layout_type;
> +      static_assert(std::same_as<layout_type, Layout>);
> +    }
> +
> +    {
> +      auto slices = std::tuple{all, all, all, s251, 0};
> +      auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
> +      using layout_type = typename decltype(result.mapping)::layout_type;
> +      static_assert(std::same_as<layout_type, Layout>);
> +    }
> +
> +    {
> +      auto s0 = std::strided_slice{1, 1, std::cw<1>};
> +      auto slices = std::tuple{s0, all, all, s251, 0};
> +      auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
> +      using layout_type = typename decltype(result.mapping)::layout_type;
> +      static_assert(is_same_padded<padding_side, layout_type>);
> +    }
> +
> +    {
> +      auto s0 = std::strided_slice{1, 2, std::cw<1>};
> +      auto slices = std::tuple{s0, all, all, s251, 0};
> +      auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
> +      using layout_type = typename decltype(result.mapping)::layout_type;
> +      static_assert(is_same_padded<padding_side, layout_type>);
> +    }
> +
> +    {
> +      auto s0 = std::strided_slice{1, 2, std::cw<1>};
> +      auto slices = std::tuple{s0, 0, all, s251, 0};
> +      auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
> +      using layout_type = typename decltype(result.mapping)::layout_type;
> +      static_assert(is_same_padded<padding_side, layout_type>);
> +    }
> +
> +    {
> +      auto s0 = std::strided_slice{1, 2, 1};
> +      auto slices = std::tuple{s0, all, all, s251, 0};
> +      auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
> +      using layout_type = decltype(result.mapping)::layout_type;
> +      static_assert(std::same_as<layout_type, std::layout_stride>);
> +    }
> +
> +    {
> +      auto slices = std::tuple{1, all, all, s251, 0};
> +      auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
> +      using layout_type = decltype(result.mapping)::layout_type;
> +      static_assert(std::same_as<layout_type, std::layout_stride>);
> +    }
> +
> +    {
> +      auto s3 = std::strided_slice{2, std::cw<7>, std::cw<2>};
> +      auto slices = std::tuple{all, all, all, s3, 0};
> +      auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
> +      using layout_type = decltype(result.mapping)::layout_type;
> +      static_assert(std::same_as<layout_type, std::layout_stride>);
> +    }
> +    return true;
> +  }
> +
> +template<typename Layout>
> +  constexpr bool
> +  test_layout_unpadded_padding_value()
> +  {
> +    using Traits =
> LayoutTraits<DeducePaddingSide::from_typename<Layout>()>;
> +    auto s0 = std::strided_slice{size_t(1), size_t(2),
> std::cw<size_t(1)>};
> +    auto s3 = std::strided_slice{size_t(2), size_t(5),
> std::cw<size_t(1)>};
> +    auto all = std::full_extent;
> +
> +    auto check = [&](auto exts, size_t expected)
> +    {
> +      auto m = typename Layout::mapping(Traits::make_extents(exts));
> +      auto slices = std::tuple{s0, size_t(0), all, s3, size_t(0)};
> +      auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
> +      auto padding_value = decltype(result.mapping)::padding_value;
> +      VERIFY(padding_value == expected);
> +    };
> +
> +    check(std::extents(std::cw<3>, std::cw<5>, std::cw<7>, 11, 13), 3*5);
> +    check(std::extents(std::cw<3>, std::cw<5>, 7, 11, 13), 3*5);
> +    check(std::extents(std::cw<3>, 5, 7, 11, 13), dyn);
> +    check(std::extents(3, 5, 7, 11, 13), dyn);
> +    return true;
> +  }
> +
> +int
> +main()
> +{
> +  test_layout_unpadded_return_types<std::layout_left>();
> +  static_assert(test_layout_unpadded_return_types<std::layout_left>());
> +
> +  test_layout_unpadded_padding_value<std::layout_left>();
> +  static_assert(test_layout_unpadded_padding_value<std::layout_left>());
> +  return 0;
> +}
> +
> diff --git
> a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc
> b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc
> new file mode 100644
> index 00000000000..1fc10a832eb
> --- /dev/null
> +++
> b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc
> @@ -0,0 +1,104 @@
> +// { dg-do compile { target c++26 } }
> +#include <mdspan>
> +
> +#include <vector>
> +
> +template<typename Layout, typename... Slices>
> +  constexpr bool
> +  check_slice_range(Slices... slices)
> +  {
> +    auto m = typename Layout::mapping<std::extents<int, 3, 5, 7>>{};
> +    auto storage = std::vector<double>(m.required_span_size());
> +    auto md = std::mdspan(storage.data(), m);
> +
> +    auto submd = submdspan(md, slices...);           // { dg-error
> "expansion of" }
> +    (void) submd;
> +    return true;
> +  }
> +
> +template<typename Layout>
> +  constexpr bool
> +  test_int_under()
> +  {
> +    check_slice_range<Layout>(1, -1, 2);             // { dg-error
> "expansion of" }
> +    return true;
> +  }
> +static_assert(test_int_under<std::layout_left>());   // { dg-error
> "expansion of" }
> +
> +template<typename Layout>
> +  constexpr bool
> +  test_int_over()
> +  {
> +    check_slice_range<Layout>(1, 5, 2);              // { dg-error
> "expansion of" }
> +    return true;
> +  }
> +static_assert(test_int_over<std::layout_left>());    // { dg-error
> "expansion of" }
> +
> +template<typename Layout>
> +  constexpr bool
> +  test_tuple_under()
> +  {
> +    check_slice_range<Layout>(1, std::tuple{-1, 2}, 2);  // { dg-error
> "expansion of" }
> +    return true;
> +  }
> +static_assert(test_tuple_under<std::layout_left>());     // { dg-error
> "expansion of" }
> +
> +template<typename Layout>
> +  constexpr bool
> +  test_tuple_reversed()
> +  {
> +    check_slice_range<Layout>(1, std::tuple{3, 2}, 2);   // { dg-error
> "expansion of" }
> +    return true;
> +  }
> +static_assert(test_tuple_reversed<std::layout_left>());   // { dg-error
> "expansion of" }
> +
> +template<typename Layout>
> +  constexpr bool
> +  test_tuple_over()
> +  {
> +    check_slice_range<Layout>(1, std::tuple{0, 6}, 2); // { dg-error
> "expansion of" }
> +    return true;
> +  }
> +static_assert(test_tuple_over<std::layout_left>());   // { dg-error
> "expansion of" }
> +
> +template<typename Layout>
> +  constexpr bool
> +  test_strided_slice_zero()
> +  {
> +    check_slice_range<Layout>(1, std::strided_slice{1, 1, 0}, 2);  // {
> dg-error "expansion of" }
> +    return true;
> +  }
> +static_assert(test_strided_slice_zero<std::layout_left>());   // {
> dg-error "expansion of" }
> +
> +template<typename Layout>
> +  constexpr bool
> +  test_strided_slice_offset_under()
> +  {
> +    check_slice_range<Layout>(1, std::strided_slice{-1, 1, 1}, 2);   // {
> dg-error "expansion of" }
> +    return true;
> +  }
> +static_assert(test_strided_slice_offset_under<std::layout_left>());   //
> { dg-error "expansion of" }
> +
> +template<typename Layout>
> +  constexpr bool
> +  test_strided_slice_offset_over()
> +  {
> +    check_slice_range<Layout>(1, std::strided_slice{6, 0, 1}, 2);    // {
> dg-error "expansion of" }
> +    return true;
> +  }
> +static_assert(test_strided_slice_offset_over<std::layout_left>());   // {
> dg-error "expansion of" }
> +
> +template<typename Layout>
> +  constexpr bool
> +  test_strided_slice_extent_over()
> +  {
> +    check_slice_range<Layout>(1, std::strided_slice{1, 5, 1}, 2);    // {
> dg-error "expansion of" }
> +    return true;
> +  }
> +static_assert(test_strided_slice_extent_over<std::layout_left>());   // {
> dg-error "expansion of" }
> +
> +// { dg-prune-output "static assertion failed" }
> +// { dg-prune-output "__glibcxx_assert_fail" }
> +// { dg-prune-output "non-constant condition" }
> +// { dg-prune-output "no matching function" }
> +// { dg-prune-output "does not satisfy placeholder constraints" }
> --
> 2.52.0
>
>

Reply via email to