[PATCH v2 04/10] libstdc++: Implement submdspan and submdspan_mapping for layout_left. [PR110352]

Luc Grosheintz luc.grosheintz@gmail.com
Mon Dec 1 14:27:41 GMT 2025



On 11/20/25 17:08, Tomasz Kaminski wrote:
> On Tue, Nov 18, 2025 at 3:37 PM Luc Grosheintz <luc.grosheintz@gmail.com>
> wrote:
> 
>> Implements `submdspan` and `submdspan_mapping` for layout_left as
>> described in P3663 (Future proofing mdspan).
>>
>> When computing the offset of the submdspan, one must check that the
>> lower bound of the slice range isn't out-of-range. There's a few
>> cases when the lower bound is never out-of-range:
>>
>>    - full_extent and exts.extent(k) != 0,
>>    - collapsing slice types.
>>
>> If those conditions are known to hold, no checks are generated.
>>
> 
>> Similarly, if all slices are full_extent, there's no need to call
>> mapping(0,...,0) for standardized mappings.
>>
>> The implementation prepares to use the symmetry between layout_left and
>> layout_right and introduces concepts like a "layout side", i.e. left,
>> right or unknown/strided.
>>
>> The tests use an iterator to replace nested for-loops. Which also makes
>> it easier to write the core test logic in a rank-independent manner.
>>
>>          PR libstdc++/110352
>>
>> libstdc++-v3/ChangeLog:
>>
>>          * include/std/mdspan (layout_left::mapping::submdspan_mapping):
>>          New friend function.
>>          (submdspan): New function.
>>          * src/c++23/std.cc.in: Add submdspan.
>>          * testsuite/23_containers/mdspan/submdspan/submdspan.cc: New test.
>>          * testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc:
>> New test.
>>          * testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc: New
>> test.
>>
>> Signed-off-by: Luc Grosheintz <luc.grosheintz@gmail.com>
>>
> Posted some idea on how to classify the slices without so much
> metaprogramming,
> the idea would be to have function, that would have constexpr array of
> slice kind,
> would go over it either left to right or right to left, and return if we
> have matching
> blocks. Could accept rank as a parameter. We most likely want this function
> to simply
> accept span, subrank, and direction, and not be templated.
> 
> Could you let me know what you think about the idea?
> I think I will stop review here, as this will affect the rest of the code.

I have a version drafted up: it works reasonably well, only I'd rather
reverse the array of SliceKind than pass in a direction. It feels easier
to express: same as left; but from the back. Makes it explicit that there
is no difference between left and right. Since, it's all consteval, there's
no cost at runtime; and at compile time we save instantiating once for
each direction.

---------------

Should all standardized mapping have `_M_strides`? I don't object, but
the usage is slightly different: _M_strides creates an array of length
`rank`; while the loop in question only needs an array of length
`subrank`, it only needs the strides at non-collapsing slices.

---------------

About qualified calls (to avoid ADL). Is this also required for uglified
names, e.g. __subextents? Since users aren't permitted to use those names
they can't cause ADL-related issues.

If we know that the types of all arguments are "ours", i.e. defined in
std:: or builtins like `int`, do we still need to protect against ADL?
After canonicalization, we're left with: standarsized mapping, extents,
full_extent_t, strided_slice, constant_wrapper and integers.

Sofar I've been implementing everything as if the answer were: "no" to
both questions. If that's wrong, let me know, and I'll do a pass to
clean up all of <mdspan>.

