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]>
---
 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;
+               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>
+      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>
+      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;
+         }
+      }
+
+    template<_LayoutSide _Side, size_t _SubRank, size_t _Nm>
+      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;
+           for(size_t __i = 0; __i < _Nm; ++__i)
+             __rev_slice_kinds[__i] = __slice_kinds[_Nm - 1 - __i];
+
+           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...>();
+       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;
 #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>
+       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()>())
+       } -> __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;
+       __mdspan::__check_valid_slices(__md.extents(), __slices...);
+       auto __result = submdspan_mapping(__md.mapping(), __slices...);
+       return std::mdspan(__md.accessor().offset(__md.data_handle(), 
__result.offset),
+           __result.mapping, typename 
_Accessor::offset_policy(__md.accessor()));
+      };
+      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 } }
+#include <mdspan>
+
+#include <iostream> // TODO remove
+#include <vector>
+#include <numeric>
+#include "../layout_traits.h"
+#include <testsuite_hooks.h>
+
+constexpr size_t dyn = std::dynamic_extent;
+constexpr auto all = std::full_extent;
+
+template<typename T>
+  constexpr bool is_strided_slice = false;
+
+template<typename O, typename E, typename S>
+  constexpr bool is_strided_slice<std::strided_slice<O, E, S>> = true;
+
+template<typename MDSpan>
+  constexpr void
+  fill(const MDSpan& md)
+  {
+    using IndexType = typename MDSpan::index_type;
+    auto exts = md.extents();
+    if constexpr (exts.rank() == 3)
+      for(IndexType i = 0; i < exts.extent(0); ++i)
+       for(IndexType j = 0; j < exts.extent(1); ++j)
+         for(IndexType k = 0; k < exts.extent(2); ++k)
+           md[i, j, k] = 100 * i + 10 * j + k;
+  }
+
+template<typename Int, size_t Rank>
+  class multi_index_generator
+  {
+    class EndIt
+    { };
+
+    class BeginIt
+    {
+    public:
+      constexpr
+      BeginIt(const std::array<Int, Rank>& shape)
+       : M_shape(shape)
+      { }
+
+      constexpr BeginIt&
+      operator++()
+      {
+       if constexpr (Rank > 0)
+         {
+           ++M_indices[Rank-1];
+           for(size_t i = Rank; i > 1; --i)
+             if (M_indices[i-1] == M_shape[i-1])
+               {
+                 M_indices[i-1] = 0;
+                 ++M_indices[i-2];
+               }
+         }
+       return *this;
+      }
+
+      constexpr auto
+      operator*()
+      { return M_indices; }
+
+      constexpr bool
+      operator==(EndIt)
+      {
+       if constexpr (Rank > 0)
+         return M_indices[0] == M_shape[0];
+       else
+         return true;
+      }
+
+    private:
+      std::array<Int, Rank> M_indices{};
+      std::array<Int, Rank> M_shape;
+    };
+
+  public:
+    constexpr
+    multi_index_generator(std::array<Int, Rank> shape)
+      : M_shape(shape)
+    { }
+
+    constexpr BeginIt
+    begin() const
+    { return BeginIt(M_shape); }
+
+    constexpr EndIt
+    end() const
+    { return EndIt{}; }
+
+  private:
+    std::array<Int, Rank> M_shape;
+  };
+
+constexpr bool
+test_multi_index()
+{
+  auto shape = std::array{3, 5, 7, 1};
+
+  std::vector<std::array<int, 4>> expected;
+  for (int i = 0; i < shape[0]; ++i)
+    for (int j = 0; j < shape[1]; ++j)
+      for (int k = 0; k <shape[2]; ++k)
+       for (int l = 0; l <shape[3]; ++l)
+         expected.push_back(std::array{i, j, k, l});
+
+  size_t i = 0;
+  for (auto actual : multi_index_generator(shape))
+    VERIFY(expected[i++] == actual);
+  return true;
+}
+
+static_assert(test_multi_index());
+
+struct
+collapse
+{ };
+
+template<typename... Slices>
+  consteval auto
+  inv_collapsed_index_map()
+  {
+    constexpr size_t rank = sizeof...(Slices);
+    auto is_collapsing = std::array{std::same_as<Slices, collapse>...};
+    constexpr auto collapsed_rank = ((!std::same_as<Slices, collapse>) + ... + 
0);
+
+    std::array<size_t, collapsed_rank> ret;
+    if constexpr (collapsed_rank > 0)
+      for(size_t k = 0, i = 0; i < rank; ++i)
+       if (!is_collapsing[i])
+         ret[k++] = i;
+    return ret;
+  }
+
+static_assert(inv_collapsed_index_map<collapse, collapse, collapse>()
+             == std::array<size_t, 0>{});
+
+static_assert(inv_collapsed_index_map<collapse, decltype(all), collapse>()
+             == std::array<size_t, 1>{1});
+
+template<typename IndexType, typename Slice>
+  constexpr std::vector<IndexType>
+  make_selection(IndexType extent, const Slice& slice)
+  {
+    if constexpr (std::convertible_to<Slice, IndexType>)
+      return {static_cast<IndexType>(slice)};
+    else if constexpr (std::same_as<Slice, std::full_extent_t>)
+      {
+       auto ret = std::vector<IndexType>(static_cast<size_t>(extent));
+       std::ranges::iota(ret, 0);
+       return ret;
+      }
+    else if constexpr (is_strided_slice<Slice>)
+      {
+       auto ret = std::vector<IndexType>{};
+       size_t n = static_cast<size_t>(slice.extent);
+       for(size_t i = 0; i < n; i += slice.stride)
+         ret.push_back(slice.offset + i);
+       return ret;
+      }
+    else
+      {
+       auto [begin, end] = slice;
+       auto ret = std::vector<IndexType>(static_cast<size_t>(end - begin));
+       std::ranges::iota(ret, begin);
+       return ret;
+      }
+  }
+
+template<typename Layout, size_t... I, typename... Slices>
+  constexpr bool
+  check_selection(std::index_sequence<I...>, auto md, Slices... slices)
+  {
+    auto exts = md.extents();
+    auto outer_shape = std::array{exts.extent(0), exts.extent(1), 
exts.extent(2)};
+
+    constexpr auto full_index = inv_collapsed_index_map<Slices...>();
+    auto make_slice = [](size_t i, auto slice)
+    {
+      if constexpr (std::same_as<decltype(slice), collapse>)
+       return i;
+      else
+       return slice;
+    };
+
+    auto loop_body = [&]<size_t... J>(std::index_sequence<J...>, auto ijk,
+                                     auto... slices)
+    {
+      auto submd = submdspan(md, slices...[I]...);
+      auto selection = std::tuple{make_selection(exts.extent(I), 
slices...[I])...};
+      auto inner_shape = std::array<size_t, full_index.size()>{
+       std::get<full_index[J]>(selection).size()...
+      };
+
+      for (auto ij : multi_index_generator(inner_shape))
+      {
+       ((ijk[full_index[J]] = get<full_index[J]>(selection)[ij[J]]),...);
+       VERIFY(submd[ij] == md[ijk]);
+      }
+    };
+
+    for (auto ijk : multi_index_generator(outer_shape))
+      loop_body(std::make_index_sequence<full_index.size()>(), ijk,
+               make_slice(ijk[I], slices...[I])...);
+    return true;
+  }
+
+template<typename Layout, typename...MD, typename... Slices>
+  constexpr bool
+  check_selection(std::mdspan<MD...> md, Slices... slices)
+  {
+    auto indices = std::make_index_sequence<sizeof...(slices)>();
+    return check_selection<Layout>(indices, md, slices...);
+  }
+
+template<typename Layout, typename IndexType, size_t... Extents,
+        typename... Slices>
+  constexpr bool
+  check_selection(std::extents<IndexType, Extents...>exts, Slices... slices)
+  {
+    auto run = [&](auto m)
+    {
+      auto storage = std::vector<double>(m.required_span_size());
+      auto md = std::mdspan(storage.data(), m);
+      fill(md);
+      return check_selection<Layout>(md, slices...);
+    };
+
+    if constexpr (std::same_as<Layout, std::layout_stride>)
+      {
+       auto m = typename Layout::mapping(exts, std::array{15, 2, 50});
+       return run(m);
+      }
+    else
+      {
+       auto m = typename Layout::mapping(exts);
+       return run(m);
+      }
+  }
+
+template<typename Layout>
+  constexpr bool
+  test_scalar_selection(auto exts)
+  {
+    check_selection<Layout>(exts, collapse{}, collapse{}, collapse{});
+    return true;
+  }
+
+template<typename Layout>
+  constexpr bool
+  test_full_lines(auto exts)
+  {
+    check_selection<Layout>(exts, all, collapse{}, collapse{});
+    check_selection<Layout>(exts, collapse{}, all, collapse{});
+    check_selection<Layout>(exts, collapse{}, collapse{}, all);
+    return true;
+  }
+
+template<typename Layout>
+  constexpr bool
+  test_full_blocks(auto exts)
+  {
+    check_selection<Layout>(exts, all, all, collapse{});
+    check_selection<Layout>(exts, all, collapse{}, all);
+    check_selection<Layout>(exts, collapse{}, all, all);
+    return true;
+  }
+
+template<typename Layout>
+  constexpr bool
+  test_cubes(auto exts)
+  {
+    auto s0 = std::pair{0, 2};
+    auto s1 = std::pair{1, 4};
+    auto s2 = std::pair{3, 7};
+
+    check_selection<Layout>(exts, all, all, all);
+    check_selection<Layout>(exts, all, all, s2);
+    check_selection<Layout>(exts, s0, all, all);
+    check_selection<Layout>(exts, s0, all, s2);
+    check_selection<Layout>(exts, s0, s1, s2);
+    return true;
+  }
+
+template<typename Layout>
+  constexpr bool
+  test_strided_line_selection(auto exts)
+  {
+    auto check = [&](auto s)
+    {
+      check_selection<Layout>(exts, collapse{}, s, collapse{});
+    };
+
+    check(std::strided_slice(0, 2, 2));
+    check(std::strided_slice(0, 3, 2));
+    check(std::strided_slice(1, 3, 2));
+    check(std::strided_slice(1, std::cw<3>, std::cw<2>));
+    return true;
+  }
+
+template<typename Layout>
+  constexpr bool
+  test_strided_box_selection(auto exts)
+  {
+    auto s0 = std::strided_slice(0, 3, 2);
+    auto s1 = std::strided_slice(1, 4, 2);
+    auto s2 = std::strided_slice(0, 7, 3);
+
+    check_selection<Layout>(exts, s0, s1, s2);
+    return true;
+  }
+
+template<typename Layout>
+  constexpr bool
+  test_all_cheap()
+  {
+    constexpr auto dyn_exts = std::extents(3, 5, 7);
+    constexpr auto sta_exts = std::extents<int, 3, 5, 7>{};
+
+    test_scalar_selection<Layout>(dyn_exts);
+    test_scalar_selection<Layout>(sta_exts);
+    static_assert(test_scalar_selection<Layout>(dyn_exts));
+    static_assert(test_scalar_selection<Layout>(sta_exts));
+
+    test_full_lines<Layout>(dyn_exts);
+    test_full_lines<Layout>(sta_exts);
+    static_assert(test_full_lines<Layout>(dyn_exts));
+    static_assert(test_full_lines<Layout>(sta_exts));
+
+    test_strided_box_selection<Layout>(dyn_exts);
+    test_strided_box_selection<Layout>(sta_exts);
+    static_assert(test_strided_box_selection<Layout>(dyn_exts));
+    static_assert(test_strided_box_selection<Layout>(sta_exts));
+    return true;
+  }
+
+template<typename Layout>
+  constexpr bool
+  test_all_expensive()
+  {
+    auto run = [](auto exts)
+    {
+      test_full_blocks<Layout>(exts);
+      test_cubes<Layout>(exts);
+    };
+
+    run(std::extents(3, 5, 7));
+    run(std::extents<int, 3, 5, 7>{});
+    return true;
+  }
+
+template<typename Layout>
+  constexpr bool
+  test_all()
+  {
+    test_all_cheap<Layout>();
+    test_all_expensive<Layout>();
+    return true;
+  }
+
+int
+main()
+{
+  test_all<std::layout_left>();
+  return 0;
+}
diff --git 
a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc 
b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc
new file mode 100644
index 00000000000..a37d3cd588f
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc
@@ -0,0 +1,136 @@
+// { dg-do run { target c++26 } }
+#include <mdspan>
+
+#include <iostream> // TODO remove
+#include "../layout_traits.h"
+#include <testsuite_hooks.h>
+
+constexpr size_t dyn = std::dynamic_extent;
+
+template<typename Mapping, typename... Slices>
+  constexpr auto
+  call_submdspan_mapping(const Mapping& m, std::tuple<Slices...> slices)
+  {
+    auto impl = [&]<size_t... I>(std::index_sequence<I...>)
+    { return submdspan_mapping(m, get<I>(slices)...); };
+    return impl(std::make_index_sequence<sizeof...(Slices)>());
+  }
+
+template<typename Layout>
+  constexpr bool
+  test_layout_unpadded_return_types()
+  {
+    constexpr auto padding_side = DeducePaddingSide::from_typename<Layout>();
+    using Traits = LayoutTraits<padding_side>;
+
+    {
+      auto m0 = typename Layout::mapping(std::extents());
+      auto result = submdspan_mapping(m0);
+      using layout_type = typename decltype(result.mapping)::layout_type;
+      static_assert(std::same_as<layout_type, Layout>);
+    }
+
+    auto exts = Traits::make_extents(std::dims<5, int>(3, 5, 7, 11, 13));
+    auto m = typename Layout::mapping(exts);
+    auto all = std::full_extent;
+    auto s251 = std::strided_slice{2, 5, std::cw<1>};
+
+    {
+      auto slices = std::tuple{0, 0, 0, 0, 0};
+      auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
+      using layout_type = typename decltype(result.mapping)::layout_type;
+      static_assert(std::same_as<layout_type, Layout>);
+    }
+
+    {
+      auto slices = std::tuple{all, all, all, s251, 0};
+      auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
+      using layout_type = typename decltype(result.mapping)::layout_type;
+      static_assert(std::same_as<layout_type, Layout>);
+    }
+
+    {
+      auto s0 = std::strided_slice{1, 1, std::cw<1>};
+      auto slices = std::tuple{s0, all, all, s251, 0};
+      auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
+      using layout_type = typename decltype(result.mapping)::layout_type;
+      static_assert(is_same_padded<padding_side, layout_type>);
+    }
+
+    {
+      auto s0 = std::strided_slice{1, 2, std::cw<1>};
+      auto slices = std::tuple{s0, all, all, s251, 0};
+      auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
+      using layout_type = typename decltype(result.mapping)::layout_type;
+      static_assert(is_same_padded<padding_side, layout_type>);
+    }
+
+    {
+      auto s0 = std::strided_slice{1, 2, std::cw<1>};
+      auto slices = std::tuple{s0, 0, all, s251, 0};
+      auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
+      using layout_type = typename decltype(result.mapping)::layout_type;
+      static_assert(is_same_padded<padding_side, layout_type>);
+    }
+
+    {
+      auto s0 = std::strided_slice{1, 2, 1};
+      auto slices = std::tuple{s0, all, all, s251, 0};
+      auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
+      using layout_type = decltype(result.mapping)::layout_type;
+      static_assert(std::same_as<layout_type, std::layout_stride>);
+    }
+
+    {
+      auto slices = std::tuple{1, all, all, s251, 0};
+      auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
+      using layout_type = decltype(result.mapping)::layout_type;
+      static_assert(std::same_as<layout_type, std::layout_stride>);
+    }
+
+    {
+      auto s3 = std::strided_slice{2, std::cw<7>, std::cw<2>};
+      auto slices = std::tuple{all, all, all, s3, 0};
+      auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
+      using layout_type = decltype(result.mapping)::layout_type;
+      static_assert(std::same_as<layout_type, std::layout_stride>);
+    }
+    return true;
+  }
+
+template<typename Layout>
+  constexpr bool
+  test_layout_unpadded_padding_value()
+  {
+    using Traits = LayoutTraits<DeducePaddingSide::from_typename<Layout>()>;
+    auto s0 = std::strided_slice{size_t(1), size_t(2), std::cw<size_t(1)>};
+    auto s3 = std::strided_slice{size_t(2), size_t(5), std::cw<size_t(1)>};
+    auto all = std::full_extent;
+
+    auto check = [&](auto exts, size_t expected)
+    {
+      auto m = typename Layout::mapping(Traits::make_extents(exts));
+      auto slices = std::tuple{s0, size_t(0), all, s3, size_t(0)};
+      auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
+      auto padding_value = decltype(result.mapping)::padding_value;
+      VERIFY(padding_value == expected);
+    };
+
+    check(std::extents(std::cw<3>, std::cw<5>, std::cw<7>, 11, 13), 3*5);
+    check(std::extents(std::cw<3>, std::cw<5>, 7, 11, 13), 3*5);
+    check(std::extents(std::cw<3>, 5, 7, 11, 13), dyn);
+    check(std::extents(3, 5, 7, 11, 13), dyn);
+    return true;
+  }
+
+int
+main()
+{
+  test_layout_unpadded_return_types<std::layout_left>();
+  static_assert(test_layout_unpadded_return_types<std::layout_left>());
+
+  test_layout_unpadded_padding_value<std::layout_left>();
+  static_assert(test_layout_unpadded_padding_value<std::layout_left>());
+  return 0;
+}
+
diff --git 
a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc 
b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc
new file mode 100644
index 00000000000..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
+  {
+    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