[PATCH v2 5/5] libstdc++: Implement std::layout_left_padded.

Tomasz Kaminski tkaminsk@redhat.com
Thu Sep 11 15:53:06 GMT 2025


On Thu, Sep 11, 2025 at 5:04 PM Luc Grosheintz <luc.grosheintz@gmail.com>
wrote:

>
>
> On 9/11/25 16:07, Tomasz Kaminski wrote:
> > On Thu, Sep 11, 2025 at 1:46 PM Luc Grosheintz <luc.grosheintz@gmail.com
> >
> > wrote:
> >
> >> This commit adds a new layout layout_left_padded as standardized in
> >> N5014 but with one deviation. It includes checking of all mandates and
> >> prerequisites. It adds a purely internal feature testing macro
> >> padded_layouts and registers layout_left_padded in the std module.
> >>
> >> The standard mandates that the padding_value is representable as
> >> index_type. However, padding_value can be dynamic_extent which is
> >> defined as `size_t(-1)`. Hence, it's impossible to use a dynamic
> >> padding_value if index_type is int. The deviation is to only require
> >> static padding values to be representable as index_type; and thereby
> >> allow dynamic padding values for every index_type.
> >>
> >> libstdc++-v3/ChangeLog:
> >>
> >>          * include/bits/version.def (padded_layouts): Add as internal
> >>          only.
> >>          * include/bits/version.h: Regenerate.
> >>          * include/std/mdspan (__fwd_prod): New overload.
> >>          (layout_left_padded): Add declaration and implementation.
> >>          (layout_right_padded): Add declaration only.
> >>          (__is_left_padded_mapping): New concept.
> >>          (__is_right_padded_mapping): Ditto.
> >>          (__standardized_mapping): Recognize left and right padded
> >>          mappings.
> >>          (layout_left::mapping::mapping): New overload for left
> >>          padded mappings.
> >>          * src/c++23/std.cc.in (layout_left_padded): Add.
> >>          * testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc:
> >>          Add tests for layout_left_padded.
> >>          * testsuite/23_containers/mdspan/layouts/ctors.cc: Ditto.
> >>          * testsuite/23_containers/mdspan/layouts/empty.cc: Ditto.
> >>          * testsuite/23_containers/mdspan/layouts/mapping.cc: Ditto.
> >>          * testsuite/23_containers/mdspan/layouts/debug/padded_neg.cc:
> New
> >> test.
> >>          * testsuite/23_containers/mdspan/layouts/padded.cc: Ditto.
> >>          * testsuite/23_containers/mdspan/layouts/padded_neg.cc: Ditto.
> >>
> >> Signed-off-by: Luc Grosheintz <luc.grosheintz@gmail.com>
> >>
> > Below is only a partial review of the changes.
> >
> >> ---
> >>   libstdc++-v3/include/bits/version.def         |  10 +
> >>   libstdc++-v3/include/bits/version.h           |   9 +
> >>   libstdc++-v3/include/std/mdspan               | 522 ++++++++++++++-
> >>   libstdc++-v3/src/c++23/std.cc.in              |   8 +-
> >>   .../mdspan/layouts/class_mandate_neg.cc       |   1 +
> >>   .../23_containers/mdspan/layouts/ctors.cc     |  61 +-
> >>   .../mdspan/layouts/debug/padded_neg.cc        |  22 +
> >>   .../23_containers/mdspan/layouts/empty.cc     |  12 +-
> >>   .../23_containers/mdspan/layouts/mapping.cc   |  72 ++-
> >>   .../23_containers/mdspan/layouts/padded.cc    | 611 ++++++++++++++++++
> >>   .../mdspan/layouts/padded_neg.cc              | 280 ++++++++
> >>   11 files changed, 1592 insertions(+), 16 deletions(-)
> >>   create mode 100644
> >> libstdc++-v3/testsuite/23_containers/mdspan/layouts/debug/padded_neg.cc
> >>   create mode 100644
> >> libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc
> >>   create mode 100644
> >> libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_neg.cc
> >>
> >> diff --git a/libstdc++-v3/include/bits/version.def
> >> b/libstdc++-v3/include/bits/version.def
> >> index 65b9a278776..63e5f394a8f 100644
> >> --- a/libstdc++-v3/include/bits/version.def
> >> +++ b/libstdc++-v3/include/bits/version.def
> >> @@ -1050,6 +1050,16 @@ ftms = {
> >>       cxxmin = 26;
> >>       extra_cond = "__glibcxx_assume_aligned "
> >>       "&& __glibcxx_is_sufficiently_aligned";
> >> +    };
> >> +};
> >> +
> >> +// Unofficial macro signaling presence of the two padded layouts.
> >> +ftms = {
> >> +  name = padded_layouts;
> >> +  no_stdname = true; // Don't change: it's not a standardized FTM.
> >> +  values = {
> >> +    v = 1;
> >> +    cxxmin = 26;
> >>     };
> >>   };
> >>
> >> diff --git a/libstdc++-v3/include/bits/version.h
> >> b/libstdc++-v3/include/bits/version.h
> >> index b05249857d2..6b4197e599d 100644
> >> --- a/libstdc++-v3/include/bits/version.h
> >> +++ b/libstdc++-v3/include/bits/version.h
> >> @@ -1178,6 +1178,15 @@
> >>   #endif /* !defined(__cpp_lib_aligned_accessor) &&
> >> defined(__glibcxx_want_aligned_accessor) */
> >>   #undef __glibcxx_want_aligned_accessor
> >>
> >> +#if !defined(__cpp_lib_padded_layouts)
> >> +# if (__cplusplus >  202302L)
> >> +#  define __glibcxx_padded_layouts 1L
> >> +#  if defined(__glibcxx_want_all) ||
> >> defined(__glibcxx_want_padded_layouts)
> >> +#  endif
> >> +# endif
> >> +#endif /* !defined(__cpp_lib_padded_layouts) &&
> >> defined(__glibcxx_want_padded_layouts) */
> >> +#undef __glibcxx_want_padded_layouts
> >> +
> >>   #if !defined(__cpp_lib_ssize)
> >>   # if (__cplusplus >= 202002L)
> >>   #  define __glibcxx_ssize 201902L
> >> diff --git a/libstdc++-v3/include/std/mdspan
> >> b/libstdc++-v3/include/std/mdspan
> >> index 9dd0c85f5b0..251566c7051 100644
> >> --- a/libstdc++-v3/include/std/mdspan
> >> +++ b/libstdc++-v3/include/std/mdspan
> >> @@ -233,6 +233,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>          _S_static_extents() noexcept
> >>          { return _Extents; }
> >>
> >> +       static constexpr span<const size_t>
> >> +       _S_static_extents(size_t __begin, size_t __end) noexcept
> >> +       { return span(_Extents.data() + __begin, __end - __begin); }
> >> +
> >>          constexpr span<const _IndexType>
> >>          _M_dynamic_extents(size_t __begin, size_t __end) const noexcept
> >>          requires (_Extents.size() > 0)
> >> @@ -261,6 +265,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>         __static_extents() noexcept
> >>         { return _Extents::_Storage::_S_static_extents(); }
> >>
> >> +    template<typename _Extents>
> >> +      constexpr span<const size_t>
> >> +      __static_extents(size_t __begin, size_t __end) noexcept
> >> +      { return _Extents::_Storage::_S_static_extents(__begin, __end); }
> >>
> > Instead of befriending this function, and adding new member of extents,
> > I would just implement it as:
> >     __static_extents<_Extents>().subspan(__begin, __end).
> > Also I would add default argument for __end to be equal __Extent::rank();
> >
>
> Yes, neat.
>
> >> +
> >>       // Pre-compute: \prod_{i = 0}^r _Extents[i], for r = 0,..., n
> >> (exclusive)
> >>       template<array _Extents>
> >>         constexpr auto __fwd_partial_prods = [] consteval
> >> @@ -420,6 +429,9 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>         friend const array<size_t, rank()>&
> >>         __mdspan::__static_extents<extents>();
> >>
> >> +      friend span<const size_t>
> >> +      __mdspan::__static_extents<extents>(size_t __begin, size_t
> __end);
> >>
> > Then remove this friend.
> >
> >> +
> >>         friend span<const index_type>
> >>         __mdspan::__dynamic_extents<extents>(const extents&, size_t,
> >> size_t);
> >>
> >> @@ -476,6 +488,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>         }
> >>
> >>       // Preconditions: _r < _Extents::rank()
> >> +    template<typename _Extents>
> >> +      constexpr typename _Extents::index_type
> >> +      __fwd_prod(const _Extents& __exts, size_t __begin, size_t __end)
> >> noexcept
> >> +      {
> >> +       size_t __sta_prod = [__begin, __end] {
> >> +         span<const size_t> __sta_exts = __static_extents<_Extents>();
> >> +         size_t __ret = 1;
> >> +         for(auto __ext : __sta_exts.subspan(__begin, __end - __begin))
> >> +           if (__ext != dynamic_extent)
> >> +             __ret *= __ext;
> >> +         return __ret;
> >> +       }();
> >> +       return __extents_prod(__exts, __sta_prod, __begin, __end);
> >> +      }
> >> +
> >>       template<typename _Extents>
> >>         constexpr typename _Extents::index_type
> >>         __fwd_prod(const _Extents& __exts, size_t __r) noexcept
> >> @@ -567,6 +594,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>         class mapping;
> >>     };
> >>
> >> +#ifdef __glibcxx_padded_layouts
> >> +  template<size_t _PaddingValue>
> >> +    struct layout_left_padded
> >> +    {
> >> +      template<typename _Extents>
> >> +       class mapping;
> >> +    };
> >> +
> >> +  template<size_t _PaddingValue>
> >> +    struct layout_right_padded
> >> +    {
> >> +      template<typename _Extents>
> >> +       class mapping;
> >> +    };
> >> +#endif
> >> +
> >>     namespace __mdspan
> >>     {
> >>       template<typename _Tp>
> >> @@ -669,10 +712,39 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>          is_same_v<typename _Layout::template mapping<typename
> >> _Mapping::extents_type>,
> >>                    _Mapping>;
> >>
> >> +    template<template<size_t> typename _Layout, typename _Mapping>
> >> +      concept __is_padded_mapping_of = requires
> >>
> > I have suggested to replace this by:
> >    __is_mapping_of< _Layout<_Mapping::padding_value>, _Mapping>>;
> > Didn't it work?
>
> Sorry, I missed that.
>
> >
> >> +      {
> >> +       typename _Mapping::extents_type;
> >> +       { _Mapping::padding_value } -> same_as<const size_t&>;
> >> +      }
> >> +      && same_as<_Mapping, typename
> >> _Layout<_Mapping::padding_value>::mapping<
> >> +         typename _Mapping::extents_type>>;
> >> +
> >> +#ifdef __glibcxx_padded_layouts
> >> +    template<typename _Mapping>
> >> +      constexpr bool __is_left_padded_mapping = __is_padded_mapping_of<
> >> +       layout_left_padded, _Mapping>;
> >> +
> >> +    template<typename _Mapping>
> >> +      constexpr bool __is_right_padded_mapping =
> __is_padded_mapping_of<
> >> +       layout_right_padded, _Mapping>;
> >> +#endif
> >> +
> >> +    template<typename _PaddedMapping>
> >> +      consteval size_t
> >> +      __get_static_stride()
> >> +      { return _PaddedMapping::_S_static_stride; }
> >> +
> >>       template<typename _Mapping>
> >>         concept __standardized_mapping = __mapping_of<layout_left,
> _Mapping>
> >>                                         || __mapping_of<layout_right,
> >> _Mapping>
> >> -                                      || __mapping_of<layout_stride,
> >> _Mapping>;
> >> +                                      || __mapping_of<layout_stride,
> >> _Mapping>
> >> +#ifdef __glibcxx_padded_layouts
> >> +                                      ||
> >> __is_left_padded_mapping<_Mapping>
> >> +                                      ||
> >> __is_right_padded_mapping<_Mapping>
> >> +#endif
> >> +                                      ;
> >>
> >>       // A tag type to create internal ctors.
> >>       class __internal_ctor
> >> @@ -726,6 +798,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>          : mapping(__other.extents(), __mdspan::__internal_ctor{})
> >>          { __glibcxx_assert(*this == __other); }
> >>
> >> +#if __glibcxx_padded_layouts
> >> +      template<class _LeftPaddedMapping>
> >> +       requires __mdspan::__is_left_padded_mapping<_LeftPaddedMapping>
> >> +                && is_constructible_v<extents_type,
> >> +                                      typename
> >> _LeftPaddedMapping::extents_type>
> >> +       constexpr
> >> +       explicit(!is_convertible_v<typename
> >> _LeftPaddedMapping::extents_type,
> >> +                                  extents_type>)
> >> +       mapping(const _LeftPaddedMapping& __other) noexcept
> >> +       : mapping(__other.extents(), __mdspan::__internal_ctor{})
> >> +       {
> >> +         constexpr size_t __ostride_sta =
> __mdspan::__get_static_stride<
> >> +           _LeftPaddedMapping>();
> >> +
> >> +         if constexpr (extents_type::rank() > 1
> >> +             && extents_type::static_extent(0) != dynamic_extent
> >> +             && __ostride_sta != dynamic_extent)
> >> +           static_assert(extents_type::static_extent(0) ==
> __ostride_sta);
> >> +
> >> +         __glibcxx_assert(extents_type::rank() <= 1
> >> +           || __other.stride(1) == __other.extents().extent(0));
> >> +       }
> >> +#endif
> >> +
> >>         constexpr mapping&
> >>         operator=(const mapping&) noexcept = default;
> >>
> >> @@ -1173,6 +1269,430 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >>         [[no_unique_address]] _Strides _M_strides;
> >>       };
> >>
> >> +#ifdef __glibcxx_padded_layouts
> >> +  namespace __mdspan
> >> +  {
> >> +    constexpr size_t
> >> +    __least_multiple_at_least(size_t __x, size_t __y)
> >>
> > I would name it just  __least_multiple, the current name makes usage very
> > wordy.
> >
> >> +    {
> >> +      if (__x <= 1)
> >> +       return __y;
> >> +      return (__y / __x + (__y % __x != 0)) * __x ;
> >> +    }
> >> +
> >> +    template<typename _IndexType>
> >>
> > +    constexpr bool
> >> +    __is_representable_least_multiple(size_t __x, size_t __y)
> >> +    {
> >>
> > +      constexpr auto __y_max = numeric_limits<_IndexType>::max();
> >>
> > +      if(std::cmp_greater(__y, __y_max))
> >> +       return false;
> >> +
> >> +      if(__x <= 1)
> >> +       return true;
> >> +
> >> +      auto __max_delta = __y_max - static_cast<_IndexType>(__y);
> >> +      auto __delta = (__y % __x == 0) ? size_t(0) : (__x - (__y %
> __x));
> >>
> > Store  __y % __x in variable, instead of repeating it.
>
> For readability sake? Something like __y_mod_x. Somehow I can't imagine
> it matters for speed. It seems like an obvious common subexpression that
> should be eliminated reliably.
>
> >
> >> +      return std::cmp_less_equal(__delta, __max_delta);
> >> +    }
> >> +
> >> +    template<size_t _PaddingValue, typename _Extents, typename
> _IndexType>
> >> +      concept __valid_static_stride = (_Extents::rank() <= 1)
> >> +       || (_PaddingValue == dynamic_extent)
> >> +       || (_Extents::static_extent(0) == dynamic_extent)
> >> +       || (__is_representable_least_multiple<_IndexType>(
> >> +           _PaddingValue, _Extents::static_extent(0)));
> >> +
> >> +    template<typename _IndexType, size_t _StaticStride1, size_t
> _Extent0,
> >> size_t..._Extents>
> >> +      constexpr auto __make_left_padded_extent(
> >> +       extents<_IndexType, _StaticStride1> __stride1,
> >> +       const extents<_IndexType, _Extent0, _Extents...>& __exts)
> >> +      {
> >> +       auto __impl = [&]<size_t... _Indices>(integer_sequence<size_t,
> >> _Indices...>)
> >> +       {
> >> +         return extents<_IndexType, _StaticStride1, _Extents...>{
> >> +           __stride1.extent(0), __exts.extent(_Indices + 1)...};
> >> +       };
> >> +       return __impl(make_index_sequence<sizeof...(_Extents)>());
> >> +      }
> >> +
> >> +    template<typename _Stride, typename _Extents>
> >> +      constexpr bool
> >> +      __is_representable_left_padded_size(const _Stride& __stride1,
> >> +                                         const _Extents& __exts)
> >> +      {
> >> +       return __is_representable_extents(
> >> +         __make_left_padded_extent(__stride1, __exts));
> >> +      }
> >> +
> >> +    template<size_t _PaddingValue, typename _Extents, typename
> _TargetInt>
> >> +      concept __valid_leftpad_size = (_Extents::rank() <= 1)
> >> +       || (_PaddingValue == dynamic_extent)
> >> +       || (!__all_static(__static_extents<_Extents>()))
> >> +       || (__contains_zero(__static_extents<_Extents>()))
> >> +       || (__static_quotient(__static_extents<_Extents>(1,
> >> _Extents::rank()),
> >> +             numeric_limits<_TargetInt>::max() /
> >> __least_multiple_at_least(
> >> +               _PaddingValue, _Extents::static_extent(0))) != 0);
> >> +
> >> +    template<typename _Extents, typename _Stride, typename... _Indices>
> >> +      constexpr typename _Extents::index_type
> >> +      __linear_index_leftpad(const _Extents& __exts, _Stride __stride,
> >> +                            _Indices... __indices)
> >> +      {
> >> +       // i0 + stride*(i1 + extents.extent(1)*...)
> >> +       using _IndexType = typename _Extents::index_type;
> >> +       _IndexType __res = 0;
> >> +       if constexpr (sizeof...(__indices) > 0)
> >> +         {
> >> +           _IndexType __mult = 1;
> >> +
> >> +           auto __update_rest = [&, __pos = 1u](_IndexType __idx)
> mutable
> >> +             {
> >> +               __res += __idx * __mult;
> >> +               __mult *= __exts.extent(__pos);
> >> +               ++__pos;
> >> +             };
> >> +
> >> +           auto __update = [&](_IndexType __idx, auto... __rest)
> >> +             {
> >> +               __res += __idx;
> >> +               __mult = __stride.extent(0);
> >> +               (__update_rest(__rest), ...);
> >> +             };
> >> +           __update(__indices...);
> >> +         }
> >> +       return __res;
> >> +      }
> >> +  }
> >> +
> >> +  template<size_t _PaddingValue>
> >> +    template<typename _Extents>
> >> +      class layout_left_padded<_PaddingValue>::mapping
> >> +      {
> >> +      public:
> >> +       static constexpr size_t padding_value = _PaddingValue;
> >> +
> >> +       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_padded<padding_value>;
> >> +
> >> +       static_assert(__mdspan::__representable_size<extents_type,
> >> index_type>,
> >> +         "The size of extents_type must be representable as
> index_type");
> >> +
> >> +       static_assert(__mdspan::__valid_static_stride<padding_value,
> >> extents_type,
> >> +                                                     index_type>,
> >> +         "stride(1) must be representable as index_type");
> >> +
> >> +       static_assert(__mdspan::__valid_static_stride<padding_value,
> >> extents_type,
> >> +                                                     size_t>,
> >> +         "stride(1) must be representable as size_t");
> >>
> > After checking this one, we can compute _S_static_stride without
> overflow,
> > so the
> > __mdspan::__valid_static_stride<padding_value, extents_type,  index_type>
> > could be expressed as _S_static_stride <=
> > numeric_limits<index_type>::max(), which would be my preference.
> > This way __is_representable_least_multiple can use size_t.
>
> Okay, understood, I'll change it.
>
> >
> >> +
> >> +       static_assert(__mdspan::__valid_leftpad_size<padding_value,
> >> extents_type, index_type>);
> >> +       static_assert(__mdspan::__valid_leftpad_size<padding_value,
> >> extents_type, size_t>);
> >>
> > This does not seem to be required by standard, only for index_type. Again
> > we could use _S_static_stride.
>
> I think I'm missing something, because to me it reads like it does:
>
> > then the product of LEAST-MULTIPLE-AT-LEAST(...) and all values
> ext.static_extent(k) ...  is representable as a value of type size_t,
> and is representable as a value of type index_type.
>
> https://eel.is/c++draft/views#mdspan.layout.leftpad.overview-5.4
>
> I agree we can't handle cases where index_type is larger than
> size_t; so in that sense, it's redundant. But it's part of the
> standard AFAICT.
>
Ah, indeed. I would again check if size fits in size_t, and then
compare it agains max index type.

>
> >
> >> +
> >> +       // DEVIATION the standard requires that _PaddingValue is
> >> representable
> >> +       // as index_type. This seems unlikely, because if index_type <
> >> size_t
> >> +       // then the mandate is violated for `padding_value ==
> >> dynamic_extent`.
> >> +       // Which means that padding_value == dynamic_extent only
> reliably
> >> work
> >> +       // if index_type == size_t (e.g. it fails for index_type == int
> on
> >> a
> >> +       // 64bit system).
> >> +       static_assert((padding_value == dynamic_extent)
> >> +           || (cmp_less_equal(padding_value,
> >> +                              numeric_limits<index_type>::max())),
> >> +         "padding_value must be representable as index_type");
> >> +
> >> +      private:
> >> +       static constexpr size_t _S_rank = _Extents::rank();
> >> +       static constexpr size_t _S_static_stride = [] consteval
> >> +       {
> >> +         if constexpr (_S_rank <= 1)
> >> +           return size_t(0);
> >>
> >   We should add condition here:
> >                 else if constexpr (_Extents::static_extent(0) == 0)
> >                   return size_t(0);
> > This is exposition only in standard, so I think we can do it.
> >
>
> I can certainly do that, but this is a consteval function. There's
> no runtime overhead; and the more if constexpr branches we have the
> more we suffer from combinational explotion of variations and therefore
> tests.
>
We could use a normal non-constexpr if in this function, except the first
one.

>
>  Sooner or later we will miss one. I'm very reluctant about this
> proposal; but naturally happy to make the change.
>
My goal is not to reduce runtime size, this change makes _S_static_stride
static (not dynamic_extent) even if padding stride was dynamic.
layout_left_padded<dyn>::mapping<extents<int, 0, 1, 2, 3>>
In consequence we will no longer store the value of extents in that case,
reducing the object size.


>
> >> +         else if constexpr (padding_value == dynamic_extent
> >> +             || _Extents::static_extent(0) == dynamic_extent)
> >> +           return dynamic_extent;
> >> +         else
> >> +           return __mdspan::__least_multiple_at_least(
> >> +               padding_value, _Extents::static_extent(0));
> >> +       }();
> >> +
> >> +       consteval friend size_t
> >> +       __mdspan::__get_static_stride<mapping>();
> >> +
> >> +      public:
> >> +       constexpr
> >> +       mapping() noexcept
> >> +       : mapping(extents_type{})
> >> +       { }
> >> +
> >> +       constexpr
> >> +       mapping(const mapping&) noexcept = default;
> >> +
> >> +       constexpr
> >> +       mapping(const extents_type& __exts)
> >> +       : _M_extents(__exts)
> >> +       {
> >> +
> >>   __glibcxx_assert(__mdspan::__is_representable_extents(_M_extents));
> >>
> > Static checks already cover this.
>
> but what if `_M_exents is completely dynamic, e.g.
>    std::extents<uint8_t, dyn, dyn>{128, 4}
>
Ah, indeed, I got confused with the default constructor, please apply
all the comments to default constructor instead, that default initializes
extents.


>
> >
> >> +         if constexpr (_S_rank > 1)
> >>
> > After thinking about it, I think that this function can be implemented
> as:
> >       if constexpr (_S_rank <= 0)
> >         return; // stride is not used
> >       else if constexpr (__exts.static_extent(0) == dynamic_extent)
> >         return; // extent(0) is dynamic so store zero
> >       else if constexpr (_PaddingValue != dynamic_extent)
> >          return; // everything was know statically, and we statically
> > checked the sizes in mandates
> >       else
> >          //  if all extents are static, we already checked the size,
> > otherwise total size is zero
> >         _M_stride = _Stride{__ext.static_extent(0)};
> > And because we don't have any preconditions, I would make this function
> > noexcept.
>
> Sorry, I'm lost. This ctor has three preconditions:
> https://eel.is/c++draft/views#mdspan.layout.leftpad.cons-2

Ignore above, again got confused with default constructor, apply all of
above
to mapping() noexcept constructor, instead of using delegating constructor.

>
>
> >
> >>
> >>
> >> +           {
> >> +             if constexpr (padding_value != dynamic_extent)
> >> +               __glibcxx_assert(
> >> +
> >>   __mdspan::__is_representable_least_multiple<index_type>(
> >> +                     padding_value, _M_extents.extent(0)));
> >> +
> >> +             index_type __stride;
> >> +             if constexpr (padding_value == dynamic_extent)
> >> +               __stride = __exts.extent(0);
> >> +             else if constexpr (__exts.static_extent(0) !=
> dynamic_extent)
> >> +               return;
> >> +             else
> >> +               {
> >> +                 __stride = static_cast<index_type>(
> >> +                   __mdspan::__least_multiple_at_least(padding_value,
> >> +
>  __exts.extent(0)));
> >> +                 __glibcxx_assert(
> >> +                   __mdspan::__is_representable_left_padded_size(
> >> +                     std::dextents<index_type, 1>{__stride},
> >> +                     _M_extents));
> >> +               }
> >> +             _M_stride = _Stride{__stride};
> >> +           }
> >> +       }
> >> +
> >> +       template<__mdspan::__valid_index_type<index_type> _OIndexType>
> >> +         constexpr mapping(const extents_type& __exts, _OIndexType
> __opad)
> >> +         : _M_extents(__exts)
> >> +         {
> >> +           if constexpr (std::is_integral_v<_OIndexType>)
> >> +             {
> >> +               __glibcxx_assert(cmp_less_equal(__opad,
> >> +                   numeric_limits<index_type>::max()));
> >>
> > +               if constexpr (std::is_signed_v<_OIndexType>)
> >> +                 __glibcxx_assert(__opad >= 0);
> >> +             }
> >> +           auto __pad = static_cast<index_type>(std::move(__opad));
> >> +           if constexpr (std::is_signed_v<index_type>)
> >> +             __glibcxx_assert(__pad >= 0);
> >> +           if constexpr (padding_value != dynamic_extent)
> >> +             __glibcxx_assert(cmp_equal(padding_value, __pad));
> >> +           if constexpr (_S_rank > 1 && _S_static_stride ==
> >> dynamic_extent)
> >> +             {
> >> +               __glibcxx_assert(
> >> +
>  __mdspan::__is_representable_least_multiple<index_type>(
> >> +                   __pad, _M_extents.extent(0)));
> >> +
> >> +               _M_stride = _Stride{static_cast<index_type>(
> >> +                 __mdspan::__least_multiple_at_least(
> >> +                   __pad, _M_extents.extent(0)))};
> >> +
> >> +               __glibcxx_assert(
> >> +                 __mdspan::__is_representable_left_padded_size(
> >> +                   _M_stride, _M_extents));
> >> +             }
> >> +         }
> >> +
> >> +       template<typename _OExtents>
> >> +         requires is_constructible_v<extents_type, _OExtents>
> >> +         constexpr explicit(!is_convertible_v<_OExtents, extents_type>)
> >> +         mapping(const layout_left::mapping<_OExtents>& __other)
> >> +         : mapping(extents_type(__other.extents()))
> >> +         {
> >> +           if constexpr (_OExtents::rank() > 1)
> >> +             static_assert(_S_static_stride == dynamic_extent
> >> +                 || _OExtents::static_extent(0) == dynamic_extent
> >> +                 || _S_static_stride == _OExtents::static_extent(0),
> >> +               "(*this).stride(1) must be compatible with
> >> other.stride(1)");
> >> +
> >> +           __glibcxx_assert((!(_S_rank > 1 && padding_value !=
> >> dynamic_extent))
> >> +             || (std::cmp_equal(_M_stride.extent(0),
> >> +                                __other.extents().extent(0))));
> >> +         }
> >> +
> >> +       template<typename _OExtents>
> >> +         requires is_constructible_v<_OExtents, extents_type>
> >> +         constexpr explicit(_OExtents::rank() > 0)
> >> +         mapping(const typename layout_stride::mapping<_OExtents>&
> >> __other)
> >> +         : _M_extents(__other.extents())
> >> +         {
> >> +           if constexpr (_S_rank > 1 && padding_value !=
> dynamic_extent)
> >>
> > +             __glibcxx_assert(cmp_equal(__other.stride(1),
> >> +                 __mdspan::__least_multiple_at_least(padding_value,
> >> +                   __other.extents().extent(0))));
> >> +
> >> +           if constexpr (_S_rank > 0)
> >> +             __glibcxx_assert(__other.stride(0) == 1);
> >> +
> >> +
>  __glibcxx_assert(cmp_less_equal(__other.required_span_size(),
> >> +
> >>   numeric_limits<index_type>::max()));
> >> +
> >> +           if constexpr (_S_rank > 1 && _S_static_stride ==
> >> dynamic_extent)
> >> +             _M_stride = _Stride{__other.stride(1)};
> >> +
> >> +           if constexpr (_S_rank > 2)
> >>
> > Could we just compare *this with others? Or after casting it to
> > layout_stride? This will remove also
> > check for stride(0). Otherwise, I think I would simply drop this
> assertion.
> >
>
> Ofc, that looks like and easy option. I chose not to because I
> feared that it would be rejected because it needlessly compares
> strides 0, and 1.
>
We need to compare stride 0 against 1. The only needless check
would be if we were checking stride(1).


>
> >> +             {
> >> +               _GLIBCXX_DEBUG_ASSERT([&]()
> >> +                 {
> >> +                   for (size_t __i = 2; __i < _S_rank; ++__i)
> >> +                     if (stride(__i) != __other.stride(__i))
> >> +                       return false;
> >> +                   return true;
> >> +                 }());
> >> +             }
> >> +         }
> >> +
> >> +       template<typename _LeftPaddedMapping>
> >> +         requires
> __mdspan::__is_left_padded_mapping<_LeftPaddedMapping>
> >> +             && is_constructible_v<extents_type,
> >> +                                   typename
> >> _LeftPaddedMapping::extents_type>
> >> +         constexpr explicit(_S_rank > 1 && (padding_value !=
> >> dynamic_extent
> >> +               || _LeftPaddedMapping::padding_value == dynamic_extent))
> >> +         mapping(const _LeftPaddedMapping& __other)
> >> +         : _M_extents(__other.extents())
> >> +         {
> >> +           if constexpr (_S_rank > 1)
> >>
> > +           {
> >> +             static_assert(padding_value == dynamic_extent
> >> +                 || _LeftPaddedMapping::padding_value == dynamic_extent
> >> +                 || padding_value == _LeftPaddedMapping::padding_value,
> >> +               "If neither padding_value is dynamic_extent, then they
> >> must "
> >> +               "be equal");
> >> +
> >> +             __glibcxx_assert((padding_value == dynamic_extent
> >> +                 || cmp_equal(__other.stride(1),
> >>
> > +                      __mdspan::__least_multiple_at_least(padding_value,
> >> +                        __other.extents().extent(0))))
> >>
> >   I think this should be:
> >                    if constexpr (_S_static_stride != dynamic_extent)
> >
> >   assert(cmp_equal(__other.stride(1), _S_static_stride));
> >                    else if constexpr (padding_value != dynamic_extent)
> >                       assert(cmp_equal(__other.stride(1),
> >
> >   __mdspan::__least_multiple_at_least(padding_value,
> > _other.extents().extent(0))));
>
> Ah, indeed I forgot to change it.
>
> >
> >
> >> +               && "other.stride(1) must be the next larger multiple of
> "
> >> +                  "padding_value that is greater or equal to
> >> other.extent(0)");
> >> +           }
> >> +
>  __glibcxx_assert(cmp_less_equal(__other.required_span_size(),
> >> +               numeric_limits<index_type>::max()));
> >> +
> >> +           if constexpr (_S_rank > 1 && (_S_static_stride ==
> >> dynamic_extent))
> >> +             _M_stride = _Stride{__other.stride(1)};
> >> +         }
> >> +
> >> +       template<typename _RightPaddedMapping>
> >> +         requires
> >> (__mdspan::__is_right_padded_mapping<_RightPaddedMapping>
> >> +             || __mdspan::__mapping_of<layout_right,
> _RightPaddedMapping>)
> >> +           && (_S_rank <= 1)
> >> +           && is_constructible_v<extents_type,
> >> +                                 typename
> >> _RightPaddedMapping::extents_type>
> >> +         constexpr explicit(!is_convertible_v<
> >> +             typename _RightPaddedMapping::extents_type, extents_type>)
> >> +         mapping(const _RightPaddedMapping& __other) noexcept
> >> +         : _M_extents(__other.extents())
> >> +         {
> >> +
>  __glibcxx_assert(cmp_less_equal(__other.required_span_size(),
> >> +               numeric_limits<index_type>::max()));
> >> +         }
> >> +
> >> +       constexpr mapping&
> >> +       operator=(const mapping&) noexcept = default;
> >> +
> >> +       constexpr const extents_type&
> >> +       extents() const noexcept { return _M_extents; }
> >> +
> >> +       constexpr array<index_type, _S_rank>
> >> +       strides() const noexcept
> >> +       {
> >> +         array<index_type, _S_rank> __ret;
> >> +         if constexpr (_S_rank > 0)
> >> +           __ret[0] = 1;
> >> +         if constexpr (_S_rank > 1)
> >> +           __ret[1] = _M_stride.extent(0);
> >> +         if constexpr (_S_rank > 2)
> >> +           for(size_t __i = 2; __i < _S_rank; ++__i)
> >> +             __ret[__i] = __ret[__i - 1] * _M_extents.extent(__i - 1);
> >> +         return __ret;
> >> +       }
> >> +
> >> +       constexpr index_type
> >> +       required_span_size() const noexcept
> >> +       {
> >> +         if constexpr (_S_rank == 0)
> >> +           return 1;
> >> +         else if (__mdspan::__empty(_M_extents))
> >> +           return 0;
> >> +         else
> >> +           return _M_stride.extent(0) *
> __mdspan::__rev_prod(_M_extents,
> >> 0)
> >>
> > We need to compute this in unsigned tupe, in case if multiplication
> overlow,
> > but later correction puts value in correct range.
> >
>
> Indeed, it unfortunate, because it looked like the mandates were
> developer friendly, e.g.:
>
> https://eel.is/c++draft/views#mdspan.layout.leftpad.overview-5.4
> https://eel.is/c++draft/views#mdspan.layout.leftpad.cons-1.3
> https://eel.is/c++draft/views#mdspan.layout.leftpad.cons-4.4
> https://eel.is/c++draft/views#mdspan.layout.leftpad.cons-8.2
>
> the above all allow us to compute the product safely; but then this
> ctor prevents us from assuming that it's safe to  compute the products:
> https://eel.is/c++draft/views#mdspan.layout.leftpad.cons-11.4
>
> Thank you, nicely spotted.
>
> >> +                  - (_M_stride.extent(0) - _M_extents.extent(0));
> >> +       }
> >> +
> >> +       // _GLIBCXX_RESOLVE_LIB_DEFECTS
> >> +       // 4314. Missing move in mdspan layout mapping::operator()
> >> +       template<__mdspan::__valid_index_type<index_type>... _Indices>
> >> +         requires (sizeof...(_Indices) == _S_rank)
> >> +         constexpr index_type
> >> +         operator()(_Indices... __indices) const noexcept
> >> +         {
> >> +           return __mdspan::__linear_index_leftpad(_M_extents,
> _M_stride,
> >> +             static_cast<index_type>(std::move(__indices))...);
> >> +         }
> >> +
> >> +       static constexpr bool
> >> +       is_always_unique() noexcept { return true; }
> >> +
> >> +       static constexpr bool
> >> +       is_always_exhaustive() noexcept
> >> +       {
> >> +         if constexpr (_S_rank <= 1)
> >> +           return true;
> >> +         else
> >> +           return extents_type::static_extent(0) != dynamic_extent
> >> +                  && _S_static_stride != dynamic_extent
> >> +                  && extents_type::static_extent(0) ==
> _S_static_stride;
> >> +       }
> >> +
> >> +       static constexpr bool
> >> +       is_always_strided() noexcept { return true; }
> >> +
> >> +       static constexpr bool
> >> +       is_unique() noexcept { return true; }
> >> +
> >> +       constexpr bool
> >> +       is_exhaustive() const noexcept
> >> +       {
> >> +         if constexpr (is_always_exhaustive())
> >> +           return true;
> >> +         else
> >> +           return _M_extents.extent(0) == _M_stride.extent(0);
> >> +       }
> >> +
> >> +       static constexpr bool
> >> +       is_strided() noexcept { return true; }
> >> +
> >> +       constexpr index_type
> >> +       stride(rank_type __r) const noexcept
> >> +       {
> >> +         __glibcxx_assert(__r < _S_rank);
> >> +         if (__r == 0)
> >> +           return 1;
> >> +         else
> >> +           return static_cast<index_type>(
> >> +             static_cast<size_t>(_M_stride.extent(0)) *
> >> +             static_cast<size_t>(__mdspan::__fwd_prod(_M_extents, 1,
> >> __r)));
> >> +       }
> >> +
> >> +       template<typename _LeftPaddedMapping>
> >> +
>  requires(__mdspan::__is_left_padded_mapping<_LeftPaddedMapping>
> >> +                  && _LeftPaddedMapping::extents_type::rank() ==
> _S_rank)
> >> +         friend constexpr bool
> >> +         operator==(const mapping& __self, const _LeftPaddedMapping&
> >> __other)
> >> +         noexcept
> >> +         {
> >> +           return __self.extents() == __other.extents()
> >> +             && (_S_rank < 2 || cmp_equal(__self.stride(1),
> >> +                                          __other.stride(1)));
> >> +         }
> >> +
> >> +      private:
> >> +       using _Stride = std::extents<index_type, _S_static_stride>;
> >> +       [[no_unique_address]] _Stride _M_stride;
> >> +       [[no_unique_address]] extents_type _M_extents{};
> >> +      };
> >> +#endif
> >> +
> >>     template<typename _ElementType>
> >>       struct default_accessor
> >>       {
> >> diff --git a/libstdc++-v3/src/c++23/std.cc.in b/libstdc++-v3/src/c++23/
> >> std.cc.in
> >> index a217a87330b..8b2f4b24c9c 100644
> >> --- a/libstdc++-v3/src/c++23/std.cc.in
> >> +++ b/libstdc++-v3/src/c++23/std.cc.in
> >> @@ -1869,9 +1869,11 @@ export namespace std
> >>     using std::aligned_accessor;
> >>   #endif
> >>     using std::mdspan;
> >> -  // FIXME layout_left_padded, layout_right_padded, strided_slice,
> >> -  // submdspan_mapping_result, full_extent_t, full_extent,
> >> submdspan_extents,
> >> -  // mdsubspan
> >> +#if __glibcxx_padded_layouts
> >> +  using std::layout_left_padded;
> >> +#endif
> >> +  // FIXME layout_right_padded, strided_slice,
> submdspan_mapping_result,
> >> +  // full_extent_t, full_extent, submdspan_extents, mdsubspan
> >>   }
> >>   #endif
> >>
> >> diff --git
> >>
> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc
> >>
> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc
> >> index 7091153daba..edf07c983da 100644
> >> ---
> >>
> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc
> >> +++
> >>
> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc
> >> @@ -46,3 +46,4 @@ auto b6 = B<6, std::layout_stride,
> std::layout_left>();
> >>   // { dg-error "require
> >>   auto b7 = B<7, std::layout_stride, std::layout_stride>(); // {
> dg-error
> >> "required from" }
> >>
> >>   // { dg-prune-output "must be representable as index_type" }
> >> +// { dg-prune-output "static assertion failed" }
> >> diff --git
> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
> >> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
> >> index 23c0a55dae1..54f79fcd6ec 100644
> >> --- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
> >> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
> >> @@ -6,6 +6,16 @@
> >>
> >>   constexpr size_t dyn = std::dynamic_extent;
> >>
> >> +template<typename Layout>
> >> +  constexpr bool
> >> +  is_layout_leftpad = false;
> >> +
> >> +#ifdef __glibcxx_padded_layouts
> >> +template<size_t Pad>
> >> +  constexpr bool
> >> +  is_layout_leftpad<std::layout_left_padded<Pad>> = true;
> >> +#endif
> >> +
> >>   template<typename Mapping, typename IndexType, size_t... Extents>
> >>     constexpr void
> >>     verify(std::extents<IndexType, Extents...> oexts)
> >> @@ -27,7 +37,6 @@ template<typename Mapping, typename OMapping>
> >>          VERIFY(std::cmp_equal(m.stride(i), other.stride(i)));
> >>     }
> >>
> >> -
> >>   template<typename To, typename From>
> >>     constexpr void
> >>     verify_convertible(From from)
> >> @@ -40,7 +49,10 @@ template<typename To, typename From>
> >>     constexpr void
> >>     verify_nothrow_convertible(From from)
> >>     {
> >> -    static_assert(std::is_nothrow_constructible_v<To, From>);
> >> +    if constexpr (is_layout_leftpad<typename To::layout_type>)
> >> +      static_assert(std::is_constructible_v<To, From>);
> >> +    else
> >> +      static_assert(std::is_nothrow_constructible_v<To, From>);
> >>       verify_convertible<To>(from);
> >>     }
> >>
> >> @@ -57,7 +69,10 @@ template<typename To, typename From>
> >>     constexpr void
> >>     verify_nothrow_constructible(From from)
> >>     {
> >> -    static_assert(std::is_nothrow_constructible_v<To, From>);
> >> +    if constexpr (is_layout_leftpad<typename To::layout_type>)
> >> +      static_assert(std::is_constructible_v<To, From>);
> >> +    else
> >> +      static_assert(std::is_nothrow_constructible_v<To, From>);
> >>       verify_constructible<To>(from);
> >>     }
> >>
> >> @@ -196,6 +211,16 @@ namespace from_extents
> >>   // ctor: mapping(mapping<OExtents>)
> >>   namespace from_same_layout
> >>   {
> >> +  template<typename Layout, typename Extents, typename OExtents>
> >> +    constexpr void
> >> +    verify_convertible(OExtents exts)
> >> +    {
> >> +      using Mapping = typename Layout::mapping<Extents>;
> >> +      using OMapping = typename Layout::mapping<OExtents>;
> >> +
> >> +      ::verify_convertible<Mapping>(OMapping(exts));
> >> +    }
> >> +
> >>     template<typename Layout, typename Extents, typename OExtents>
> >>       constexpr void
> >>       verify_nothrow_convertible(OExtents exts)
> >> @@ -223,8 +248,12 @@ namespace from_same_layout
> >>         verify_nothrow_convertible<Layout, std::extents<unsigned int>>(
> >>          std::extents<int>{});
> >>
> >> -      verify_nothrow_constructible<Layout, std::extents<int>>(
> >> -       std::extents<unsigned int>{});
> >> +      if constexpr (!is_layout_leftpad<Layout>)
> >> +       verify_nothrow_constructible<Layout, std::extents<int>>(
> >> +         std::extents<unsigned int>{});
> >> +      else
> >> +       verify_convertible<Layout, std::extents<int>>(
> >> +         std::extents<unsigned int>{});
> >>
> >>         assert_not_constructible<
> >>          typename Layout::mapping<std::extents<int>>,
> >> @@ -234,8 +263,12 @@ namespace from_same_layout
> >>          typename Layout::mapping<std::extents<int, 1>>,
> >>          typename Layout::mapping<std::extents<int>>>();
> >>
> >> -      verify_nothrow_constructible<Layout, std::extents<int, 1>>(
> >> -       std::extents<int, dyn>{1});
> >> +      if constexpr (!is_layout_leftpad<Layout>)
> >> +       verify_nothrow_constructible<Layout, std::extents<int, 1>>(
> >> +         std::extents<int, dyn>{1});
> >> +      else
> >> +       verify_convertible<Layout, std::extents<int, 1>>(
> >> +         std::extents<int, dyn>{1});
> >>
> >>         verify_nothrow_convertible<Layout, std::extents<int, dyn>>(
> >>          std::extents<int, 1>{});
> >> @@ -247,8 +280,12 @@ namespace from_same_layout
> >>         verify_nothrow_constructible<Layout, std::extents<int, 1, 2>>(
> >>          std::extents<int, dyn, 2>{1});
> >>
> >> -      verify_nothrow_convertible<Layout, std::extents<int, dyn, 2>>(
> >> -       std::extents<int, 1, 2>{});
> >> +      if constexpr (!is_layout_leftpad<Layout>)
> >> +       verify_nothrow_convertible<Layout, std::extents<int, dyn, 2>>(
> >> +         std::extents<int, 1, 2>{});
> >> +      else
> >> +       verify_nothrow_constructible<Layout, std::extents<int, dyn, 2>>(
> >> +         std::extents<int, 1, 2>{});
> >>         return true;
> >>       }
> >>
> >> @@ -429,6 +466,12 @@ main()
> >>   {
> >>     test_all<std::layout_left>();
> >>     test_all<std::layout_right>();
> >> +#ifdef __glibcxx_padded_layouts
> >> +  test_all<std::layout_left_padded<0>>();
> >> +  test_all<std::layout_left_padded<1>>();
> >> +  test_all<std::layout_left_padded<2>>();
> >> +  test_all<std::layout_left_padded<dyn>>();
> >> +#endif
> >>
> >>     from_left_or_right::test_all<std::layout_left, std::layout_right>();
> >>     from_left_or_right::test_all<std::layout_right, std::layout_left>();
> >> diff --git
> >>
> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/debug/padded_neg.cc
> >>
> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/debug/padded_neg.cc
> >> new file mode 100644
> >> index 00000000000..189becfb92f
> >> --- /dev/null
> >> +++
> >>
> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/debug/padded_neg.cc
> >> @@ -0,0 +1,22 @@
> >> +// { dg-do run { target c++26 xfail *-*-* } }
> >> +// { dg-require-debug-mode "" }
> >> +
> >> +#include <mdspan>
> >> +#include <array>
> >> +
> >> +void
> >> +test_from_strided_mismatching_strides()
> >> +{
> >> +  auto exts = std::extents(3, 5, 7);
> >> +  auto strides = std::array<size_t, 3>{1, 4, 21 /* != 20 */};
> >> +  auto ms = std::layout_stride::mapping(exts, strides);
> >> +  auto m = std::layout_left_padded<2>::mapping<std::dims<3>>(ms);
> >> +  (void) m;
> >> +}
> >> +
> >> +int
> >> +main()
> >> +{
> >> +  test_from_strided_mismatching_strides();
> >> +  return 0;
> >> +};
> >> diff --git
> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc
> >> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc
> >> index 655b9b6d6c3..123e3c61deb 100644
> >> --- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc
> >> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc
> >> @@ -35,7 +35,8 @@ test_static_overflow()
> >>   {
> >>     constexpr Int n1 = std::numeric_limits<Int>::max();
> >>     constexpr size_t n2 = std::dynamic_extent - 1;
> >> -  constexpr size_t n = std::cmp_less(n1, n2) ? size_t(n1) : n2;
> >> +  // Allow some room for padding.
> >> +  constexpr size_t n = (std::cmp_less(n1, n2) ? size_t(n1) : n2) - 4;
> >>
> >>     verify_all(typename Layout::mapping<std::extents<Int, n, n, 0, n,
> >> n>>{});
> >>     verify_all(typename Layout::mapping<std::extents<Int, 0, n, n,
> n>>{});
> >> @@ -73,7 +74,8 @@ test_dynamic_overflow()
> >>   {
> >>     constexpr Int n1 = std::numeric_limits<Int>::max();
> >>     constexpr size_t n2 = std::dynamic_extent - 1;
> >> -  constexpr Int n = std::cmp_less(n1, n2) ? n1 : Int(n2);
> >> +  // Allow some room for padding.
> >> +  constexpr Int n = (std::cmp_less(n1, n2) ? n1 : Int(n2)) - 4;
> >>
> >>     verify_all(make_mapping<Layout>(
> >>         std::extents<Int, dyn, dyn, 0, dyn, dyn>{n, n, n, n}));
> >> @@ -127,5 +129,11 @@ main()
> >>     static_assert(test_all<std::layout_left>());
> >>     static_assert(test_all<std::layout_right>());
> >>     static_assert(test_all<std::layout_stride>());
> >> +#ifdef __glibcxx_padded_layouts
> >> +  static_assert(test_all<std::layout_left_padded<0>>());
> >> +  static_assert(test_all<std::layout_left_padded<1>>());
> >> +  static_assert(test_all<std::layout_left_padded<2>>());
> >> +  static_assert(test_all<std::layout_left_padded<dyn>>());
> >> +#endif
> >>     return 0;
> >>   }
> >> diff --git
> >> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc
> >> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc
> >> index db15e2a48f3..91d66ea87b8 100644
> >> --- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc
> >> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc
> >> @@ -370,6 +370,30 @@ template<>
> >>       }
> >>     };
> >>
> >> +#if __glibcxx_padded_layouts
> >> +template<size_t PaddingValue>
> >> +  struct TestStride2D<std::layout_left_padded<PaddingValue>>
> >> +  {
> >> +    static constexpr void
> >> +    run()
> >> +    {
> >> +      using Layout = std::layout_left_padded<PaddingValue>;
> >> +      typename Layout::mapping<std::extents<int, 3, 5>> m;
> >> +      size_t effective_pad = (PaddingValue == 0 || PaddingValue == dyn)
> >> +       ? size_t(1) : PaddingValue;
> >> +
> >> +      VERIFY(m.stride(0) == 1);
> >> +
> >> +      // The next multiple of PaddingValue, that's greater or equal
> >> +      // to exts.extent(0) is the unique value in the range:
> >> +      //   [exts.extent(0), exts.extent(0) + PaddingValue)
> >> +      // that is divisible by PaddingValue.
> >> +      VERIFY((m.stride(1) % effective_pad) == 0);
> >> +      VERIFY(3 <= m.stride(1) && std::cmp_less(m.stride(1), 3 +
> >> effective_pad));
> >> +    }
> >> +  };
> >> +#endif
> >> +
> >>   template<typename Layout>
> >>     constexpr void
> >>     test_stride_2d()
> >> @@ -423,6 +447,32 @@ template<>
> >>       }
> >>     };
> >>
> >> +#if __glibcxx_padded_layouts
> >> +template<size_t PaddingValue>
> >> +  struct TestStride3D<std::layout_left_padded<PaddingValue>>
> >> +  {
> >> +    static constexpr void
> >> +    run()
> >> +    {
> >> +      using Layout = std::layout_left_padded<PaddingValue>;
> >> +      typename Layout::mapping<std::extents<int, 3, 5, 7>> m;
> >> +      size_t effective_pad = (PaddingValue == 0 || PaddingValue == dyn)
> >> +       ? size_t(1) : PaddingValue;
> >> +
> >> +      VERIFY(m.stride(0) == 1);
> >> +
> >> +      // The next multiple of PaddingValue, that's greater or equal
> >> +      // to exts.extent(0) is the unique value in the range:
> >> +      //   [exts.extent(0), exts.extent(0) + PaddingValue)
> >> +      // that is divisible by PaddingValue.
> >> +      VERIFY((m.stride(1) % effective_pad) == 0);
> >> +      VERIFY(3 <= m.stride(1) && std::cmp_less(m.stride(1), 3 +
> >> effective_pad));
> >> +
> >> +      VERIFY(m.stride(1) * 5 == m.stride(2));
> >> +    }
> >> +  };
> >> +#endif
> >> +
> >>   template<typename Layout>
> >>     constexpr void
> >>     test_stride_3d()
> >> @@ -451,7 +501,8 @@ template<typename Layout>
> >>     test_has_stride_0d()
> >>     {
> >>       using Mapping = typename Layout::mapping<std::extents<int>>;
> >> -    constexpr bool expected = std::is_same_v<Layout,
> std::layout_stride>;
> >> +    constexpr bool expected = !(std::is_same_v<Layout,
> std::layout_left>
> >> +       || std::is_same_v<Layout, std::layout_right>);
> >>       static_assert(has_stride<Mapping> == expected);
> >>     }
> >>
> >> @@ -601,10 +652,29 @@ main()
> >>     test_all<std::layout_left>();
> >>     test_all<std::layout_right>();
> >>     test_all<std::layout_stride>();
> >> +#ifdef __glibcxx_padded_layouts
> >> +  test_all<std::layout_left_padded<0>>();
> >> +  test_all<std::layout_left_padded<1>>();
> >> +  test_all<std::layout_left_padded<2>>();
> >> +  test_all<std::layout_left_padded<5>>();
> >> +  test_all<std::layout_left_padded<dyn>>();
> >> +#endif
> >>
> >>     test_has_op_eq<std::layout_right, std::layout_left, false>();
> >>     test_has_op_eq<std::layout_right, std::layout_stride, true>();
> >>     test_has_op_eq<std::layout_left, std::layout_stride, true>();
> >> +#ifdef __glibcxx_padded_layouts
> >> +  test_has_op_eq<std::layout_left, std::layout_left_padded<0>,
> false>();
> >> +  test_has_op_eq<std::layout_left, std::layout_left_padded<6>,
> false>();
> >> +  test_has_op_eq<std::layout_left, std::layout_left_padded<dyn>,
> false>();
> >> +  // The next one looks strange, because it's neither. Somehow, the
> >> +  // conversion rules seem to be playing a critical role again.
> >> +  // test_has_op_eq<std::layout_right, std::layout_left_padded<0>,
> >> false>();
> >> +
> >> +  test_has_op_eq<std::layout_left_padded<2>,
> std::layout_left_padded<6>,
> >> true>();
> >> +  test_has_op_eq<std::layout_left_padded<2>,
> >> std::layout_left_padded<dyn>, true>();
> >> +#endif
> >> +
> >>     test_has_op_eq_peculiar();
> >>     return 0;
> >>   }
> >> diff --git
> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc
> >> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc
> >> new file mode 100644
> >> index 00000000000..a0cd30c39ff
> >> --- /dev/null
> >> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc
> >> @@ -0,0 +1,611 @@
> >> +// { dg-do run { target c++26 } }
> >> +#include <mdspan>
> >> +
> >> +#include <cstdint>
> >> +#include "../int_like.h"
> >> +#include <testsuite_hooks.h>
> >> +
> >> +#include <iostream>
> >> +
> >> +constexpr size_t dyn = std::dynamic_extent;
> >> +
> >> +constexpr bool
> >> +test_representable_least_multiple()
> >> +{
> >> +  VERIFY(std::__mdspan::__is_representable_least_multiple<uint8_t>(0,
> 0));
> >>
> > I would prefer if we would not use the internals in implementation here,
> > and rely on negative
> > tests for combination of stride and mapping. It was very beneficial for
> us
> > to run libc++ tests
> > on our implementation, and this test is implementation specific if they
> > would like to do the same.
> >
>
> I can remove them again.
>
> >> +  VERIFY(std::__mdspan::__is_representable_least_multiple<uint8_t>(1,
> 0));
> >> +  VERIFY(std::__mdspan::__is_representable_least_multiple<uint8_t>(0,
> >> 255));
> >> +  VERIFY(std::__mdspan::__is_representable_least_multiple<uint8_t>(1,
> >> 255));
> >> +  VERIFY(std::__mdspan::__is_representable_least_multiple<uint8_t>(2,
> 2));
> >> +  VERIFY(std::__mdspan::__is_representable_least_multiple<uint8_t>(5,
> >> 254));
> >> +  VERIFY(std::__mdspan::__is_representable_least_multiple<uint8_t>(5,
> >> 255));
> >> +  VERIFY(!std::__mdspan::__is_representable_least_multiple<uint8_t>(2,
> >> 255));
> >> +
> VERIFY(!std::__mdspan::__is_representable_least_multiple<uint8_t>(128,
> >> 129));
> >> +  return true;
> >> +}
> >> +
> >> +constexpr bool
> >> +test_representable_padded_size()
> >> +{
> >> +  {
> >> +    using E = std::extents<uint8_t, 64, 2>;
> >> +    [[maybe_unused]] std::layout_left_padded<1>::mapping<E> m1;
> >> +  }
> >> +
> >> +  {
> >> +    using E = std::extents<uint8_t, 0, 2>;
> >> +    [[maybe_unused]] std::layout_left_padded<0>::mapping<E> m1;
> >> +    [[maybe_unused]] std::layout_left_padded<1>::mapping<E> m2;
> >> +    [[maybe_unused]] std::layout_left_padded<128>::mapping<E> m3;
> >> +    [[maybe_unused]] std::layout_left_padded<255>::mapping<E> m4;
> >> +  }
> >> +
> >> +  {
> >> +    using E = std::extents<uint8_t, 0, 2>;
> >> +    [[maybe_unused]] std::layout_left_padded<dyn>::mapping<E> m1(E{},
> >> 128);
> >> +    [[maybe_unused]] std::layout_left_padded<dyn>::mapping<E> m2(E{},
> 0);
> >> +    [[maybe_unused]] std::layout_left_padded<dyn>::mapping<E> m3(E{},
> 1);
> >> +    [[maybe_unused]] std::layout_left_padded<dyn>::mapping<E> m4(E{},
> >> 128);
> >> +  }
> >> +
> >> +  {
> >> +    using E = std::extents<uint8_t, dyn, 2>;
> >> +    [[maybe_unused]] std::layout_left_padded<0>::mapping<E> m1;
> >> +    [[maybe_unused]] std::layout_left_padded<1>::mapping<E> m2;
> >> +    [[maybe_unused]] std::layout_left_padded<128>::mapping<E> m3;
> >> +    [[maybe_unused]] std::layout_left_padded<255>::mapping<E> m4;
> >> +  }
> >> +  return true;
> >> +}
> >> +
> >> +constexpr bool
> >> +test_default_ctor()
> >> +{
> >> +  {
> >> +    using E = std::extents<size_t, 3, 5>;
> >> +
> >> +    std::layout_left_padded<2>::mapping<E> msta;
> >> +    VERIFY(msta.stride(0) == 1);
> >> +    VERIFY(msta.stride(1) == 4);
> >> +
> >> +    std::layout_left_padded<dyn>::mapping<E> mdyn;
> >> +    VERIFY(mdyn.stride(0) == 1);
> >> +    VERIFY(mdyn.stride(1) == 3);
> >> +  }
> >> +
> >> +  {
> >> +    using E = std::extents<size_t, dyn, 5>;
> >> +
> >> +    std::layout_left_padded<2>::mapping<E> msta;
> >> +    VERIFY(msta.stride(0) == 1);
> >> +    VERIFY(msta.stride(1) == 0);
> >> +
> >> +    std::layout_left_padded<dyn>::mapping<E> mdyn;
> >> +    VERIFY(mdyn.stride(0) == 1);
> >> +    VERIFY(mdyn.stride(1) == 0);
> >> +  }
> >> +  return true;
> >> +}
> >> +
> >> +constexpr bool
> >> +test_from_exts()
> >> +{
> >> +  auto exts = std::dextents<size_t, 2>{3, 5};
> >> +
> >> +  std::layout_left_padded<0>::mapping m0_sta(exts);
> >> +  VERIFY(m0_sta.stride(1) == exts.extent(0));
> >> +
> >> +  std::layout_left_padded<1>::mapping m1_sta(exts);
> >> +  VERIFY(m1_sta.stride(1) == exts.extent(0));
> >> +
> >> +  std::layout_left_padded<2>::mapping m2_sta(exts);
> >> +  VERIFY(m2_sta.stride(1) == 4);
> >> +
> >> +  std::layout_left_padded<dyn>::mapping mdyn(exts);
> >> +  VERIFY(mdyn.stride(1) == exts.extent(0));
> >> +  return true;
> >> +}
> >> +
> >> +template<size_t PaddingValue, typename CustomPadType>
> >> +constexpr bool
> >> +test_from_pad()
> >> +{
> >> +  using Layout = std::layout_left_padded<PaddingValue>;
> >> +  auto pad = 3;
> >> +  auto exts = std::dextents<size_t, 3>{pad + 1, 5, 7};
> >> +  typename Layout::mapping m(exts, CustomPadType{pad});
> >> +  VERIFY(std::cmp_equal(m.stride(1), 2*pad));
> >> +  VERIFY(m.extents() == exts);
> >> +  return true;
> >> +}
> >> +
> >> +template<size_t PaddingValue>
> >> +constexpr void
> >> +test_from_pad_all()
> >> +{
> >> +  test_from_pad<PaddingValue, int>();
> >> +  static_assert(test_from_pad<PaddingValue, int>());
> >> +
> >> +  test_from_pad<PaddingValue, IntLike>();
> >> +  test_from_pad<PaddingValue, MutatingInt>();
> >> +  test_from_pad<PaddingValue, RValueInt>();
> >> +
> >> +  using Layout = std::layout_left_padded<PaddingValue>;
> >> +  using Extents = std::dims<3>;
> >> +  using Mapping = Layout::template mapping<Extents>;
> >> +  static_assert(!std::is_constructible_v<Mapping, Extents,
> ThrowingInt>);
> >> +  static_assert(!std::is_constructible_v<Mapping, Extents,
> NotIntLike>);
> >> +}
> >> +
> >> +constexpr bool
> >> +is_same_mapping(const auto& lhs, const auto& rhs)
> >> +{
> >> +  if (lhs.extents().rank() != rhs.extents().rank())
> >> +    return false;
> >> +
> >> +  if (lhs.extents() != rhs.extents())
> >> +    return false;
> >> +
> >> +  for (size_t i = 0; i < lhs.extents().rank(); ++i)
> >> +    if (lhs.stride(i) != rhs.stride(i))
> >> +      return false;
> >> +  return true;
> >> +}
> >> +
> >> +enum class ConversionRule
> >> +{
> >> +  Never,
> >> +  Always,
> >> +  Regular
> >> +};
> >> +
> >> +template<typename To, typename From>
> >> +consteval bool
> >> +should_convert(auto rule)
> >> +{
> >> +  if constexpr (rule == ConversionRule::Never)
> >> +    return false;
> >> +  if constexpr (rule == ConversionRule::Always)
> >> +    return true;
> >> +  else
> >> +    return std::is_convertible_v<typename From::extents_type,
> >> +                                typename To::extents_type>;
> >> +}
> >> +
> >> +template<typename To, typename From>
> >> +  constexpr void
> >> +  check_convertible(const From& m, auto conversion_rule)
> >> +  {
> >> +    VERIFY(is_same_mapping(m, To(m)));
> >> +    constexpr bool expected = should_convert<To,
> From>(conversion_rule);
> >> +    static_assert(std::is_convertible_v<From, To> == expected);
> >> +  }
> >> +
> >> +template<typename LayoutTo, typename Esta, typename Edyn, typename
> Ewrong>
> >> +constexpr void
> >> +check_convertible_variants(auto msta, auto conversion_rule)
> >> +{
> >> +  using LayoutFrom = decltype(msta)::layout_type;
> >> +  constexpr auto cregular = std::cw<ConversionRule::Regular>;
> >> +
> >> +  // There's a twist when both mappings are left-padded. There's two
> >> distinct
> >> +  // ctors: a defaulted copy ctor and a constrained template that
> enables
> >> +  // construction from left-padded mappings even if their layout_type
> is
> >> +  // different. The two ctors have different rules regarding
> conversion.
> >> +
> >> +  if constexpr (!std::same_as<LayoutTo, LayoutFrom>)
> >> +    check_convertible<typename LayoutTo::mapping<Esta>>(msta,
> >> conversion_rule);
> >> +  else
> >> +    check_convertible<typename LayoutTo::mapping<Esta>>(msta,
> cregular);
> >> +
> >> +  check_convertible<typename LayoutTo::mapping<Edyn>>(msta,
> >> conversion_rule);
> >> +
> >> +  auto mdyn = typename LayoutFrom::mapping<Edyn>(msta);
> >> +  check_convertible<typename LayoutTo::mapping<Esta>>(mdyn,
> >> conversion_rule);
> >> +  if constexpr (!std::same_as<LayoutTo, LayoutFrom>)
> >> +    check_convertible<typename LayoutTo::mapping<Edyn>>(mdyn,
> >> conversion_rule);
> >> +  else
> >> +    check_convertible<typename LayoutTo::mapping<Edyn>>(mdyn,
> cregular);
> >> +
> >> +  static_assert(!std::is_constructible_v<
> >> +      typename LayoutTo::mapping<Esta>, typename
> >> LayoutFrom::mapping<Ewrong>>);
> >> +};
> >> +
> >> +template<typename PaddedLayout>
> >> +  constexpr void
> >> +  test_from_left_1d()
> >> +  {
> >> +    using E1 = std::extents<int, 6>;
> >> +    using E2 = std::extents<int, dyn>;
> >> +    using E3 = std::extents<int, 5>;
> >> +    constexpr auto cr = std::cw<ConversionRule::Regular>;
> >> +
> >> +    auto msta = std::layout_left::mapping(E1{});
> >> +    check_convertible_variants<PaddedLayout, E1, E2, E3>(msta, cr);
> >> +  }
> >> +
> >> +template<typename PaddedLayout>
> >> +  constexpr void
> >> +  test_from_left_2d()
> >> +  {
> >> +    using E1 = std::extents<int, 6, 5>;
> >> +    using E2 = std::extents<int, dyn, 5>;
> >> +    using E3 = std::extents<int, 6, 6>;
> >> +    constexpr auto cr = std::cw<ConversionRule::Regular>;
> >> +
> >> +    auto msta = std::layout_left::mapping(E1{});
> >> +    check_convertible_variants<PaddedLayout, E1, E2, E3>(msta, cr);
> >> +  }
> >> +
> >> +constexpr bool
> >> +test_from_left()
> >> +{
> >> +  auto check = []<typename PaddedLayout>(PaddedLayout)
> >> +  {
> >> +    test_from_left_1d<PaddedLayout>();
> >> +    test_from_left_2d<PaddedLayout>();
> >> +  };
> >> +
> >> +  check(std::layout_left_padded<0>{});
> >> +  check(std::layout_left_padded<1>{});
> >> +  check(std::layout_left_padded<2>{});
> >> +  check(std::layout_left_padded<6>{});
> >> +  check(std::layout_left_padded<dyn>{});
> >> +
> >> +  // rank == 1 is more permissive:
> >> +  test_from_left_1d<std::layout_left_padded<5>>();
> >> +  return true;
> >> +}
> >> +
> >> +constexpr bool
> >> +test_from_stride()
> >> +{
> >> +  using E1 = std::extents<size_t, 6, 5, 7>;
> >> +  using E2 = std::dims<3>;
> >> +  using E3 = std::extents<size_t, 6, 6, 7>;
> >> +
> >> +  auto check = []<typename PaddedLayout>(PaddedLayout)
> >> +  {
> >> +    auto exts = E1{};
> >> +    auto strides = std::array<int, 3>{
> >> +      1, exts.extent(0), exts.extent(0) * exts.extent(1)};
> >> +    constexpr auto cr = std::cw<ConversionRule::Never>;
> >> +
> >> +    auto m = std::layout_stride::mapping(exts, strides);
> >> +    check_convertible_variants<PaddedLayout, E1, E2, E3>(m, cr);
> >> +  };
> >> +
> >> +  check(std::layout_left_padded<0>{});
> >> +  check(std::layout_left_padded<1>{});
> >> +  check(std::layout_left_padded<2>{});
> >> +  check(std::layout_left_padded<6>{});
> >> +  check(std::layout_left_padded<dyn>{});
> >> +  return true;
> >> +}
> >> +
> >> +constexpr void
> >> +test_from_leftpad_0d()
> >> +{
> >> +  using E1 = std::extents<uint16_t>;
> >> +  using E2 = std::extents<uint8_t>;
> >> +  using E3 = std::extents<uint8_t, 1>;
> >> +
> >> +  std::layout_left_padded<6>::mapping<E1> msta{E1{}};
> >> +
> >> +  auto check = []<typename To>(To, auto m)
> >> +  {
> >> +    constexpr auto cr = std::cw<ConversionRule::Always>;
> >> +    check_convertible_variants<To, E1, E2, E3>(m, cr);
> >> +  };
> >> +
> >> +  check(std::layout_left_padded<6>{}, msta);
> >> +  check(std::layout_left_padded<dyn>{}, msta);
> >> +}
> >> +
> >> +constexpr void
> >> +test_from_leftpad_1d()
> >> +{
> >> +  using E1 = std::extents<int, 6>;
> >> +  using E2 = std::extents<int, dyn>;
> >> +  using E3 = std::extents<int, 6, 6>;
> >> +
> >> +  std::layout_left_padded<6>::mapping<E1> msta{E1{}};
> >> +  std::layout_left_padded<dyn>::mapping<E1> mdyn{E1{}};
> >> +
> >> +  auto check = []<typename To>(To, auto m)
> >> +  {
> >> +    constexpr auto cr = std::cw<ConversionRule::Always>;
> >> +    check_convertible_variants<To, E1, E2, E3>(m, cr);
> >> +  };
> >> +
> >> +  // Remember, for rank <= 1 the padding_value is irrelevant.
> >> +  check(std::layout_left_padded<6>{}, msta);
> >> +  check(std::layout_left_padded<6>{}, mdyn);
> >> +  check(std::layout_left_padded<dyn>{}, msta);
> >> +  check(std::layout_left_padded<dyn>{}, mdyn);
> >> +}
> >> +
> >> +constexpr void
> >> +test_from_leftpad_2d()
> >> +{
> >> +  using E1 = std::extents<int, 6, 5>;
> >> +  using E2 = std::extents<int, dyn, 5>;
> >> +  using E3 = std::extents<int, 6, 6>;
> >> +
> >> +  std::layout_left_padded<6>::mapping<E1> msta{E1{}};
> >> +  std::layout_left_padded<dyn>::mapping<E1> mdyn{E1{}};
> >> +
> >> +  constexpr auto calways = std::cw<ConversionRule::Always>;
> >> +  constexpr auto cnever = std::cw<ConversionRule::Never>;
> >> +
> >> +  auto check = []<typename To>(To, auto m, auto cr)
> >> +  { check_convertible_variants<To, E1, E2, E3>(m, cr); };
> >> +
> >> +  check(std::layout_left_padded<6>{}, msta, cnever);
> >> +  check(std::layout_left_padded<6>{}, mdyn, cnever);
> >> +  check(std::layout_left_padded<dyn>{}, msta, calways);
> >> +  check(std::layout_left_padded<dyn>{}, mdyn, cnever);
> >> +}
> >> +
> >> +constexpr bool
> >> +test_from_leftpad()
> >> +{
> >> +  test_from_leftpad_0d();
> >> +  test_from_leftpad_1d();
> >> +  test_from_leftpad_2d();
> >> +  return true;
> >> +}
> >> +
> >> +constexpr bool
> >> +test_from_right()
> >> +{
> >> +  using E1 = std::extents<size_t, 3>;
> >> +  using E2 = std::dims<1>;
> >> +  using E3 = std::extents<size_t, 5>;
> >> +  constexpr auto cr = std::cw<ConversionRule::Regular>;
> >> +
> >> +  auto msta = std::layout_right::mapping(E1{});
> >> +
> >> +  // Remember, the padding_value has no effect for rank <= 1.
> >> +  check_convertible_variants<std::layout_left_padded<0>, E1, E2,
> >> E3>(msta, cr);
> >> +  check_convertible_variants<std::layout_left_padded<1>, E1, E2,
> >> E3>(msta, cr);
> >> +  check_convertible_variants<std::layout_left_padded<2>, E1, E2,
> >> E3>(msta, cr);
> >> +  check_convertible_variants<std::layout_left_padded<5>, E1, E2,
> >> E3>(msta, cr);
> >> +  check_convertible_variants<std::layout_left_padded<6>, E1, E2,
> >> E3>(msta, cr);
> >> +  check_convertible_variants<std::layout_left_padded<dyn>, E1, E2,
> >> E3>(msta, cr);
> >> +  return true;
> >> +}
> >> +
> >> +constexpr bool
> >> +test_to_left()
> >> +{
> >> +  using E1 = std::extents<int, 6, 5>;
> >> +  using E2 = std::extents<int, dyn, 5>;
> >> +  using E3 = std::extents<int, 6, 6>;
> >> +
> >> +  auto exts_sta = E1{};
> >> +
> >> +  auto check = [](auto msta)
> >> +  {
> >> +    constexpr auto cr = std::cw<ConversionRule::Regular>;
> >> +    check_convertible_variants<std::layout_left, E1, E2, E3>(msta, cr);
> >> +  };
> >> +
> >> +  check(std::layout_left_padded<0>::mapping(exts_sta));
> >> +  check(std::layout_left_padded<2>::mapping(exts_sta));
> >> +  check(std::layout_left_padded<6>::mapping(exts_sta));
> >> +  check(std::layout_left_padded<dyn>::mapping(exts_sta, 0));
> >> +  check(std::layout_left_padded<dyn>::mapping(exts_sta, 2));
> >> +  check(std::layout_left_padded<dyn>::mapping(exts_sta, 6));
> >> +  return true;
> >> +}
> >> +
> >> +constexpr bool
> >> +test_never_to_right()
> >> +{
> >> +  using E1 = std::extents<size_t, 3>;
> >> +  using E2 = std::dims<1>;
> >> +
> >> +  auto mr_sta = std::layout_right::mapping(E1{});
> >> +  auto mlp_sta = std::layout_left_padded<2>::mapping<E1>{mr_sta};
> >> +  static_assert(!std::is_constructible_v<decltype(mr_sta),
> >> decltype(mlp_sta)>);
> >> +
> >> +  auto mr_dyn = std::layout_right::mapping(E2{E1{}});
> >> +  auto mlp_dyn = std::layout_left_padded<2>::mapping<E2>{mr_dyn};
> >> +  static_assert(!std::is_constructible_v<decltype(mr_dyn),
> >> decltype(mlp_dyn)>);
> >> +  return true;
> >> +}
> >> +
> >> +template<typename Layout>
> >> +constexpr void
> >> +test_strides()
> >> +{
> >> +  auto check = [](auto exts)
> >> +  {
> >> +    auto m = typename Layout::mapping(exts);
> >> +    using IndexType = typename decltype(m)::index_type;
> >> +    constexpr size_t rank = decltype(m)::extents_type::rank();
> >> +
> >> +    auto strides = m.strides();
> >> +    static_assert(std::same_as<decltype(strides), std::array<IndexType,
> >> rank>>);
> >> +    VERIFY(strides.size() == rank);
> >> +    for (size_t i = 0; i < strides.size(); ++i)
> >> +      VERIFY(strides[i] == m.stride(i));
> >> +  };
> >> +
> >> +  check(std::extents());
> >> +  check(std::extents(0));
> >> +  check(std::extents(3));
> >> +  check(std::extents(3, 5, 7));
> >> +  check(std::extents(3, 0, 7));
> >> +}
> >> +
> >> +constexpr bool
> >> +test_strides_all()
> >> +{
> >> +  test_strides<std::layout_left_padded<0>>();
> >> +  test_strides<std::layout_left_padded<1>>();
> >> +  test_strides<std::layout_left_padded<3>>();
> >> +  test_strides<std::layout_left_padded<dyn>>();
> >> +  return true;
> >> +}
> >> +
> >> +constexpr void
> >> +test_exhaustive_0d()
> >> +{
> >> +  auto exts = std::extents<int>{};
> >> +
> >> +  auto check = [](auto m)
> >> +  {
> >> +    static_assert(m.is_always_exhaustive());
> >> +    VERIFY(m.is_exhaustive());
> >> +  };
> >> +
> >> +  check(std::layout_left_padded<0>::mapping(exts));
> >> +  check(std::layout_left_padded<1>::mapping(exts));
> >> +  check(std::layout_left_padded<2>::mapping(exts));
> >> +  check(std::layout_left_padded<dyn>::mapping(exts));
> >> +}
> >> +
> >> +constexpr void
> >> +test_exhaustive_1d()
> >> +{
> >> +  auto check_dyn_and_sta = []<typename Layout>(Layout)
> >> +  {
> >> +    auto check = [](auto exts)
> >> +    {
> >> +      auto m = typename Layout::mapping(exts);
> >> +      static_assert(m.is_always_exhaustive());
> >> +      VERIFY(m.is_exhaustive());
> >> +    };
> >> +
> >> +    check(std::extents(4));
> >> +    check(std::extents<int, 4>{});
> >> +  };
> >> +
> >> +  check_dyn_and_sta(std::layout_left_padded<1>{});
> >> +  check_dyn_and_sta(std::layout_left_padded<2>{});
> >> +  check_dyn_and_sta(std::layout_left_padded<6>{});
> >> +  check_dyn_and_sta(std::layout_left_padded<dyn>{});
> >> +}
> >> +
> >> +constexpr void
> >> +test_exhaustive_3d()
> >> +{
> >> +  auto exts_dyn = std::extents(4, 5, 7);
> >> +  auto exts_sta = std::extents<int, 4, 5, 7>{};
> >> +  auto ctrue = std::cw<true>;
> >> +  auto cfalse= std::cw<false>;
> >> +
> >> +  auto check = [](auto m, auto static_expected, auto runtime_expected)
> >> +  {
> >> +    static_assert(m.is_always_exhaustive() == static_expected);
> >> +    VERIFY(m.is_exhaustive() == runtime_expected);
> >> +  };
> >> +
> >> +  check(std::layout_left_padded<0>::mapping(exts_sta), ctrue, true);
> >> +  check(std::layout_left_padded<0>::mapping(exts_dyn), cfalse, true);
> >> +  check(std::layout_left_padded<1>::mapping(exts_sta), ctrue, true);
> >> +  check(std::layout_left_padded<1>::mapping(exts_dyn), cfalse, true);
> >> +  check(std::layout_left_padded<2>::mapping(exts_sta), ctrue, true);
> >> +  check(std::layout_left_padded<2>::mapping(exts_dyn), cfalse, true);
> >> +  check(std::layout_left_padded<6>::mapping(exts_dyn), cfalse, false);
> >> +  check(std::layout_left_padded<6>::mapping(exts_sta), cfalse, false);
> >> +  check(std::layout_left_padded<dyn>::mapping(exts_sta), cfalse, true);
> >> +  check(std::layout_left_padded<dyn>::mapping(exts_dyn, 2), cfalse,
> true);
> >> +  check(std::layout_left_padded<dyn>::mapping(exts_dyn, 3), cfalse,
> >> false);
> >> +}
> >> +
> >> +constexpr bool
> >> +test_exhaustive()
> >> +{
> >> +  test_exhaustive_0d();
> >> +  test_exhaustive_1d();
> >> +  test_exhaustive_3d();
> >> +  return true;
> >> +}
> >> +
> >> +constexpr bool
> >> +test_op_eq()
> >> +{
> >> +  // The generic cases are handled in layouts/mapping.cc. Here we check
> >> special
> >> +  // cases related to non exhaustive layouts.
> >> +  using E1 = std::extents<size_t, 6, 5, 7>;
> >> +  using E2 = std::dims<3>;
> >> +  using E3 = std::extents<size_t, 7, 5, 7>;
> >> +
> >> +  auto m1 = std::layout_left_padded<0>::mapping(E1{});
> >> +  auto m2 = std::layout_left_padded<7>::mapping(E1{});
> >> +
> >> +  VERIFY(m1 == std::layout_left_padded<0>::mapping(E1{}));
> >> +  VERIFY(m1 == std::layout_left_padded<1>::mapping(E1{}));
> >> +  VERIFY(m1 == std::layout_left_padded<2>::mapping(E1{}));
> >> +  VERIFY(m1 == std::layout_left_padded<6>::mapping(E1{}));
> >> +  VERIFY(m1 != std::layout_left_padded<7>::mapping(E1{}));
> >> +  VERIFY(m1 == std::layout_left_padded<dyn>::mapping(E1{}));
> >> +  VERIFY(m1 != std::layout_left_padded<dyn>::mapping(E1{}, 7));
> >> +
> >> +  VERIFY(m1 == std::layout_left_padded<0>::mapping(E2{E1{}}));
> >> +  VERIFY(m1 == std::layout_left_padded<1>::mapping(E2{E1{}}));
> >> +  VERIFY(m1 == std::layout_left_padded<2>::mapping(E2{E1{}}));
> >> +  VERIFY(m1 == std::layout_left_padded<6>::mapping(E2{E1{}}));
> >> +  VERIFY(m1 != std::layout_left_padded<7>::mapping(E2{E1{}}));
> >> +  VERIFY(m1 == std::layout_left_padded<dyn>::mapping(E2{E1{}}));
> >> +  VERIFY(m1 != std::layout_left_padded<dyn>::mapping(E2{E1{}}, 7));
> >> +
> >> +  VERIFY(m2 == std::layout_left_padded<7>::mapping(E1{}));
> >> +  VERIFY(m2 == std::layout_left_padded<dyn>::mapping(E1{}, 7));
> >> +  VERIFY(m2 == std::layout_left_padded<7>::mapping(E2{E1{}}));
> >> +  VERIFY(m2 == std::layout_left_padded<dyn>::mapping(E2{E1{}}, 7));
> >> +
> >> +  VERIFY(m2 != std::layout_left_padded<7>::mapping(E3{}));
> >> +  VERIFY(m2 != std::layout_left_padded<dyn>::mapping(E3{}, 7));
> >> +  return true;
> >> +}
> >> +
> >> +int
> >> +main()
> >> +{
> >> +  test_representable_least_multiple();
> >> +  static_assert(test_representable_least_multiple());
> >> +
> >> +  test_representable_padded_size();
> >> +  static_assert(test_representable_padded_size());
> >> +
> >> +  test_default_ctor();
> >> +  static_assert(test_default_ctor());
> >> +
> >> +  test_from_exts();
> >> +  static_assert(test_from_exts());
> >> +
> >> +  test_from_left();
> >> +  static_assert(test_from_left());
> >> +
> >> +  test_from_pad_all<dyn>();
> >> +  test_from_pad_all<3>();
> >> +
> >> +  test_from_stride();
> >> +  static_assert(test_from_stride());
> >> +
> >> +  test_from_leftpad();
> >> +  static_assert(test_from_leftpad());
> >> +
> >> +  test_from_right();
> >> +  static_assert(test_from_right());
> >> +
> >> +  test_to_left();
> >> +  static_assert(test_to_left());
> >> +
> >> +  test_never_to_right();
> >> +  static_assert(test_never_to_right());
> >> +
> >> +  test_strides_all();
> >> +  static_assert(test_strides_all());
> >> +
> >> +  test_exhaustive();
> >> +  static_assert(test_exhaustive());
> >> +
> >> +  test_op_eq();
> >> +  static_assert(test_op_eq());
> >> +  return 0;
> >> +}
> >> diff --git
> >> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_neg.cc
> >> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_neg.cc
> >> new file mode 100644
> >> index 00000000000..72ef55ea87b
> >> --- /dev/null
> >> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_neg.cc
> >> @@ -0,0 +1,280 @@
> >> +// { dg-do compile { target c++26 } }
> >> +#include <mdspan>
> >> +
> >> +#include <cstdint>
> >> +
> >> +constexpr size_t dyn = std::dynamic_extent;
> >> +
> >> +constexpr bool
> >> +test_from_extens_representable_sta()
> >> +{
> >> +  using E1 = std::extents<uint8_t, 8, 128>;
> >> +  auto m = std::layout_left_padded<dyn>::mapping(E1{}); // { dg-error
> >> "from here" }
> >> +  return true;
> >> +}
> >> +static_assert(test_from_extens_representable_sta());
> >> +
> >> +constexpr bool
> >> +test_from_extents_representable_padded_size()
> >> +{
> >> +  using E1 = std::extents<uint8_t, 8, 128>;
> >> +  using E2 = std::dextents<uint8_t, 2>;
> >> +
> >> +  auto m = std::layout_left_padded<dyn>::mapping(E2{E1{}}); // {
> dg-error
> >> "expansion of" }
> >> +  return true;
> >> +}
> >> +static_assert(test_from_extents_representable_padded_size());
> >> +
> >> +constexpr bool
> >> +test_from_extents_representable_stride()
> >> +{
> >> +  using E1 = std::extents<uint8_t, dyn, 1>;
> >> +  auto m = std::layout_left_padded<128>::mapping(E1{129}); // {
> dg-error
> >> "expansion of" }
> >>
> > +  return true;
> >> +}
> >> +static_assert(test_from_extents_representable_stride());
> >> +
> >> +constexpr bool
> >> +test_from_pad_representable_stride()
> >> +{
> >> +  using E1 = std::dextents<uint8_t, 2>;
> >> +  auto m = std::layout_left_padded<dyn>::mapping(E1{129, 2}, 128); // {
> >> dg-error "expansion of" }
> >> +  (void) m;
> >> +  return true;
> >> +}
> >> +static_assert(test_from_pad_representable_stride());
> >> +
> >> +constexpr bool
> >> +test_from_pad_representable_padded_size()
> >> +{
> >> +  using E1 = std::dextents<uint8_t, 2>;
> >> +  auto m = std::layout_left_padded<dyn>::mapping(E1{64, 2}, 128); // {
> >> dg-error "expansion of" }
> >> +  (void) m;
> >> +  return true;
> >> +}
> >> +static_assert(test_from_pad_representable_padded_size());
> >> +
> >> +constexpr bool
> >> +test_from_left()
> >> +{
> >> +  auto exts = std::extents<uint8_t, 6, dyn>{4};
> >> +  auto ml = std::layout_left::mapping(exts);
> >> +
> >> +  using Layout = std::layout_left_padded<4>;
> >> +  Layout::mapping<decltype(exts)> m(ml); // { dg-error "from here" }
> >> +  return true;
> >> +}
> >> +static_assert(test_from_left());
> >> +
> >> +constexpr bool
> >> +test_from_left_bad_runtime_stride()
> >> +{
> >> +  auto exts = std::extents<uint8_t, dyn, dyn>{6, 4};
> >> +  auto ml = std::layout_left::mapping(exts);
> >> +
> >> +  using Layout = std::layout_left_padded<4>;
> >> +  Layout::mapping<decltype(exts)> m(ml); // { dg-error "expansion of" }
> >> +  return true;
> >> +}
> >> +static_assert(test_from_left_bad_runtime_stride());
> >> +
> >> +constexpr bool
> >> +test_from_left_representable_extents()
> >> +{
> >> +  auto exts = std::extents<uint16_t, dyn, dyn>{8, 128};
> >> +  auto ml = std::layout_left::mapping(exts);
> >> +
> >> +  using Layout = std::layout_left_padded<8>;
> >> +  Layout::mapping<std::extents<uint8_t, dyn, dyn>> m(ml); // { dg-error
> >> "expansion of" }
> >> +  return true;
> >> +}
> >> +static_assert(test_from_left_representable_extents());
> >> +
> >> +template<size_t PaddingValue>
> >> +  constexpr bool
> >> +  test_pad_overflow()
> >> +  {
> >> +    auto exts = std::extents<uint8_t, dyn>{4};
> >> +    auto n = size_t(1) << 9;
> >> +    auto m = typename
> >> std::layout_left_padded<PaddingValue>::mapping(exts, n);
> >> +    (void) m;
> >> +    return true;
> >> +  }
> >> +static_assert(test_pad_overflow<1>());   // { dg-error "expansion of" }
> >> +static_assert(test_pad_overflow<dyn>()); // { dg-error "expansion of" }
> >> +
> >> +template<size_t PaddingValue>
> >> +  constexpr bool
> >> +  test_from_pad_negative()
> >> +  {
> >> +    auto exts = std::extents(4);
> >> +    auto m = typename
> >> std::layout_left_padded<PaddingValue>::mapping(exts, -1);
> >> +    (void) m;
> >> +    return true;
> >> +  }
> >> +static_assert(test_from_pad_negative<1>());   // { dg-error "expansion
> >> of" }
> >> +static_assert(test_from_pad_negative<dyn>()); // { dg-error "expansion
> >> of" }
> >> +
> >> +template<size_t Pad>
> >> +  constexpr bool
> >> +  test_static_pad_same()
> >> +  {
> >> +    using Extents = std::extents<int, dyn>;
> >> +    using Mapping = typename
> >> std::layout_left_padded<Pad>::mapping<Extents>;
> >> +    auto exts = Extents{4};
> >> +    auto m = Mapping(exts, Pad + 1);      // { dg-error "expansion of"
> }
> >> +    (void) m;
> >> +    return true;
> >> +  }
> >> +static_assert(test_static_pad_same<1>()); // { dg-error "expansion of"
> }
> >> +static_assert(test_static_pad_same<3>()); // { dg-error "expansion of"
> }
> >> +
> >> +constexpr bool
> >> +test_from_stride_wrong_stride0()
> >> +{
> >> +  using Extents = std::dextents<size_t, 2>;
> >> +  auto e = Extents{3, 5};
> >> +  auto ms = std::layout_stride::mapping(e, std::array<int, 2>{2, 7});
> >> +  auto m = std::layout_left_padded<dyn>::mapping<Extents>(ms); // {
> >> dg-error "expansion of" }
> >> +  (void) m;
> >> +  return true;
> >> +}
> >> +static_assert(test_from_stride_wrong_stride0());
> >> +
> >> +constexpr bool
> >> +test_from_stride_wrong_stride1()
> >> +{
> >> +  using Extents = std::dextents<size_t, 2>;
> >> +  auto e = Extents{3, 5};
> >> +  auto ms = std::layout_stride::mapping(e, std::array<int, 2>{1, 3});
> >> +  auto m = std::layout_left_padded<2>::mapping<Extents>(ms); // {
> >> dg-error "expansion of" }
> >> +  (void) m;
> >> +  return true;
> >> +}
> >> +static_assert(test_from_stride_wrong_stride1());
> >> +
> >> +constexpr bool
> >> +test_from_stride_wrong_stride2()
> >> +{
> >> +  using Extents = std::dims<3>;
> >> +  auto e = Extents{3, 5, 7};
> >> +  auto ms = std::layout_stride::mapping(e, std::array<int, 3>{1, 4,
> 21});
> >> +  auto m = std::layout_left_padded<dyn>::mapping<Extents>(ms); // here
> >> (not implemented)
> >> +  (void) m;
> >> +  return true;
> >> +}
> >> +static_assert(test_from_stride_wrong_stride2());
> >> +
> >> +constexpr bool
> >> +test_from_stride_oversized()
> >> +{
> >> +  auto exts = std::extents<uint16_t, dyn, dyn>{2, 6};
> >> +  auto ms = std::layout_stride::mapping(exts, std::array<uint16_t,
> 2>{1,
> >> 128});
> >> +
> >> +  using Layout = std::layout_left_padded<dyn>;
> >> +  Layout::mapping<std::extents<uint8_t, dyn, dyn>> m(ms); // { dg-error
> >> "expansion of" }
> >> +  (void) m;
> >> +  return true;
> >> +}
> >> +static_assert(test_from_stride_oversized());
> >> +
> >> +constexpr bool
> >> +test_from_leftpad_dyn()
> >> +{
> >> +  using Extents = std::dextents<size_t, 2>;
> >> +  auto e = Extents{3, 5};
> >> +  auto mlp = std::layout_left_padded<dyn>::mapping(e);
> >> +  auto m = std::layout_left_padded<2>::mapping<Extents>(mlp); // {
> >> dg-error "expansion of" }
> >> +  (void) m;
> >> +  return true;
> >> +}
> >> +static_assert(test_from_leftpad_dyn());
> >> +
> >> +constexpr bool
> >> +test_from_leftpad_sta()
> >> +{
> >> +  using Extents = std::dextents<size_t, 2>;
> >> +  auto e = Extents{3, 5};
> >> +  auto mlp = std::layout_left_padded<3>::mapping(e);
> >> +  auto m = std::layout_left_padded<2>::mapping<Extents>(mlp); // {
> >> dg-error "required from" }
> >> +  (void) m;
> >> +  return true;
> >> +}
> >> +static_assert(test_from_leftpad_sta());
> >> +
> >> +constexpr bool
> >> +test_from_leftpad_oversized()
> >> +{
> >> +  using E1 = std::extents<uint16_t, 8, 128>;
> >> +  using E2 = std::extents<uint8_t, dyn, dyn>;
> >> +  auto mlp = std::layout_left_padded<dyn>::mapping<E1>(E1{});
> >> +  auto m = std::layout_left_padded<dyn>::mapping<E2>(mlp); // {
> dg-error
> >> "expansion of" }
> >> +  (void) m;
> >> +  return true;
> >> +}
> >> +static_assert(test_from_leftpad_oversized());
> >> +
> >> +template<size_t RunId>
> >> +  constexpr bool
> >> +  test_to_left_not_exhaustive()
> >> +  {
> >> +    using E1 = std::extents<int, 6, 5>;
> >> +    using E2 = std::extents<int, dyn, 5>;
> >> +
> >> +    [[maybe_unused]] auto msta =
> >> std::layout_left_padded<7>::mapping(E1{});
> >> +    if constexpr (RunId == 0)
> >> +      {
> >> +       auto m = std::layout_left::mapping<E1>(msta); // { dg-error
> >> "required from" }
> >> +       (void) m;
> >> +      }
> >> +    if constexpr (RunId == 1)
> >> +      {
> >> +       auto m = std::layout_left::mapping<E2>(msta); // { dg-error
> >> "expansion of" }
> >> +       (void) m;
> >> +      }
> >> +
> >> +    [[maybe_unused]] auto mdyn =
> >> std::layout_left_padded<dyn>::mapping(E2{E1{}}, 7);
> >> +    if constexpr (RunId == 2)
> >> +      {
> >> +       auto m = std::layout_left::mapping<E1>(mdyn); // { dg-error
> >> "expansion of" }
> >> +       (void) m;
> >> +      }
> >> +    if constexpr (RunId == 3)
> >> +      {
> >> +       auto m = std::layout_left::mapping<E2>(mdyn); // { dg-error
> >> "expansion of" }
> >> +       (void) m;
> >> +      }
> >> +    return true;
> >> +  }
> >> +static_assert(test_to_left_not_exhaustive<0>());
> >> +static_assert(test_to_left_not_exhaustive<1>());
> >> +static_assert(test_to_left_not_exhaustive<2>());
> >> +static_assert(test_to_left_not_exhaustive<3>());
> >> +
> >> +constexpr bool
> >> +test_statically_bad_padding_value()
> >> +{
> >> +  using Extents = std::extents<uint8_t, 129, 2>;
> >> +  std::layout_left_padded<2>::mapping<Extents> m; // { dg-error
> "required
> >> from here" }
> >> +  return true;
> >> +}
> >> +static_assert(test_statically_bad_padding_value());
> >> +
> >> +constexpr bool
> >> +test_statically_oversized()
> >> +{
> >> +  using Extents = std::extents<uint8_t, 64, 2>;
> >> +  std::layout_left_padded<128>::mapping<Extents> m; // { dg-error
> >> "required from here" }
> >> +  return true;
> >> +}
> >> +static_assert(test_statically_oversized());
> >> +
> >> +// { dg-prune-output "padding_value must be representable as
> index_type" }
> >> +// { dg-prune-output "non-constant condition for static assertion" }
> >> +// { dg-prune-output "called in a constant expression" }
> >> +// { dg-prune-output "no matching function" }
> >> +// { dg-prune-output "static assertion failed" }
> >> +// { dg-prune-output "__glibcxx_assert_fail()" }
> >> +// { dg-prune-output "must be compatible with other.stride" }
> >> +// { dg-prune-output "padding_value is dynamic_extent" }
> >> +// { dg-prune-output "_S_rank <= 1" }
> >> --
> >> 2.50.1
> >>
> >>
> >
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20250911/8a544dbd/attachment-0001.htm>


More information about the Libstdc++ mailing list