> 
> 
>> ---
>>   libstdc++-v3/include/std/mdspan               | 387 ++++++++++++++++++
>>   libstdc++-v3/src/c++23/std.cc.in              |   2 +-
>>   .../mdspan/submdspan/submdspan.cc             | 369 +++++++++++++++++
>>   .../mdspan/submdspan/submdspan_mapping.cc     | 136 ++++++
>>   .../mdspan/submdspan/submdspan_neg.cc         | 102 +++++
>>   5 files changed, 995 insertions(+), 1 deletion(-)
>>   create mode 100644
>> libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc
>>   create mode 100644
>> libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc
>>   create mode 100644
>> libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc
>>
>> diff --git a/libstdc++-v3/include/std/mdspan
>> b/libstdc++-v3/include/std/mdspan
>> index 36e04f7e1b5..712826ea7e7 100644
>> --- a/libstdc++-v3/include/std/mdspan
>> +++ b/libstdc++-v3/include/std/mdspan
>> @@ -578,20 +578,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>            return __r == 0 ? 1 : __exts.extent(0);
>>          else if constexpr
>> (__all_dynamic(std::span(__sta_exts).first(__rank-1)))
>>            return __extents_prod(__exts, 1, 0, __r);
>>          else
>>            {
>>              size_t __sta_prod = __fwd_partial_prods<__sta_exts>[__r];
>>              return __extents_prod(__exts, __sta_prod, 0, __r);
>>            }
>>         }
>>
>> +    template<typename _IndexType, size_t _Nm>
>> +      consteval _IndexType
>> +      __fwd_prod(span<const _IndexType, _Nm> __values)
>> +      {
>> +       _IndexType __ret = 1;
>> +       for(auto __value : __values)
>> +         __ret *= __value;
>> +       return __ret;
>> +      }
>> +
>>       // Preconditions: _r < _Extents::rank()
>>       template<typename _Extents>
>>         constexpr typename _Extents::index_type
>>         __rev_prod(const _Extents& __exts, size_t __r) noexcept
>>         {
>>          constexpr size_t __rank = _Extents::rank();
>>          constexpr auto& __sta_exts = __static_extents<_Extents>();
>>          if constexpr (__rank == 1)
>>            return 1;
>>          else if constexpr (__rank == 2)
>> @@ -1027,20 +1037,374 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>          constexpr auto __sub_rank = __subrank<_IndexType, _Slices...>();
>>          auto __map = std::array<size_t, __sub_rank>{};
>>          auto __is_int_like = std::array{convertible_to<_Slices,
>> _IndexType>...};
>>
>>          size_t __i = 0;
>>          for (size_t __k = 0; __k < __rank; ++__k)
>>            if (!__is_int_like[__k])
>>              __map[__i++] = __k;
>>          return __map;
>>         }
>> +
>> +    template<typename _IndexType, typename _Slice>
>> +      constexpr _IndexType
>> +      __slice_begin(_Slice __slice)
>> +      {
>>
>   Please consider implementing this as:
>     if constexpr (is_same_v<_Slice, _full_extent_t>)
>        return 0;
>     else if constexpr (__is_strided_slice<_Slice>)
>        return __slice.offset;
>     else
>        return __slice;
> This should be lighter to check that using concept,
> and you do not need to pass _IndexType.
> 
> 
> +       if constexpr (convertible_to<_Slice, _IndexType>)
>> +         return __slice;
>> +       else if constexpr (__is_strided_slice<_Slice>)
>> +         return __slice.offset;
>> +       else
>> +         return 0; // full_extent
>> +      }
>> +
>> +    template<typename _Mapping, typename... _Slices>
>> +      constexpr size_t
>> +      __suboffset(const _Mapping& __mapping, const _Slices&... __slices)
>> +      {
>> +       using _IndexType = typename _Mapping::index_type;
>> +       auto __any_past_the_end = [&]<size_t...
>> _Is>(index_sequence<_Is...>)
>> +       {
> 
> +         auto __is_past_the_end = [](const auto& __slice, const auto&
>> __ext)
>> +         {
>> +           using _Slice = remove_cvref_t<decltype(__slice)>;
>>
>      if constexpr (__ext.static_extent(
> 
> 
> I will suggest expressing this as:
>         if constexpr (is_same_v<_Slice, __full_extent_t>)
>           return true; // we checked that before
>         else if constexpr (!_is_strided_slice<_Slice>)
>           return false;
>         else
>            // Compiler wii probably optimize if this two are constant.
>           return __slice.offset = __ext.extent(0);
> 
> 
>> +           if constexpr (is_convertible_v<_Slice, _IndexType>)
>>
> I do not think this is correct, we single indices to operators.
> 
>> +             return false;
>> +           else if (same_as<_Slice, full_extent_t>
>> +               && __ext.static_extent(0) > 0
>> +               && __ext.static_extent(0) != dynamic_extent)
>>
> 
> +             return false;
>> +           else
>> +             return __slice_begin<_IndexType>(__slice) == __ext.extent(0);
>>
> We seem to this for __full_extent, before the previous if is not constexpr.
> 
>> +         };
>> +
>> +         const auto& __exts = __mapping.extents();
>>
> +         return ((__is_past_the_end(__slices...[_Is],
>> +                                    __extract_extent<_Is>(__exts))) ||
>> ...);
>> +       };
>> +
>> +       if constexpr ((same_as<_Slices, full_extent_t> && ...))
>> +         return __offset(__mapping);
>> +
>>
> I think I would check empty mdspan separately here, so if
> empty(__mapping.extents())
> the return __mapping.required_span_size(), this will also simply
> __any_past_the_end.
> 
> 
>> +       if constexpr (!((convertible_to<_Slices, _IndexType>) && ...))
> 
> +         if
>> (__any_past_the_end(make_index_sequence<sizeof...(__slices)>()))
>> +           return __mapping.required_span_size();
>> +       return __mapping(__slice_begin<_IndexType>(__slices)...);
>>
> Because we do not pass _IndexType to slice begin, use
> static_cast<_IndexType> here.
> 
>> +      }
>> +
>> +    enum class _LayoutSide
>> +    {
>> +      __left,
>> +      __right,
>> +      __unknown
>> +    };
>> +
>> +    template<typename _Mapping>
>> +      consteval _LayoutSide
>> +      __deduce_mapping_side()
>> +      {
>> +       if constexpr (__is_left_padded_mapping<_Mapping>
>> +           || __mapping_of<layout_left, _Mapping>)
>> +         return _LayoutSide::__left;
>> +       if constexpr (__is_right_padded_mapping<_Mapping>
>> +           || __mapping_of<layout_right, _Mapping>)
>> +         return _LayoutSide::__right;
>> +       else
>> +         return _LayoutSide::__unknown;
>> +      }
>> +
>> +    template<_LayoutSide _Side, size_t _Rank>
>> +      struct _StridesTrait
>> +      {
>> +       static constexpr const _LayoutSide _S_side = _Side;
>> +
>> +       static constexpr size_t
>> +       _S_idx(size_t __k) noexcept
>> +       {
>> +         if constexpr (_Side == _LayoutSide::__left)
>> +           return __k;
>> +         else
>> +           return _Rank - 1 - __k;
>> +       }
>> +
>> +       template<typename _Mapping>
>> +         static constexpr typename _Mapping::index_type
>> +         _S_extent(const _Mapping& __mapping, size_t __k)
>> +         {
>> +           if (__k == 0)
>> +             return __mapping.stride(_S_idx(1));
>>
> Nice trick of handling padded and not padded, but comment on it.
> 
>> +           else
>> +             return __mapping.extents().extent(_S_idx(__k));
>> +         }
>> +
>> +       template<typename _IndexType, typename... _Slices>
>> +         static consteval auto
>> +         _S_inv_map()
>> +         {
>> +           static_assert(_Side != _LayoutSide::__unknown);
>> +           auto __impl = [&]<size_t... _Is>(index_sequence<_Is...>)
>> +           {
>> +             return __inv_map_rank<_IndexType,
>> _Slices...[_S_idx(_Is)]...>();
>> +           };
>> +           return __impl(make_index_sequence<_Rank>());
>> +         }
>> +      };
>> +
>> +    template<typename _SubExtents, typename _Mapping, typename... _Slices>
>> +      constexpr auto
>> +      __substrides_generic(const _Mapping& __mapping, const _Slices&...
>> __slices)
>> +      {
>> +       using _IndexType = typename _Mapping::index_type;
>> +       if constexpr (_SubExtents::rank() == 0)
>> +         return array<_IndexType, _SubExtents::rank()>{};
>> +       else
>> +         {
>> +           auto __stride = [&__mapping](size_t __k, auto __slice) ->
>> _IndexType
>> +           {
>> +             if constexpr (__is_strided_slice<decltype(__slice)>)
>> +               if (__slice.stride < __slice.extent)
>> +                 return __mapping.stride(__k) * __slice.stride;
>> +             return __mapping.stride(__k);
>> +           };
>> +
>> +           auto __impl = [&]<size_t... _Is>(index_sequence<_Is...>)
>> +           {
>> +             constexpr auto __inv_map = __inv_map_rank<_IndexType,
>> _Slices...>();
>> +             return array<_IndexType, _SubExtents::rank()>{
>> +               __stride(__inv_map[_Is], __slices...[__inv_map[_Is]])...};
>> +           };
>> +           return __impl(make_index_sequence<_SubExtents::rank()>());
>> +         }
>> +      };
>> +
>> +    template<typename _SubExtents, typename _Mapping, typename... _Slices>
>> +      constexpr auto
>> +      __substrides_standardized(const _Mapping& __mapping,
>> +                              const _Slices&... __slices)
>> +      {
>> +       using _IndexType = typename _Mapping::index_type;
>> +       using _Trait = _StridesTrait<__deduce_mapping_side<_Mapping>(),
>> +                                    _Mapping::extents_type::rank()>;
>> +       using _SubTrait = _StridesTrait<__deduce_mapping_side<_Mapping>(),
>> +                                       _SubExtents::rank()>;
>> +
>> +       constexpr size_t __sub_rank = _SubExtents::rank();
>> +
>> +       array<_IndexType, __sub_rank> __ret;
>> +       if constexpr (__sub_rank > 0)
>> +         {
>> +           constexpr auto __inv_map
>> +             = _Trait::template _S_inv_map<_IndexType, _Slices...>();
>> +           auto __loop = [&]<size_t... _Ks>(index_sequence<_Ks...>)
>> +           {
>> +             size_t __i0 = 0;
>> +             size_t __stride = 1;
>> +             auto __body = [&](size_t __k, auto __slice)
>> +             {
>> +               for (size_t __i = __i0; __i < __inv_map[__k]; ++__i)
>> +                 __stride *= _Trait::_S_extent(__mapping, __i);
> 
> I wonder if for all standardized mappings, we should not add _M_strides
> (public) function to access that. This would be useful.
> 
>> +
>> +               size_t __krev = _SubTrait::_S_idx(__k);
>> +               if constexpr (__is_strided_slice<decltype(__slice)>)
>> +                 __ret[__krev] = __stride * __slice.stride;
>> +               else
>> +                 __ret[__krev] = __stride;
>> +
>> +               __i0 = __inv_map[__k];
>> +             };
>> +
>> +             ((__body(_Ks,
>> __slices...[_Trait::_S_idx(__inv_map[_Ks])])),...);
>> +           };
>> +           __loop(make_index_sequence<__sub_rank>());
>> +         }
>> +       return __ret;
>> +      }
>> +
>> +
>> +    template<typename _SubExtents, typename _Mapping, typename... _Slices>
>> +      constexpr auto
>> +      __substrides(const _Mapping& __mapping, const _Slices&... __slices)
>> +      {
>> +       if constexpr (__deduce_mapping_side<_Mapping>() ==
>> _LayoutSide::__unknown)
>> +         return __substrides_generic<_SubExtents>(__mapping, __slices...);
>> +       else
>> +         return __substrides_standardized<_SubExtents>(__mapping,
>> __slices...);
>> +      }
>> +
>> +    template<typename _Slice, typename _IndexType>
>> +      concept __is_unit_stride_slice =
> 
> 
> (__is_strided_slice<_Slice>
>> +         && __detail::__integral_constant_like<typename
>> _Slice::stride_type>
>>
> Usig is_constant_wrapper instead of  __integral_constant_like.
> 
>> +         && _Slice::stride_type::value == 1)
>> +       || same_as<_Slice, full_extent_t>;
>> +
>> +    //                   _BlockSize - 1
>> +    // [full, ..., full, unit_slice    , ...]
>> +    template<typename _IndexType, size_t _BlockSize, typename... _Slices>
>>
> I think this funciton would read much better if we do normal template
> parameter recursion,
> i.e. have typename _Slice, typename... _RemSlices, we should never search a
> block
> with one element.
> 
>> +      consteval bool
>> +      __is_block()
>> +      {
>> +       if constexpr (_BlockSize == 0 || _BlockSize > sizeof...(_Slices))
>> +         return false;
>> +       else if constexpr (_BlockSize == 1)
>> +         return __is_unit_stride_slice<_Slices...[0], _IndexType>;
>> +       else if constexpr (same_as<_Slices...[0], full_extent_t>)
>>
> This will be much simpler.
> 
>> +         {
>> +           auto __recurse = []<size_t... _Is>(index_sequence<_Is...>)
>> +           {
>> +             return __is_block<_IndexType, _BlockSize - 1,
>> +                               _Slices...[_Is + 1]...>();
>> +           };
>> +           return __recurse(make_index_sequence<sizeof...(_Slices) -
>> 1>());
>> +         }
>> +       else
>> +         return false;
>>
> +      }
>> +
>> +    //     __u              __u + _BlockSize - 1
>> +    // [*, full, ..., full,           unit_slice, *]
>> +    template<typename _IndexType, size_t _Start, size_t _BlockSize,
>> +            typename... _Slices>
>> +      consteval size_t
>> +      __find_block()
>>
> Hmm, I think that this function could be implemented much easier by having:
>        bool full_map[sizeof...(Slices)]{ is_same_v<full_exent_t, Slices>....
> };
> Or even better having an array of classification of slices at the
> begining of the functo
>       constexpr SliceKind kinds[sizeof...(Slices)]{  function to classify,
> full_extent -> full, strided -> stride, unit };
> And then pass it to classification functions. That will go over what could
> be returned.
> 
> 
>> +      {
>> +       static_assert(_BlockSize != dynamic_extent,
>> +         "The implementation can't handle submdspans with rank ==
>> size_t(-1)");
>> +
>> +       if constexpr (sizeof...(_Slices) == 0)
>> +         return dynamic_extent;
>> +       else if constexpr (__is_block<_IndexType, _BlockSize,
>> _Slices...>())
>> +         return _Start;
>> +       else
>> +         {
>> +           auto __recurse = []<size_t... _Is>(index_sequence<_Is...>)
>> +           {
>> +             return __find_block<_IndexType, _Start + 1, _BlockSize,
>> +                                 _Slices...[_Is + 1]...>();
>> +           };
>> +           return __recurse(make_index_sequence<sizeof...(_Slices) -
>> 1>());
>> +         }
>> +      }
>> +
>> +    template<typename _IndexType, size_t _SubRank, typename... _Slices>
>> +      static consteval bool
>> +      __is_compact_block()
>> +      {
>> +       if constexpr (_SubRank == 0)
>> +         return false;
>> +       else
>> +         return  __find_block<_IndexType, 0, _SubRank, _Slices...>() == 0;
>> +      }
>> +
>> +    //                         __u
>> +    // [unit_slice, i, ..., k, full, ..., full, unit_slice, *]
>> +    template<typename _IndexType, size_t _SubRank, typename _Slice,
>> +            typename... _Slices>
>> +      static consteval size_t
>> +      __padded_block_begin_generic()
>> +      {
>> +       if constexpr (!__mdspan::__is_unit_stride_slice<_Slice,
>> _IndexType>)
>> +         return dynamic_extent;
>> +       else if constexpr (sizeof...(_Slices) == 0)
>> +         return dynamic_extent;
>> +       else
>> +         {
>> +           constexpr auto __u = __find_block<_IndexType, 0, _SubRank - 1,
>> +                                             _Slices...>();
>> +           if constexpr (__u != dynamic_extent)
>> +             return __u + 1;
>> +           else
>> +             return dynamic_extent;
>> +         }
>> +      }
>> +
>> +    template<_LayoutSide _Side, typename _IndexType, size_t _SubRank,
>> +             typename... _Slices>
>> +      static consteval size_t
>> +      __padded_block_begin()
>> +      {
>> +       if constexpr (_Side == _LayoutSide::__left)
>> +         return __padded_block_begin_generic<_IndexType, _SubRank,
>> +                                             _Slices...>();
>> +      }
>> +
>> +    template<_LayoutSide _Side>
>> +      struct _SubMdspanMapping;
>> +
>> +    template<>
>> +      struct _SubMdspanMapping<_LayoutSide::__left>
>> +      {
>> +       using _Layout = layout_left;
>> +       template<size_t _Pad> using _PaddedLayout =
>> layout_left_padded<_Pad>;
>> +
>> +       template<typename _Mapping, size_t _Us>
>> +         static consteval size_t
>> +         _S_pad()
>> +         {
>> +           using _Extents = typename _Mapping::extents_type;
>> +           constexpr auto __sta_exts = __static_extents<_Extents>(0, _Us);
>> +           if constexpr (!__all_static(__sta_exts))
>> +             return dynamic_extent;
>> +           else
>> +             return __fwd_prod(__sta_exts);
>> +         }
>> +
>> +       template<typename _IndexType, size_t _SubRank, typename... _Slices>
>> +         static consteval bool
>> +         _S_is_unpadded_submdspan()
>> +         { return __is_compact_block<_IndexType, _SubRank, _Slices...>();
>> }
>> +      };
>> +
>> +    template<typename _Mapping>
>> +      constexpr auto
>> +      __submdspan_mapping_impl(const _Mapping& __mapping)
>> +      { return submdspan_mapping_result{__mapping, 0}; }
>> +
>> +    template<typename _Mapping, typename... _Slices>
>> +      requires (sizeof...(_Slices) > 0)
>> +      constexpr auto
>> +      __submdspan_mapping_impl(const _Mapping& __mapping, _Slices...
>> __slices)
>> +      {
>> +       using _IndexType= typename _Mapping::index_type;
>> +       constexpr auto __side = __deduce_mapping_side<_Mapping>();
>> +       using _Trait = _SubMdspanMapping<__side>;
>> +
>> +       auto __offset = __suboffset(__mapping, __slices...);
>>
> The calls need to be qualified (and one above) and everywhere in general.
>   +       auto __sub_exts = submdspan_extents(__mapping.
> We should call  __mdspan::__subextents as we already canonicalized
> slices.
> 
>> +       using _SubExtents = decltype(__sub_exts);
>> +       constexpr auto __sub_rank = _SubExtents::rank();
>> +       if constexpr (_SubExtents::rank() == 0)
>> +         return submdspan_mapping_result{
>> +           typename _Trait::_Layout::mapping(__sub_exts), __offset};
>> +       else if constexpr (
>> +           _Trait::template _S_is_unpadded_submdspan<_IndexType,
>> __sub_rank,
>> +                                                     _Slices...>())
>> +         return submdspan_mapping_result{
>> +           typename _Trait::_Layout::mapping(__sub_exts), __offset};
>> +       else if constexpr (
>> +           constexpr auto __u = __padded_block_begin<__side, _IndexType,
>> +                                                    __sub_rank,
>> _Slices...>();
>> +           __u != dynamic_extent)
>> +         {
>> +           constexpr auto __pad = _Trait::template _S_pad<_Mapping,
>> __u>();
>> +           using _Layout = typename _Trait::template _PaddedLayout<__pad>;
>> +           return submdspan_mapping_result{
>> +             typename _Layout::mapping(__sub_exts, __mapping.stride(__u)),
>> +             __offset};
>> +         }
>> +       else
>> +         {
>> +           auto __sub_strides
>> +             = __substrides<_SubExtents>(__mapping, __slices...);
>> +           return submdspan_mapping_result{
>> +             layout_stride::mapping(__sub_exts, __sub_strides),
>> __offset};
>> +         }
>> +      }
>>   #endif // __glibcxx_submdspan
>>     }
>>
>>     template<typename _Extents>
>>       class layout_left::mapping
>>       {
>>       public:
>>         using extents_type = _Extents;
>>         using index_type = typename extents_type::index_type;
>>         using size_type = typename extents_type::size_type;
>> @@ -1168,20 +1532,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>         template<typename _OExtents>
>>          constexpr explicit
>>          mapping(const _OExtents& __oexts, __mdspan::__internal_ctor)
>> noexcept
>>          : _M_extents(__oexts)
>>          {
>>            static_assert(__mdspan::__representable_size<_OExtents,
>> index_type>,
>>              "The size of OtherExtents must be representable as
>> index_type");
>>
>> __glibcxx_assert(__mdspan::__is_representable_extents(_M_extents));
>>          }
>>
>> +#if __glibcxx_submdspan
>> +      template<__mdspan::__valid_canonical_slice_type<index_type>...
>> _Slices>
>> +       requires (extents_type::rank() == sizeof...(_Slices))
>> +       friend constexpr auto
>> +       submdspan_mapping(const mapping& __mapping, _Slices... __slices)
>> +       { return __mdspan::__submdspan_mapping_impl(__mapping,
>> __slices...); }
>> +#endif // __glibcxx_submdspan
>> +
>>          [[no_unique_address]] extents_type _M_extents{};
>>       };
>>
>>     namespace __mdspan
>>     {
>>       template<typename _Extents, typename... _Indices>
>>         constexpr typename _Extents::index_type
>>         __linear_index_right(const _Extents& __exts, _Indices... __indices)
>>         noexcept
>>         {
>> @@ -2824,16 +3196,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>       requires (sizeof...(_Extents) == sizeof...(_Slices))
>>       constexpr auto
>>       submdspan_canonicalize_slices(const extents<_IndexType, _Extents...>&
>> __exts,
>>                                    _Slices... __raw_slices)
>>       {
>>         auto [...__slices]
>>          = make_tuple(__mdspan::__slice_cast<_IndexType>(__raw_slices)...);
>>         __mdspan::__assert_valid_slices(__exts, __slices...);
>>         return make_tuple(__slices...);
>>       }
>> +
>> +  template<typename _ElementType, typename _Extents, typename _Layout,
>> +          typename _Accessor, typename... _Slices>
>> +    requires (sizeof...(_Slices) == _Extents::rank())
>> +    constexpr auto
>> +    submdspan(
>> +       const mdspan<_ElementType, _Extents, _Layout, _Accessor>& __md,
>> +       _Slices... __raw_slices)
>> +    {
>> +      auto [...__slices] = submdspan_canonicalize_slices(__md.extents(),
>> +                                                        __raw_slices...);
>>
> Again, we do not want to instantiate tuples, so _would do __impl lambda
> trick here,
> were we pass canonicalized slices as arguments.
> 
>> +      auto __result = submdspan_mapping(__md.mapping(), __slices...);
>> +      return mdspan(__md.accessor().offset(__md.data_handle(),
>> __result.offset),
>> +         __result.mapping, typename
>> _Accessor::offset_policy(__md.accessor()));
>> +    }
>>   #endif // __glibcxx_submdspan
>>
>>   _GLIBCXX_END_NAMESPACE_VERSION
>>   }
>>   #endif
>>   #endif
>> diff --git a/libstdc++-v3/src/c++23/std.cc.in b/libstdc++-v3/src/c++23/
>> std.cc.in
>> index c2a9293b05a..2dac6a6d887 100644
>> --- a/libstdc++-v3/src/c++23/std.cc.in
>> +++ b/libstdc++-v3/src/c++23/std.cc.in
>> @@ -1878,22 +1878,22 @@ export namespace std
>>     using std::layout_left_padded;
>>     using std::layout_right_padded;
>>   #endif
>>   #if __glibcxx_submdspan
>>     using std::strided_slice;
>>     using std::full_extent_t;
>>     using std::full_extent;
>>     using std::submdspan_mapping_result;
>>     using std::submdspan_canonicalize_slices;
>>     using std::submdspan_extents;
>> +  using std::submdspan;
>>   #endif
>> -  // FIXME mdsubspan
>>   }
>>   #endif
>>
>>   // 20.2 <memory>
>>   export namespace std
>>   {
>>     using std::align;
>>     using std::allocator;
>>     using std::allocator_arg;
>>     using std::allocator_arg_t;
>> diff --git
>> a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc
>> b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc
>> new file mode 100644
>> index 00000000000..53e91407a9c
>> --- /dev/null
>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan.cc
>> @@ -0,0 +1,369 @@
>> +// { dg-do run { target c++26 } }
>> +#include <mdspan>
>> +
>> +#include <iostream> // TODO remove
>> +#include <vector>
>> +#include <numeric>
>> +#include "../layout_traits.h"
>> +#include <testsuite_hooks.h>
>> +
>> +constexpr size_t dyn = std::dynamic_extent;
>> +constexpr auto all = std::full_extent;
>> +
>> +template<typename T>
>> +  constexpr bool is_strided_slice = false;
>> +
>> +template<typename O, typename E, typename S>
>> +  constexpr bool is_strided_slice<std::strided_slice<O, E, S>> = true;
>> +
>> +template<typename MDSpan>
>> +  constexpr void
>> +  fill(const MDSpan& md)
>> +  {
>> +    using IndexType = typename MDSpan::index_type;
>> +    auto exts = md.extents();
>> +    if constexpr (exts.rank() == 3)
>> +      for(IndexType i = 0; i < exts.extent(0); ++i)
>> +       for(IndexType j = 0; j < exts.extent(1); ++j)
>> +         for(IndexType k = 0; k < exts.extent(2); ++k)
>> +           md[i, j, k] = 100 * i + 10 * j + k;
>> +  }
>> +
>> +template<typename Int, size_t Rank>
>> +  class multi_index_generator
>> +  {
>> +    class EndIt
>> +    { };
>> +
>> +    class BeginIt
>> +    {
>> +    public:
>> +      constexpr
>> +      BeginIt(const std::array<Int, Rank>& shape)
>> +       : M_shape(shape)
>> +      { }
>> +
>> +      constexpr BeginIt&
>> +      operator++()
>> +      {
>> +       if constexpr (Rank > 0)
>> +         {
>> +           ++M_indices[Rank-1];
>> +           for(size_t i = Rank; i > 1; --i)
>> +             if (M_indices[i-1] == M_shape[i-1])
>> +               {
>> +                 M_indices[i-1] = 0;
>> +                 ++M_indices[i-2];
>> +               }
>> +         }
>> +       return *this;
>> +      }
>> +
>> +      constexpr auto
>> +      operator*()
>> +      { return M_indices; }
>> +
>> +      constexpr bool
>> +      operator==(EndIt)
>> +      {
>> +       if constexpr (Rank > 0)
>> +         return M_indices[0] == M_shape[0];
>> +       else
>> +         return true;
>> +      }
>> +
>> +    private:
>> +      std::array<Int, Rank> M_indices{};
>> +      std::array<Int, Rank> M_shape;
>> +    };
>> +
>> +  public:
>> +    constexpr
>> +    multi_index_generator(std::array<Int, Rank> shape)
>> +      : M_shape(shape)
>> +    { }
>> +
>> +    constexpr BeginIt
>> +    begin() const
>> +    { return BeginIt(M_shape); }
>> +
>> +    constexpr EndIt
>> +    end() const
>> +    { return EndIt{}; }
>> +
>> +  private:
>> +    std::array<Int, Rank> M_shape;
>> +  };
>> +
>> +constexpr bool
>> +test_multi_index()
>> +{
>> +  auto shape = std::array{3, 5, 7, 1};
>> +
>> +  std::vector<std::array<int, 4>> expected;
>> +  for (int i = 0; i < shape[0]; ++i)
>> +    for (int j = 0; j < shape[1]; ++j)
>> +      for (int k = 0; k <shape[2]; ++k)
>> +       for (int l = 0; l <shape[3]; ++l)
>> +         expected.push_back(std::array{i, j, k, l});
>> +
>> +  size_t i = 0;
>> +  for (auto actual : multi_index_generator(shape))
>> +    VERIFY(expected[i++] == actual);
>> +  return true;
>> +}
>> +
>> +static_assert(test_multi_index());
>> +
>> +struct
>> +collapse
>> +{ };
>> +
>> +template<typename... Slices>
>> +  consteval auto
>> +  inv_collapsed_index_map()
>> +  {
>> +    constexpr size_t rank = sizeof...(Slices);
>> +    auto is_collapsing = std::array{std::same_as<Slices, collapse>...};
>> +    constexpr auto collapsed_rank = ((!std::same_as<Slices, collapse>) +
>> ... + 0);
>> +
>> +    std::array<size_t, collapsed_rank> ret;
>> +    if constexpr (collapsed_rank > 0)
>> +      for(size_t k = 0, i = 0; i < rank; ++i)
>> +       if (!is_collapsing[i])
>> +         ret[k++] = i;
>> +    return ret;
>> +  }
>> +
>> +static_assert(inv_collapsed_index_map<collapse, collapse, collapse>()
>> +             == std::array<size_t, 0>{});
>> +
>> +static_assert(inv_collapsed_index_map<collapse, decltype(all), collapse>()
>> +             == std::array<size_t, 1>{1});
>> +
>> +template<typename IndexType, typename Slice>
>> +  constexpr std::vector<IndexType>
>> +  make_selection(IndexType extent, const Slice& slice)
>> +  {
>> +    if constexpr (std::convertible_to<Slice, IndexType>)
>> +      return {static_cast<IndexType>(slice)};
>> +    else if constexpr (std::same_as<Slice, std::full_extent_t>)
>> +      {
>> +       auto ret = std::vector<IndexType>(static_cast<size_t>(extent));
>> +       std::ranges::iota(ret, 0);
>> +       return ret;
>> +      }
>> +    else if constexpr (is_strided_slice<Slice>)
>> +      {
>> +       auto ret = std::vector<IndexType>{};
>> +       size_t n = static_cast<size_t>(slice.extent);
>> +       for(size_t i = 0; i < n; i += slice.stride)
>> +         ret.push_back(slice.offset + i);
>> +       return ret;
>> +      }
>> +    else
>> +      {
>> +       auto [begin, end] = slice;
>> +       auto ret = std::vector<IndexType>(static_cast<size_t>(end -
>> begin));
>> +       std::ranges::iota(ret, begin);
>> +       return ret;
>> +      }
>> +  }
>> +
>> +template<typename Layout, size_t... I, typename... Slices>
>> +  constexpr bool
>> +  check_selection(std::index_sequence<I...>, auto md, Slices... slices)
>> +  {
>> +    auto exts = md.extents();
>> +    auto outer_shape = std::array{exts.extent(0), exts.extent(1),
>> exts.extent(2)};
>> +
>> +    constexpr auto full_index = inv_collapsed_index_map<Slices...>();
>> +    auto make_slice = [](size_t i, auto slice)
>> +    {
>> +      if constexpr (std::same_as<decltype(slice), collapse>)
>> +       return i;
>> +      else
>> +       return slice;
>> +    };
>> +
>> +    auto loop_body = [&]<size_t... J>(std::index_sequence<J...>, auto ijk,
>> +                                     auto... slices)
>> +    {
>> +      auto submd = submdspan(md, slices...[I]...);
>> +      auto selection = std::tuple{make_selection(exts.extent(I),
>> slices...[I])...};
>> +      auto inner_shape = std::array<size_t, full_index.size()>{
>> +       std::get<full_index[J]>(selection).size()...
>> +      };
>> +
>> +      for (auto ij : multi_index_generator(inner_shape))
>> +      {
>> +       ((ijk[full_index[J]] = get<full_index[J]>(selection)[ij[J]]),...);
>> +       VERIFY(submd[ij] == md[ijk]);
>> +      }
>> +    };
>> +
>> +    for (auto ijk : multi_index_generator(outer_shape))
>> +      loop_body(std::make_index_sequence<full_index.size()>(), ijk,
>> +               make_slice(ijk[I], slices...[I])...);
>> +    return true;
>> +  }
>> +
>> +template<typename Layout, typename...MD, typename... Slices>
>> +  constexpr bool
>> +  check_selection(std::mdspan<MD...> md, Slices... slices)
>> +  {
>> +    auto indices = std::make_index_sequence<sizeof...(slices)>();
>> +    return check_selection<Layout>(indices, md, slices...);
>> +  }
>> +
>> +template<typename Layout, typename IndexType, size_t... Extents,
>> +        typename... Slices>
>> +  constexpr bool
>> +  check_selection(std::extents<IndexType, Extents...>exts, Slices...
>> slices)
>> +  {
>> +    auto run = [&](auto m)
>> +    {
>> +      auto storage = std::vector<double>(m.required_span_size());
>> +      auto md = std::mdspan(storage.data(), m);
>> +      fill(md);
>> +      return check_selection<Layout>(md, slices...);
>> +    };
>> +
>> +    if constexpr (std::same_as<Layout, std::layout_stride>)
>> +      {
>> +       auto m = typename Layout::mapping(exts, std::array{15, 2, 50});
>> +       return run(m);
>> +      }
>> +    else
>> +      {
>> +       auto m = typename Layout::mapping(exts);
>> +       return run(m);
>> +      }
>> +  }
>> +
>> +template<typename Layout>
>> +  constexpr bool
>> +  test_scalar_selection(auto exts)
>> +  {
>> +    check_selection<Layout>(exts, collapse{}, collapse{}, collapse{});
>> +    return true;
>> +  }
>> +
>> +template<typename Layout>
>> +  constexpr bool
>> +  test_full_lines(auto exts)
>> +  {
>> +    check_selection<Layout>(exts, all, collapse{}, collapse{});
>> +    check_selection<Layout>(exts, collapse{}, all, collapse{});
>> +    check_selection<Layout>(exts, collapse{}, collapse{}, all);
>> +    return true;
>> +  }
>> +
>> +template<typename Layout>
>> +  constexpr bool
>> +  test_full_blocks(auto exts)
>> +  {
>> +    check_selection<Layout>(exts, all, all, collapse{});
>> +    check_selection<Layout>(exts, all, collapse{}, all);
>> +    check_selection<Layout>(exts, collapse{}, all, all);
>> +    return true;
>> +  }
>> +
>> +template<typename Layout>
>> +  constexpr bool
>> +  test_cubes(auto exts)
>> +  {
>> +    auto s0 = std::pair{0, 2};
>> +    auto s1 = std::pair{1, 4};
>> +    auto s2 = std::pair{3, 7};
>> +
>> +    check_selection<Layout>(exts, all, all, all);
>> +    check_selection<Layout>(exts, all, all, s2);
>> +    check_selection<Layout>(exts, s0, all, all);
>> +    check_selection<Layout>(exts, s0, all, s2);
>> +    check_selection<Layout>(exts, s0, s1, s2);
>> +    return true;
>> +  }
>> +
>> +template<typename Layout>
>> +  constexpr bool
>> +  test_strided_line_selection(auto exts)
>> +  {
>> +    auto check = [&](auto s)
>> +    {
>> +      check_selection<Layout>(exts, collapse{}, s, collapse{});
>> +    };
>> +
>> +    check(std::strided_slice(0, 2, 2));
>> +    check(std::strided_slice(0, 3, 2));
>> +    check(std::strided_slice(1, 3, 2));
>> +    check(std::strided_slice(1, std::cw<3>, std::cw<2>));
>> +    return true;
>> +  }
>> +
>> +template<typename Layout>
>> +  constexpr bool
>> +  test_strided_box_selection(auto exts)
>> +  {
>> +    auto s0 = std::strided_slice(0, 3, 2);
>> +    auto s1 = std::strided_slice(1, 4, 2);
>> +    auto s2 = std::strided_slice(0, 7, 3);
>> +
>> +    check_selection<Layout>(exts, s0, s1, s2);
>> +    return true;
>> +  }
>> +
>> +template<typename Layout>
>> +  constexpr bool
>> +  test_all_cheap()
>> +  {
>> +    constexpr auto dyn_exts = std::extents(3, 5, 7);
>> +    constexpr auto sta_exts = std::extents<int, 3, 5, 7>{};
>> +
>> +    test_scalar_selection<Layout>(dyn_exts);
>> +    test_scalar_selection<Layout>(sta_exts);
>> +    static_assert(test_scalar_selection<Layout>(dyn_exts));
>> +    static_assert(test_scalar_selection<Layout>(sta_exts));
>> +
>> +    test_full_lines<Layout>(dyn_exts);
>> +    test_full_lines<Layout>(sta_exts);
>> +    static_assert(test_full_lines<Layout>(dyn_exts));
>> +    static_assert(test_full_lines<Layout>(sta_exts));
>> +
>> +    test_strided_box_selection<Layout>(dyn_exts);
>> +    test_strided_box_selection<Layout>(sta_exts);
>> +    static_assert(test_strided_box_selection<Layout>(dyn_exts));
>> +    static_assert(test_strided_box_selection<Layout>(sta_exts));
>> +    return true;
>> +  }
>> +
>> +template<typename Layout>
>> +  constexpr bool
>> +  test_all_expensive()
>> +  {
>> +    auto run = [](auto exts)
>> +    {
>> +      test_full_blocks<Layout>(exts);
>> +      test_cubes<Layout>(exts);
>> +    };
>> +
>> +    run(std::extents(3, 5, 7));
>> +    run(std::extents<int, 3, 5, 7>{});
>> +    return true;
>> +  }
>> +
>> +template<typename Layout>
>> +  constexpr bool
>> +  test_all()
>> +  {
>> +    test_all_cheap<Layout>();
>> +    test_all_expensive<Layout>();
>> +    return true;
>> +  }
>> +
>> +int
>> +main()
>> +{
>> +  test_all<std::layout_left>();
>> +  return 0;
>> +}
>> diff --git
>> a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc
>> b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc
>> new file mode 100644
>> index 00000000000..a37d3cd588f
>> --- /dev/null
>> +++
>> b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_mapping.cc
>> @@ -0,0 +1,136 @@
>> +// { dg-do run { target c++26 } }
>> +#include <mdspan>
>> +
>> +#include <iostream> // TODO remove
>> +#include "../layout_traits.h"
>> +#include <testsuite_hooks.h>
>> +
>> +constexpr size_t dyn = std::dynamic_extent;
>> +
>> +template<typename Mapping, typename... Slices>
>> +  constexpr auto
>> +  call_submdspan_mapping(const Mapping& m, std::tuple<Slices...> slices)
>> +  {
>> +    auto impl = [&]<size_t... I>(std::index_sequence<I...>)
>> +    { return submdspan_mapping(m, get<I>(slices)...); };
>> +    return impl(std::make_index_sequence<sizeof...(Slices)>());
>> +  }
>> +
>> +template<typename Layout>
>> +  constexpr bool
>> +  test_layout_unpadded_return_types()
>> +  {
>> +    constexpr auto padding_side =
>> DeducePaddingSide::from_typename<Layout>();
>> +    using Traits = LayoutTraits<padding_side>;
>> +
>> +    {
>> +      auto m0 = typename Layout::mapping(std::extents());
>> +      auto result = submdspan_mapping(m0);
>> +      using layout_type = typename decltype(result.mapping)::layout_type;
>> +      static_assert(std::same_as<layout_type, Layout>);
>> +    }
>> +
>> +    auto exts = Traits::make_extents(std::dims<5, int>(3, 5, 7, 11, 13));
>> +    auto m = typename Layout::mapping(exts);
>> +    auto all = std::full_extent;
>> +    auto s251 = std::strided_slice{2, 5, std::cw<1>};
>> +
>> +    {
>> +      auto slices = std::tuple{0, 0, 0, 0, 0};
>> +      auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
>> +      using layout_type = typename decltype(result.mapping)::layout_type;
>> +      static_assert(std::same_as<layout_type, Layout>);
>> +    }
>> +
>> +    {
>> +      auto slices = std::tuple{all, all, all, s251, 0};
>> +      auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
>> +      using layout_type = typename decltype(result.mapping)::layout_type;
>> +      static_assert(std::same_as<layout_type, Layout>);
>> +    }
>> +
>> +    {
>> +      auto s0 = std::strided_slice{1, 1, std::cw<1>};
>> +      auto slices = std::tuple{s0, all, all, s251, 0};
>> +      auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
>> +      using layout_type = typename decltype(result.mapping)::layout_type;
>> +      static_assert(is_same_padded<padding_side, layout_type>);
>> +    }
>> +
>> +    {
>> +      auto s0 = std::strided_slice{1, 2, std::cw<1>};
>> +      auto slices = std::tuple{s0, all, all, s251, 0};
>> +      auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
>> +      using layout_type = typename decltype(result.mapping)::layout_type;
>> +      static_assert(is_same_padded<padding_side, layout_type>);
>> +    }
>> +
>> +    {
>> +      auto s0 = std::strided_slice{1, 2, std::cw<1>};
>> +      auto slices = std::tuple{s0, 0, all, s251, 0};
>> +      auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
>> +      using layout_type = typename decltype(result.mapping)::layout_type;
>> +      static_assert(is_same_padded<padding_side, layout_type>);
>> +    }
>> +
>> +    {
>> +      auto s0 = std::strided_slice{1, 2, 1};
>> +      auto slices = std::tuple{s0, all, all, s251, 0};
>> +      auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
>> +      using layout_type = decltype(result.mapping)::layout_type;
>> +      static_assert(std::same_as<layout_type, std::layout_stride>);
>> +    }
>> +
>> +    {
>> +      auto slices = std::tuple{1, all, all, s251, 0};
>> +      auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
>> +      using layout_type = decltype(result.mapping)::layout_type;
>> +      static_assert(std::same_as<layout_type, std::layout_stride>);
>> +    }
>> +
>> +    {
>> +      auto s3 = std::strided_slice{2, std::cw<7>, std::cw<2>};
>> +      auto slices = std::tuple{all, all, all, s3, 0};
>> +      auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
>> +      using layout_type = decltype(result.mapping)::layout_type;
>> +      static_assert(std::same_as<layout_type, std::layout_stride>);
>> +    }
>> +    return true;
>> +  }
>> +
>> +template<typename Layout>
>> +  constexpr bool
>> +  test_layout_unpadded_padding_value()
>> +  {
>> +    using Traits =
>> LayoutTraits<DeducePaddingSide::from_typename<Layout>()>;
>> +    auto s0 = std::strided_slice{size_t(1), size_t(2),
>> std::cw<size_t(1)>};
>> +    auto s3 = std::strided_slice{size_t(2), size_t(5),
>> std::cw<size_t(1)>};
>> +    auto all = std::full_extent;
>> +
>> +    auto check = [&](auto exts, size_t expected)
>> +    {
>> +      auto m = typename Layout::mapping(Traits::make_extents(exts));
>> +      auto slices = std::tuple{s0, size_t(0), all, s3, size_t(0)};
>> +      auto result = call_submdspan_mapping(m, Traits::make_tuple(slices));
>> +      auto padding_value = decltype(result.mapping)::padding_value;
>> +      VERIFY(padding_value == expected);
>> +    };
>> +
>> +    check(std::extents(std::cw<3>, std::cw<5>, std::cw<7>, 11, 13), 3*5);
>> +    check(std::extents(std::cw<3>, std::cw<5>, 7, 11, 13), 3*5);
>> +    check(std::extents(std::cw<3>, 5, 7, 11, 13), dyn);
>> +    check(std::extents(3, 5, 7, 11, 13), dyn);
>> +    return true;
>> +  }
>> +
>> +int
>> +main()
>> +{
>> +  test_layout_unpadded_return_types<std::layout_left>();
>> +  static_assert(test_layout_unpadded_return_types<std::layout_left>());
>> +
>> +  test_layout_unpadded_padding_value<std::layout_left>();
>> +  static_assert(test_layout_unpadded_padding_value<std::layout_left>());
>> +  return 0;
>> +}
>> +
>> diff --git
>> a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc
>> b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc
>> new file mode 100644
>> index 00000000000..4f9aad81cb7
>> --- /dev/null
>> +++
>> b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_neg.cc
>> @@ -0,0 +1,102 @@
>> +// { dg-do compile { target c++26 } }
>> +#include <mdspan>
>> +
>> +#include <vector>
>> +
>> +template<typename Layout, typename... Slices>
>> +  constexpr bool
>> +  check_slice_range(Slices... slices)
>> +  {
>> +    auto m = typename Layout::mapping<std::extents<int, 3, 5, 7>>{};
>> +    auto storage = std::vector<double>(m.required_span_size());
>> +    auto md = std::mdspan(storage.data(), m);
>> +
>> +    auto submd = submdspan(md, slices...);           // { dg-error
>> "expansion of" }
>> +    (void) submd;
>> +    return true;
>> +  }
>> +
>> +template<typename Layout>
>> +  constexpr bool
>> +  test_int_under()
>> +  {
>> +    check_slice_range<Layout>(1, -1, 2);             // { dg-error
>> "expansion of" }
>> +    return true;
>> +  }
>> +static_assert(test_int_under<std::layout_left>());   // { dg-error
>> "expansion of" }
>> +
>> +template<typename Layout>
>> +  constexpr bool
>> +  test_int_over()
>> +  {
>> +    check_slice_range<Layout>(1, 5, 2);              // { dg-error
>> "expansion of" }
>> +    return true;
>> +  }
>> +static_assert(test_int_over<std::layout_left>());    // { dg-error
>> "expansion of" }
>> +
>> +template<typename Layout>
>> +  constexpr bool
>> +  test_tuple_under()
>> +  {
>> +    check_slice_range<Layout>(1, std::tuple{-1, 2}, 2);  // { dg-error
>> "expansion of" }
>> +    return true;
>> +  }
>> +static_assert(test_tuple_under<std::layout_left>());     // { dg-error
>> "expansion of" }
>> +
>> +template<typename Layout>
>> +  constexpr bool
>> +  test_tuple_reversed()
>> +  {
>> +    check_slice_range<Layout>(1, std::tuple{3, 2}, 2);   // { dg-error
>> "expansion of" }
>> +    return true;
>> +  }
>> +static_assert(test_tuple_reversed<std::layout_left>());   // { dg-error
>> "expansion of" }
>> +
>> +template<typename Layout>
>> +  constexpr bool
>> +  test_tuple_over()
>> +  {
>> +    check_slice_range<Layout>(1, std::tuple{0, 6}, 2); // { dg-error
>> "expansion of" }
>> +    return true;
>> +  }
>> +static_assert(test_tuple_over<std::layout_left>());   // { dg-error
>> "expansion of" }
>> +
>> +template<typename Layout>
>> +  constexpr bool
>> +  test_strided_slice_zero()
>> +  {
>> +    check_slice_range<Layout>(1, std::strided_slice{1, 1, 0}, 2);  // {
>> dg-error "expansion of" }
>> +    return true;
>> +  }
>> +static_assert(test_strided_slice_zero<std::layout_left>());   // {
>> dg-error "expansion of" }
>> +
>> +template<typename Layout>
>> +  constexpr bool
>> +  test_strided_slice_offset_under()
>> +  {
>> +    check_slice_range<Layout>(1, std::strided_slice{-1, 1, 1}, 2);   // {
>> dg-error "expansion of" }
>> +    return true;
>> +  }
>> +static_assert(test_strided_slice_offset_under<std::layout_left>());   //
>> { dg-error "expansion of" }
>> +
>> +template<typename Layout>
>> +  constexpr bool
>> +  test_strided_slice_offset_over()
>> +  {
>> +    check_slice_range<Layout>(1, std::strided_slice{6, 0, 1}, 2);    // {
>> dg-error "expansion of" }
>> +    return true;
>> +  }
>> +static_assert(test_strided_slice_offset_over<std::layout_left>());   // {
>> dg-error "expansion of" }
>> +
>> +template<typename Layout>
>> +  constexpr bool
>> +  test_strided_slice_extent_over()
>> +  {
>> +    check_slice_range<Layout>(1, std::strided_slice{1, 5, 1}, 2);    // {
>> dg-error "expansion of" }
>> +    return true;
>> +  }
>> +static_assert(test_strided_slice_extent_over<std::layout_left>());   // {
>> dg-error "expansion of" }
>> +
>> +// { dg-prune-output "static assertion failed" }
>> +// { dg-prune-output "__glibcxx_assert_fail" }
>> +// { dg-prune-output "non-constant condition" }
>> --
>> 2.51.2
>>
>>
> 



More information about the Libstdc++ mailing list