On 12/5/25 11:04, Tomasz Kaminski wrote:
On Thu, Dec 4, 2025 at 10:34 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/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 <[email protected]>

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

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).

+       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




Reply via email to