Okay, I I'm through, two or three question further down. Nice idea
with the tag type! I'm happy the constraints could be removed safely.

(I'll hold off with sending v4.)

Thank you,
Luc

On 9/25/25 1:06 PM, Tomasz Kaminski wrote:
On Tue, Sep 23, 2025 at 3:23 PM Luc Grosheintz <[email protected]>
wrote:

This commit adds a new layout layout_left_padded as standardized in
N5014 but with one deviation.

I would remove "but with one deviation" here, you are mentioning LWG issue
below anyway.

It adds a purely internal feature testing
macro padded_layouts and registers layout_left_padded in the std module.

This commit implements LWG4372, because without it's not possible
to properly test padded layouts with a dynamic padding value. It also
implements LWG4314, for consistency with prior layouts.

The implementation uses a _PaddedStorage to deduplicate most of the code
shared between left- and right-padded layouts. It's implemented through
aggregation rather than inheritence, because of a bug related to
inheriting conditionally explicit ctors.

The tests are written such that the canonical version is works for
layout_left_padded. A version for layout_right_padded is derived
essentially by reversing the order of the extents.

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.
         (layout_left::mapping::mapping): New overload for left
         padded mappings.
         (__is_left_padded_mapping): New concept.
         (__is_right_padded_mapping): Ditto.
         (__standardized_mapping): Recognize left and right padded
         mappings.
         (__valid_static_stride): Prepare for right padded layouts.
         (__valid_padded_size): Ditto.
         (_LeftPaddedIndices): Traits for left padded details.
         (_PaddedStorage): New class.
         * src/c++23/std.cc.in (layout_left_padded): Add.
         * testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc:
         Refactor and 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/padded.cc: Ditto.
         * testsuite/23_containers/mdspan/layouts/padded_neg.cc: Ditto.
         * testsuite/23_containers/mdspan/layouts/padded_traits.h: New
         traits.

Signed-off-by: Luc Grosheintz <[email protected]>
---

The implementation looks really good now. The _PaddedStorage idea looks
like a really nice idea, and I hope we can push it a bit further in future.
The test cases are extensive and took time to get through, but I think you
checked
everything that I could think of.
(I have noticed that we do not assert for later strided when constructing
from layout
stride, but I think this is OK for the moment).


Few suggestions:
  * _PaddedStorage constructor does not need the constraints or explicit
checks.
   make all of them except default explicit, and remove constrains.
* Instead of checking is_same_padded_layout, just pass layout type as first
   parameter (typeanme _LayoutTraits::_LayoutSame), using is as tag.
* few suggestions for rename -> remove dynamic, padded_X to pad X


  libstdc++-v3/include/bits/version.def         |  10 +
  libstdc++-v3/include/bits/version.h           |   9 +
  libstdc++-v3/include/std/mdspan               | 665 ++++++++++++++++-
  libstdc++-v3/src/c++23/std.cc.in              |   8 +-
  .../mdspan/layouts/class_mandate_neg.cc       |   1 +
  .../23_containers/mdspan/layouts/ctors.cc     |  69 +-
  .../23_containers/mdspan/layouts/empty.cc     |  20 +-
  .../23_containers/mdspan/layouts/mapping.cc   | 108 ++-
  .../23_containers/mdspan/layouts/padded.cc    | 675 ++++++++++++++++++
  .../mdspan/layouts/padded_neg.cc              | 324 +++++++++
  .../mdspan/layouts/padded_traits.h            |  63 ++
  11 files changed, 1936 insertions(+), 16 deletions(-)
  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
  create mode 100644
libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_traits.h

diff --git a/libstdc++-v3/include/bits/version.def
b/libstdc++-v3/include/bits/version.def
index 721125df280..b92b71fc624 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -1062,6 +1062,16 @@ ftms = {
      cxxmin = 26;
      extra_cond = "__glibcxx_assume_aligned "
      "&& __glibcxx_is_sufficiently_aligned";
+    };
+};
+
+// Unofficial macro signaling presence of the two padded layouts.

Change it to
//  Internal macro for padded layouts

+ftms = {
+  name = padded_layouts;
+  no_stdname = true; // Don't change: it's not a standardized FTM.

I would not make an instruction like that here. just write // internal

+  values = {
+    v = 1;

+    cxxmin = 26;
    };
  };

diff --git a/libstdc++-v3/include/bits/version.h
b/libstdc++-v3/include/bits/version.h
index 7e8ad1007ef..5e10a893426 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -1193,6 +1193,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..18782bf0a06 100644
--- a/libstdc++-v3/include/std/mdspan
+++ b/libstdc++-v3/include/std/mdspan
@@ -261,6 +261,14 @@ _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
+      {
+       const auto& __sta_exts = __static_extents<_Extents>();
+       return span<const size_t>(__sta_exts.data() + __begin, __end -
__begin);
+      }
+
      // Pre-compute: \prod_{i = 0}^r _Extents[i], for r = 0,..., n
(exclusive)
      template<array _Extents>
        constexpr auto __fwd_partial_prods = [] consteval
@@ -476,6 +484,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>(__begin, __end);
+         size_t __ret = 1;
+         for(auto __ext : __sta_exts)
+           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 +590,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 +708,34 @@ _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 __padded_mapping_of = __mapping_of<
+       _Layout<_Mapping::padding_value>, _Mapping>;
+
+#ifdef __glibcxx_padded_layouts
+    template<typename _Mapping>
+      constexpr bool __is_left_padded_mapping = __padded_mapping_of<
+       layout_left_padded, _Mapping>;
+
+    template<typename _Mapping>
+      constexpr bool __is_right_padded_mapping = __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 +789,30 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
         : mapping(__other.extents(), __mdspan::__internal_ctor{})
         { __glibcxx_assert(*this == __other); }

+#if __glibcxx_padded_layouts
+      template<class _LeftpadMapping>
+       requires __mdspan::__is_left_padded_mapping<_LeftpadMapping>
+                && is_constructible_v<extents_type,
+                                      typename
_LeftpadMapping::extents_type>
+       constexpr
+       explicit(!is_convertible_v<typename _LeftpadMapping::extents_type,
+                                  extents_type>)
+       mapping(const _LeftpadMapping& __other) noexcept
+       : mapping(__other.extents(), __mdspan::__internal_ctor{})
+       {
+         constexpr size_t __ostride_sta = __mdspan::__get_static_stride<
+           _LeftpadMapping>();
+
+         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);
+
+         if constexpr (extents_type::rank() > 1)
+           __glibcxx_assert(__other.stride(1) ==
__other.extents().extent(0));

I would change this to:
    if constexpr (extents_type::rank() > 1)
      {
           if constexpr (extents_type::static_extent(0) != dynamic_extent &&
__ostride_sta != dynamic_extent)
              static_assert(extents_type::static_extent(0) == __ostride_sta);
           else
              __glibcxx_assert(__other.stride(1) ==
__other.extents().extent(0))
      }
  We do not need dynamic check if we checked statically.


+       }
+#endif
+
        constexpr mapping&
        operator=(const mapping&) noexcept = default;

@@ -1173,6 +1260,582 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        [[no_unique_address]] _Strides _M_strides;
      };

+#ifdef __glibcxx_padded_layouts
+  namespace __mdspan
+  {
+    constexpr size_t
+    __least_multiple(size_t __x, size_t __y)
+    {
+      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 = __gnu_cxx::__int_traits<_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 __y_mod_x = __y % __x;
+      auto __delta = (__y_mod_x == 0) ? size_t(0) : (__x - __y_mod_x);
+      return std::cmp_less_equal(__delta, __max_delta);
+    }
+
+    template<typename _Extents, size_t _PaddingValue, typename
_LayoutTraits,
+            size_t _Rank = _Extents::rank()>
+      concept __valid_static_stride = (_Extents::rank() <= 1)
+       || (_PaddingValue == dynamic_extent)
+       || (_Extents::static_extent(_LayoutTraits::_S_ext_idx) ==
dynamic_extent)
+       || (__is_representable_least_multiple<size_t>(
+           _PaddingValue,
_Extents::static_extent(_LayoutTraits::_S_ext_idx)));
+
+    template<typename _TargetInt, size_t _PaddingValue, typename _Extents,
+            typename _LayoutTraits>
+      consteval bool
+      __is_representable_padded_size()
+      {
+       size_t __ext_idx = _LayoutTraits::_S_ext_idx;
+       auto __sta_exts = __static_extents<_Extents>(
+         _LayoutTraits::_S_unpad_begin, _LayoutTraits::_S_unpad_end);
+       auto __pad_ext = _Extents::static_extent(__ext_idx);
+       size_t __padded_stride = __least_multiple(_PaddingValue,
__pad_ext);
+       auto __max = __gnu_cxx::__int_traits<_TargetInt>::__max;
+       return __static_quotient(__sta_exts, __max / __padded_stride) != 0;
+      }
+
+    template<typename _TargetInt, typename _Extents, size_t _PaddingValue,
+             typename _LayoutTraits,
+            size_t _Rank = _Extents::rank()>
+      concept __valid_padded_size = (_Rank <= 1)
+       || (_PaddingValue == dynamic_extent)
+       || (!__all_static(__static_extents<_Extents>()))
+       || (__contains_zero(__static_extents<_Extents>()))
+       || (__is_representable_padded_size<_TargetInt, _PaddingValue,
_Extents,
+                                          _LayoutTraits>());
+
+    template<typename _Extents, typename _Stride, typename... _Indices>
+      constexpr typename _Extents::index_type
+      __linear_index_leftpad(const _Extents& __exts, _Stride __stride,
+                            _Indices... __indices)

If we would provide an extent() method for PaddedStorage, that returns
either padded_stride or _M_extent, we could use __linear_index_leaft to
compute
it, could we?
We expand

I think I understand. However, I'd really prefer to have
five separate, simple methods. In the applications I'm familiar with
these are the only performance critical parts of mdspan. Everything
else I'd expect to add up to a tiny fraction of the total runtime.

I'd like to punt this one. There's a need to check that it generates
sensible code. Which I'm happy to do, but as a dedicated project.



+      {
+       // 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 _Rank>
+      struct _LeftPaddedLayoutTraits
+      {
+       using _LayoutSame = layout_left;
+       using _LayoutOther = layout_right;
+
+       constexpr static size_t _S_ext_idx = 0;
+       constexpr static size_t _S_stride_idx = 1;
+       constexpr static size_t _S_unpad_begin = 1;
+       constexpr static size_t _S_unpad_end = _Rank;

Make this const, so they do not occupy storage, and do not have linake

+
+       template<typename _Mapping>
+         constexpr static bool _S_is_same_padded_mapping =
+         __is_left_padded_mapping<_Mapping>;

Or you can remove them completely, see below.


+
+       template<typename _Mapping>
+         constexpr static bool _S_is_other_padded_mapping =
+         __is_right_padded_mapping<_Mapping>;

As above.

+
+       template<typename _IndexType, size_t _StaticStride,
size_t..._Extents>
+         constexpr static auto _S_make_padded_extent(
+           extents<_IndexType, _StaticStride> __stride,
+           const extents<_IndexType, _Extents...>& __exts)
+         {
+           auto __impl = [&]<size_t... _Is>(integer_sequence<size_t,
_Is...>)
+           {
+             return extents<_IndexType, _StaticStride,
+                            (_Extents...[_Is + 1])...>{
+               __stride.extent(0), __exts.extent(_Is + 1)...};

+           };
+           return __impl(make_index_sequence<sizeof...(_Extents) - 1>());
+         }
+      };
+
+    template<size_t _PaddingValue, typename _Extents, typename
_LayoutTraits>
+      class _PaddedStorage
+      {
+       using _LayoutSame = typename _LayoutTraits::_LayoutSame;
+
+      public:
+       using _IndexType = typename _Extents::index_type;
+       constexpr static size_t _S_rank = _Extents::rank();
+
+       static_assert(__representable_size<_Extents, _IndexType>,
+         "The size of extents_type must be representable as index_type");
+
+       static_assert(__valid_static_stride<_Extents, _PaddingValue,
+                                           _LayoutTraits>,
+         "The padded stride must be representable as size_t");
+
+       static_assert(__valid_padded_size<size_t, _Extents, _PaddingValue,
+                                         _LayoutTraits>);
+
+       static constexpr size_t _S_static_stride = [] consteval
+       {
+         constexpr size_t __rank = _Extents::rank();
+         if constexpr (__rank <= 1)
+           return 0;
+         else
+           {
+             constexpr size_t __ext_idx = _LayoutTraits::_S_ext_idx;
+             constexpr size_t __sta_ext =
_Extents::static_extent(__ext_idx);
+             if constexpr (__sta_ext == 0)
+               return size_t(0);
+             else if constexpr (_PaddingValue == dynamic_extent
+                 || __sta_ext == dynamic_extent)
+               return dynamic_extent;
+             else
+               return __least_multiple(_PaddingValue, __sta_ext);
+           }
+       }();
+
+       static_assert(_S_static_stride == dynamic_extent
+         || cmp_less_equal(_S_static_stride,
+                           __gnu_cxx::__int_traits<_IndexType>::__max));

Add a message here:
"The padded stride must be representable as index_type");


+
+       static_assert(__valid_padded_size<_IndexType, _Extents,
_PaddingValue,
+                                         _LayoutTraits>);
+
+       // _GLIBCXX_RESOLVE_LIB_DEFECTS
+       // 4372. Weaken Mandates: for dynamic padding values in padded
layouts
+       static_assert((_PaddingValue == dynamic_extent)
+           || (cmp_less_equal(_PaddingValue,
+
__gnu_cxx::__int_traits<_IndexType>::__max)),
+         "padding_value must be representable as index_type");

Move this check first, it very inexpensive, and we then do not need to even
check
representable size.

+
+       constexpr
+       _PaddedStorage() noexcept
+       {
+         if constexpr (_S_rank > 0)
+           {
+             constexpr size_t __sta_ext = _S_static_padded_extent();

With rename, to _S_static_padextent() I think you can remove the variable
and the nestewd if.

+             if constexpr (_S_static_stride == dynamic_extent
+                           && __sta_ext != dynamic_extent)
+               _M_stride = _Stride{__sta_ext};
+           }
+       }
+
+       constexpr explicit
+       _PaddedStorage(const _Extents& __exts)
+       : _M_extents(__exts)
+       {
+         if constexpr (!__all_static(__static_extents<_Extents>()))
+           __glibcxx_assert(__is_representable_extents(_M_extents));
+
+         if constexpr (_S_rank > 1)
+           {
+             constexpr size_t __sta_pad_ext = _S_static_padded_extent();
+             _IndexType __dyn_pad_ext = _M_dynamic_padded_extent();

Same here.

+
+             if constexpr (_PaddingValue != dynamic_extent)
+               __glibcxx_assert(
+                   __is_representable_least_multiple<_IndexType>(
+                     _PaddingValue, __dyn_pad_ext));

This check is too eager, to do, as if _S_static_padextent(0) is not dynamic,
we have checked that at compile time, this should be in the else branch
below,
before we compute __least_multiple.


+
+             _IndexType __stride;
+             if constexpr (_PaddingValue == dynamic_extent)
+               __stride = __dyn_pad_ext;
+             else if constexpr (__sta_pad_ext != dynamic_extent)
+               return;
+             else
+               {
+                 __stride = static_cast<_IndexType>(
+                   __least_multiple(_PaddingValue, __dyn_pad_ext));
+                 __glibcxx_assert(__is_representable_extents(
+                     _LayoutTraits::_S_make_padded_extent(
+                       std::dextents<_IndexType, 1>{__stride},
+                       _M_extents)));
+               }
+             _M_stride = _Stride{__stride};
+           }
+       }
+
+       template<__valid_index_type<_IndexType> _OIndexType>

Do not check concepts here again, they are checked when original constructor
is invoked, and we forward here when this is valid.

+         constexpr

Make all constructors explicit for the same reason.

I've double checked, they should all be explicit. (This one isn't
because it has two arguments so it's much less error prone.) I added
the explicit.


+         _PaddedStorage(const _Extents& __exts, _OIndexType&& __opad)
+         : _M_extents(__exts)
+         {
+           if constexpr (std::is_integral_v<_OIndexType>)
+             {
+               __glibcxx_assert(cmp_less_equal(__opad,
+                   __gnu_cxx::__int_traits<_IndexType>::__max));
+               if constexpr (std::is_signed_v<_OIndexType>)
+                 __glibcxx_assert(__opad >= 0);
+             }
+           auto __pad = static_cast<_IndexType>(std::move(__opad));
+           if constexpr (std::is_signed_v<_IndexType>)
+             __glibcxx_assert(__pad >= 0);
+           if constexpr (_PaddingValue != dynamic_extent)
+             __glibcxx_assert(cmp_equal(_PaddingValue, __pad));
+           if constexpr (_S_rank > 1 && _S_static_stride ==
dynamic_extent)
+             {
+               __glibcxx_assert(
+                 __is_representable_least_multiple<_IndexType>(
+                   __pad, _M_dynamic_padded_extent()));
+
+               _M_stride = _Stride{static_cast<_IndexType>(
+                 __least_multiple(__pad, _M_dynamic_padded_extent()))};
+
+               __glibcxx_assert(__is_representable_extents(
+                 _LayoutTraits::_S_make_padded_extent(
+                   _M_stride, _M_extents)));
+             }
+         }
+
+       template<typename _OExtents>
+         requires is_constructible_v<_Extents, _OExtents>

Again we checked it before calling here, remove the check.

+         constexpr explicit
+         _PaddedStorage(const typename _LayoutSame::mapping<_OExtents>&
+                        __other)
+         : _PaddedStorage(_Extents(__other.extents()))
+         {
+           constexpr size_t __ext_idx = _LayoutTraits::_S_ext_idx;
+           static_assert(_S_rank <= 1

This is same condition as below, so I woul do:
   if constexpr (_S_rank > 1) and put static_assert inside.


+               || _S_static_stride == dynamic_extent
+               || _OExtents::static_extent(__ext_idx) == dynamic_extent
+               || _S_static_stride == _OExtents::static_extent(__ext_idx),
+             "The padded stride must be compatible with other");
+
+           if constexpr (_S_rank > 1 && _PaddingValue != dynamic_extent)
+             __glibcxx_assert(std::cmp_equal(_M_dynamic_padded_stride(),
+                                             _M_dynamic_padded_extent()));
+         }
+
+       template<typename _OExtents>
+         requires is_constructible_v<_OExtents, _Extents>

Same here.

+         constexpr explicit
+         _PaddedStorage(const typename layout_stride::mapping<_OExtents>&
+                        __other)
+         : _M_extents(__other.extents())
+         {
+           constexpr size_t __stride_idx = _LayoutTraits::_S_stride_idx;
+           if constexpr (_S_rank > 1 && _S_static_stride !=
dynamic_extent)
+             __glibcxx_assert(cmp_equal(__other.stride(__stride_idx),
+                                        _S_static_stride));
+           else if constexpr (_S_rank > 1 && _PaddingValue !=
dynamic_extent)
+             __glibcxx_assert(cmp_equal(__other.stride(__stride_idx),
+                 __least_multiple(_PaddingValue,
_M_dynamic_padded_extent())));
+
+           __glibcxx_assert(cmp_less_equal(__other.required_span_size(),
+
  __gnu_cxx::__int_traits<_IndexType>
+                                                    ::__max));

If you check this first, then we can extract all other code into the
constexpr (_S_rank > 1)
constexpr branch.

Also add TODO  to compare strides.

The strides are compared in the mapping ctor, because there
one can write *this == other.


+
+           if constexpr (_S_rank > 1 && _S_static_stride ==
dynamic_extent)
+             _M_stride = _Stride{__other.stride(__stride_idx)};
+         }
+
+       template<typename _SamePaddedMapping>
+         requires _LayoutTraits::template _S_is_same_padded_mapping<
+                    _SamePaddedMapping>
+             && is_constructible_v<_Extents,
+                                   typename
_SamePaddedMapping::extents_type>

Remove this here.

+         constexpr explicit
+         _PaddedStorage(const _SamePaddedMapping& __other)

And instead add an additional function parameter, typename
_LayoutTraits::_LayoutSame here,
and pass it when calling this function. This will act as the tag.

+         : _M_extents(__other.extents())
+         {
+           if constexpr (_S_rank > 1)
+           {
+             static_assert(_PaddingValue == dynamic_extent
+                 || _SamePaddedMapping::padding_value  == dynamic_extent
+                 || _PaddingValue == _SamePaddedMapping::padding_value,
+               "If neither PaddingValue is dynamic_extent, then they must
"
+               "be equal");
+
+           constexpr size_t __ext_idx = _LayoutTraits::_S_ext_idx;
+           constexpr size_t __stride_idx = _LayoutTraits::_S_stride_idx;

Indent these more.

+             if constexpr (_S_static_stride != dynamic_extent)
+               __glibcxx_assert(cmp_equal(_S_static_stride,
+                                          __other.stride(__stride_idx)));
+             else if constexpr (_PaddingValue != dynamic_extent)
+               __glibcxx_assert(cmp_equal(__other.stride(__stride_idx),
+                     __least_multiple(
+                       _PaddingValue,
__other.extents().extent(__ext_idx)))
+                 && "The padded stride must be compatible with other");
+
+             if constexpr (_S_static_stride == dynamic_extent)
+               _M_stride = _Stride{__other.stride(__stride_idx)};
+           }
+           __glibcxx_assert(cmp_less_equal(__other.required_span_size(),
+               __gnu_cxx::__int_traits<_IndexType>::__max));
+         }
+
+       template<typename _OtherPaddedMapping>
+         requires (_LayoutTraits::template _S_is_other_padded_mapping<
+                     _OtherPaddedMapping>
+             || __mapping_of<typename _LayoutTraits::_LayoutOther,
+                             _OtherPaddedMapping>)
+           && (_S_rank <= 1)
+           && is_constructible_v<_Extents,
+                                 typename
_OtherPaddedMapping::extents_type>

Same suggestion as above.

+         constexpr explicit
+         _PaddedStorage(const _OtherPaddedMapping& __other) noexcept
+         : _M_extents(__other.extents())
+         {
+           __glibcxx_assert(cmp_less_equal(__other.required_span_size(),
+               __gnu_cxx::__int_traits<_IndexType>::__max));
+         }
+
+       static constexpr bool
+       _M_is_always_exhaustive() noexcept
+       {
+         if constexpr (_S_rank <= 1)
+           return true;
+         else
+           {
+             constexpr size_t __sta_pad_ext = _S_static_padded_extent();
+             return __sta_pad_ext != dynamic_extent
+                    && _S_static_stride != dynamic_extent
+                    && __sta_pad_ext == _S_static_stride;
+           }
+       }
+
+       constexpr bool
+       _M_is_exhaustive() const noexcept
+       {
+         if constexpr (_M_is_always_exhaustive())
+           return true;
+         else
+           return cmp_equal(_M_dynamic_padded_extent(),
+                            _M_dynamic_padded_stride());
+       }
+
+       constexpr static size_t
+       _S_static_padded_extent() noexcept

Name it _S_static_padextent.

+       { return _Extents::static_extent(_LayoutTraits::_S_ext_idx); }
+
+       constexpr _IndexType
+       _M_dynamic_padded_extent() const noexcept

Name it just padded_extent, we have extent and static_extent,
or even _M_padextent(), so you do not introduce variable like __dyn_pad_ext,
in constructors.

+       { return _M_extents.extent(_LayoutTraits::_S_ext_idx); }
+
+       constexpr size_t
+       _M_dynamic_padded_stride() const noexcept

Similar here, remove dynamic, and name it _M_padstride.

+       { return _M_stride.extent(0); }
+
+       constexpr _IndexType
+       _M_required_span_size() const noexcept
+       {
+         if constexpr (_S_rank == 0)
+           return 1;
+         else if (__mdspan::__empty(_M_extents))
+           return 0;
+         else
+           return static_cast<_IndexType>(
+               static_cast<size_t>(_M_dynamic_padded_stride())
+               * __mdspan::__fwd_prod(_M_extents,
+                                      _LayoutTraits::_S_unpad_begin,
+                                      _LayoutTraits::_S_unpad_end)
+                  - static_cast<size_t>(_M_dynamic_padded_stride()
+                                        - _M_dynamic_padded_extent()));

Could you split this a bit?
// use size_t for computation, as multiplication may overflow
size_t __size =
   static_cast<size_t>(_M_padded_stride())
       * __mdspan::__fwd_prod(_M_extents,
+                                      _LayoutTraits::_S_unpad_begin,
+                                      _LayoutTraits::_S_unpad_end)
+                  - static_cast<size_t>(_M_dynamic_padded_stride()
+                                        - _M_dynamic_padded_extent()));
  return static_cast<_IndexType>(__size);

+       }
+
+       using _Stride = std::extents<_IndexType, _S_static_stride>;
+       [[no_unique_address]] _Stride _M_stride;
+       [[no_unique_address]] _Extents _M_extents{};
+      };
+  }
+
+  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>;
+
+      private:
+       static constexpr size_t _S_rank = extents_type::rank();
+       using _LeftPaddedStorage = __mdspan::_PaddedStorage<_PaddingValue,
+             _Extents, __mdspan::_LeftPaddedLayoutTraits<_S_rank>>;
+       [[no_unique_address]] _LeftPaddedStorage _M_storage;
+
+       static constexpr size_t _S_static_stride =
+         _LeftPaddedStorage::_S_static_stride;

Remove this, name the aliases _PaddedStorage for both mapping, and
define _get_static_stride as:
   return _Layout::_PaddedStorage::_S_static_stride


+
+       consteval friend size_t
+       __mdspan::__get_static_stride<mapping>();
+
+       constexpr index_type
+       _M_extent(size_t __r) const noexcept
+       { return _M_storage._M_extents.extent(__r); }
+
+       constexpr index_type
+       _M_dynamic_padded_stride() const noexcept
+       { return _M_storage._M_stride.extent(0); }
+
+      public:
+       constexpr
+       mapping() noexcept
+       { }
+
+       constexpr
+       mapping(const mapping&) noexcept = default;
+
+       constexpr
+       mapping(const extents_type& __exts)
+       : _M_storage(__exts)
+       { }
+
+       template<__mdspan::__valid_index_type<index_type> _OIndexType>
+         constexpr mapping(const extents_type& __exts, _OIndexType __opad)
+         : _M_storage(__exts, std::move(__opad))
+         { }
+
+       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)
+         : _M_storage(__other)
+         { }
+
+       template<typename _OExtents>
+         requires is_constructible_v<_OExtents, extents_type>
+         constexpr explicit(_OExtents::rank() > 0)
+         mapping(const typename layout_stride::mapping<_OExtents>&
__other)
+         : _M_storage(__other)
+         { __glibcxx_assert(*this == __other); }
+
+       template<typename _LeftpadMapping>
+         requires __mdspan::__is_left_padded_mapping<_LeftpadMapping>
+             && is_constructible_v<extents_type,
+                                   typename _LeftpadMapping::extents_type>
+         constexpr explicit(_S_rank > 1 && (padding_value !=
dynamic_extent
+               || _LeftpadMapping::padding_value == dynamic_extent))
+         mapping(const _LeftpadMapping& __other)
+         : _M_storage(__other)

With the suggestion to avoid having constrain again, use  :
_M_storage(layout_left, __other)

+         { }
+
+       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_storage(__other)

And here it would be  _M_storage(layot_right, __other)

+         { }
+
+       constexpr mapping&
+       operator=(const mapping&) noexcept = default;
+
+       constexpr const extents_type&
+       extents() const noexcept { return _M_storage._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_dynamic_padded_stride();
+         if constexpr (_S_rank > 2)
+           for(size_t __i = 2; __i < _S_rank; ++__i)
+             __ret[__i] = __ret[__i - 1] * _M_extent(__i - 1);
+         return __ret;
+       }
+
+       constexpr index_type
+       required_span_size() const noexcept
+       { return _M_storage._M_required_span_size(); }
+
+       // _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(
+             extents(), _M_storage._M_stride,
+             static_cast<index_type>(std::move(__indices))...);
+         }
+
+       static constexpr bool
+       is_always_exhaustive() noexcept
+       { return _LeftPaddedStorage::_M_is_always_exhaustive(); }
+
+       constexpr bool
+       is_exhaustive() noexcept
+       { return _M_storage._M_is_exhaustive(); }
+
+       static constexpr bool
+       is_always_unique() noexcept { return true; }
+
+       static constexpr bool
+       is_always_strided() noexcept { return true; }
+
+       static constexpr bool
+       is_unique() noexcept { return true; }
+
+       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_dynamic_padded_stride()) *
+             static_cast<size_t>(__mdspan::__fwd_prod(extents(), 1,
__r)));
+       }
+
+       template<typename _LeftpadMapping>
+         requires(__mdspan::__is_left_padded_mapping<_LeftpadMapping>
+                  && _LeftpadMapping::extents_type::rank() == _S_rank)
+         friend constexpr bool
+         operator==(const mapping& __self, const _LeftpadMapping& __other)
+         noexcept
+         {
+           return __self.extents() == __other.extents()
+             && (_S_rank < 2 || cmp_equal(__self.stride(1),
+                                          __other.stride(1)));
+         }
+      };
+#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 9352482cbdb..f10bab59e2c 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..891471467e1 100644
--- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
@@ -1,11 +1,22 @@
  // { dg-do run { target c++23 } }
  #include <mdspan>

+#include "padded_traits.h"
  #include <cstdint>
  #include <testsuite_hooks.h>

  constexpr size_t dyn = std::dynamic_extent;

+#if __glibcxx_padded_layouts

We have recently discussed using FTM in internal test, and
realized that it will make it harder to use them with import std.
We realize that we most likely should use __cplusplus instead.


+template<typename Layout>
+  constexpr bool
+  is_padded_layout = is_left_padded<Layout>;
+#else
+template<typename>
+  constexpr bool
+  is_padded_layout = false;
+#endif

I would prefer it to be defined as:
template<typename Layout>
   constexpr bool
   is_padded_layout =
#if __glibcxx_padded_layouts
      is_left_padded<Layout>
#else
    false
#endif

+
  template<typename Mapping, typename IndexType, size_t... Extents>
    constexpr void
    verify(std::extents<IndexType, Extents...> oexts)
@@ -27,7 +38,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 +50,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_padded_layout<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 +70,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_padded_layout<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 +212,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 +249,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_padded_layout<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 +264,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_padded_layout<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 +281,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_padded_layout<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;
      }

@@ -424,11 +462,24 @@ template<typename Layout>
      from_stride::test_all<Layout>();
    }

+template<template<size_t> typename Layout>
+  constexpr void
+  test_padded_all()
+  {
+    test_all<Layout<0>>();
+    test_all<Layout<1>>();
+    test_all<Layout<2>>();
+    test_all<Layout<dyn>>();
+  }
+
  int
  main()
  {
    test_all<std::layout_left>();
    test_all<std::layout_right>();
+#ifdef __glibcxx_padded_layouts
+  test_padded_all<std::layout_left_padded>();
+#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/empty.cc
b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc
index cbc425f6c15..236aca8bc2b 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 @@ template<typename Layout, typename Int>
    {
      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 @@ template<typename Layout, typename Int>
    {
      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}));
@@ -121,11 +123,25 @@ template<typename Layout>
      return true;
    }

+template<template<size_t> typename Layout>
+  constexpr bool
+  test_padded_all()
+  {
+    static_assert(test_all<Layout<0>>());
+    static_assert(test_all<Layout<1>>());
+    static_assert(test_all<Layout<2>>());
+    static_assert(test_all<Layout<dyn>>());
+    return true;
+  }
+
  int
  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_padded_all<std::layout_left_padded>());
+#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..5bf6bf65d3a 100644
--- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc
@@ -2,9 +2,11 @@
  #include <mdspan>

  #include "../int_like.h"
+#include "padded_traits.h"
  #include <cstdint>
  #include <testsuite_hooks.h>

+

Extra new line appeared here.

  constexpr size_t dyn = std::dynamic_extent;

  template<typename Mapping>
@@ -370,6 +372,37 @@ template<>
      }
    };

+#if __glibcxx_padded_layouts
+template<typename Layout>
+  requires is_left_padded<Layout>
+  struct TestStride2D<Layout>
+  {
+    static constexpr void
+    run()
+    {
+      using Traits =
LayoutTraits<DeducePaddingSide::from_typename<Layout>()>;
+      using Extents = typename Traits::extents_type<std::extents<int, 3,
5>>;
+      using Mapping = typename Layout::mapping<Extents>;
+      constexpr size_t padding_value = Mapping::padding_value;
+
+      Mapping m;
+      size_t effective_pad = (padding_value == 0 || padding_value == dyn)
+       ? size_t(1) : padding_value;
+
+      constexpr auto i0 = is_left_padded<Layout> ? 0 : 1;
+      VERIFY(m.stride(i0) == 1);
+
+      // The next multiple of padding_value, that's greater or equal
+      // to exts.extent(0) is the unique value in the range:
+      //   [exts.extent(0), exts.extent(0) + padding_value)
+      // that is divisible by padding_value.
+      auto stride = Traits::padded_stride(m);
+      VERIFY((stride % effective_pad) == 0);
+      VERIFY(3 <= stride && std::cmp_less(stride, 3 + effective_pad));
+    }
+  };
+#endif
+
  template<typename Layout>
    constexpr void
    test_stride_2d()
@@ -423,6 +456,40 @@ template<>
      }
    };

+#if __glibcxx_padded_layouts
+template<typename Layout>
+  requires is_left_padded<Layout>
+  struct TestStride3D<Layout>
+  {
+    static constexpr void
+    run()
+    {
+      using Traits =
LayoutTraits<DeducePaddingSide::from_typename<Layout>()>;
+      using Extents = typename Traits::extents_type<std::extents<int, 3,
5, 7>>;
+      using Mapping = typename Layout::mapping<Extents>;
+      constexpr size_t padding_value = Mapping::padding_value;
+
+      Mapping m;
+      size_t effective_pad = (padding_value == 0 || padding_value == dyn)
+       ? size_t(1) : padding_value;
+
+      constexpr auto i0 = is_left_padded<Layout> ? 0 : 2;
+      VERIFY(m.stride(i0) == 1);
+
+      // The next multiple of padding_value, that's greater or equal
+      // to exts.extent(0) is the unique value in the range:
+      //   [exts.extent(0), exts.extent(0) + padding_value)
+      // that is divisible by padding_value.
+      auto stride = Traits::padded_stride(m);
+      VERIFY((stride % effective_pad) == 0);
+      VERIFY(3 <= stride && std::cmp_less(stride, 3 + effective_pad));
+
+      constexpr auto i2 = is_left_padded<Layout> ? 2 : 0;
+      VERIFY(stride * 5 == m.stride(i2));
+    }
+  };
+#endif
+
  template<typename Layout>
    constexpr void
    test_stride_3d()
@@ -451,7 +518,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);
    }

@@ -595,16 +663,54 @@ template<typename Layout>
      test_has_op_eq<Layout, Layout, true>();
    }

+#ifdef __glibcxx_padded_layouts
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_padded_all()
+  {
+    test_all<Layout<0>>();
+    test_all<Layout<1>>();
+    test_all<Layout<2>>();
+    test_all<Layout<5>>();
+    test_all<Layout<dyn>>();
+    return true;
+  }
+
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_padded_has_op_eq()
+  {
+    using Traits =
LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
+    test_has_op_eq<typename Traits::layout_same, Layout<0>, false>();
+    test_has_op_eq<typename Traits::layout_same, Layout<6>, false>();
+    test_has_op_eq<typename Traits::layout_same, Layout<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<typename Traits::layout_other, Layout<0>, false>();
+
+    test_has_op_eq<Layout<2>, Layout<6>, true>();
+    test_has_op_eq<Layout<2>, Layout<dyn>, true>();
+    return true;
+  }
+#endif
+
  int
  main()
  {
    test_all<std::layout_left>();
    test_all<std::layout_right>();
    test_all<std::layout_stride>();
+#ifdef __glibcxx_padded_layouts
+  test_padded_all<std::layout_left_padded>();
+#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_padded_has_op_eq<std::layout_left_padded>();
+#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..ea9a8ef3f4b
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc
@@ -0,0 +1,675 @@
+// { dg-do run { target c++26 } }
+#include <mdspan>
+
+#include <cstdint>
+#include "../int_like.h"
+#include "padded_traits.h"
+#include <testsuite_hooks.h>
+
+#include <iostream>
+
+constexpr size_t dyn = std::dynamic_extent;
+
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_representable_padded_size()
+  {
+    using Traits =
LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
+    {
+      using E = typename Traits::extents_type<std::extents<uint8_t, 64,
2>>;
+      [[maybe_unused]] typename Layout<1>::mapping<E> m1;
+    }
+
+    {
+      using E = typename Traits::extents_type<std::extents<uint8_t, 0,
2>>;
+      [[maybe_unused]] typename Layout<0>::mapping<E> m1;
+      [[maybe_unused]] typename Layout<1>::mapping<E> m2;
+      [[maybe_unused]] typename Layout<128>::mapping<E> m3;
+      [[maybe_unused]] typename Layout<255>::mapping<E> m4;
+    }
+
+    {
+      using E = typename Traits::extents_type<std::extents<uint8_t, 0,
2>>;
+      [[maybe_unused]] typename Layout<dyn>::mapping<E> m1(E{}, 0);
+      [[maybe_unused]] typename Layout<dyn>::mapping<E> m2(E{}, 1);
+      [[maybe_unused]] typename Layout<dyn>::mapping<E> m3(E{}, 128);
+      [[maybe_unused]] typename Layout<dyn>::mapping<E> m4(E{}, 255);
+    }
+
+    {
+      using E = typename Traits::extents_type<std::extents<uint8_t, dyn,
2>>;
+      [[maybe_unused]] typename Layout<0>::mapping<E> m1;
+      [[maybe_unused]] typename Layout<1>::mapping<E> m2;
+      [[maybe_unused]] typename Layout<128>::mapping<E> m3;
+      [[maybe_unused]] typename Layout<255>::mapping<E> m4;
+    }
+    return true;
+  }
+
+template<typename Layout, typename CanonicalExtents>
+  constexpr void
+  test_default_ctor_single(auto canonical_strides)
+  {
+    using Traits =
LayoutTraits<DeducePaddingSide::from_typename<Layout>()>;
+    using E = typename Traits::extents_type<CanonicalExtents>;
+    auto strides = Traits::make_expected(canonical_strides);
+
+    typename Layout::template mapping<E> msta;
+    VERIFY(msta.stride(0) == strides[0]);
+    VERIFY(msta.stride(1) == strides[1]);
+  }
+
+
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_default_ctor()
+  {
+    using E1 = std::extents<size_t, 3, 5>;
+    test_default_ctor_single<Layout<2>, E1>(std::array<size_t, 2>{1, 4});
+    test_default_ctor_single<Layout<dyn>, E1>(std::array<size_t, 2>{1,
3});
+
+    using E2 = std::extents<size_t, dyn, 5>;
+    test_default_ctor_single<Layout<2>, E2>(std::array<size_t, 2>{1, 0});
+    test_default_ctor_single<Layout<dyn>, E2>(std::array<size_t, 2>{1,
0});
+    return true;
+  }
+
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_from_exts()
+  {
+    using Traits =
LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
+    auto exts = Traits::make_extents(std::dextents<size_t, 2>{3, 5});
+
+    typename Layout<0>::mapping m0_sta(exts);
+    VERIFY(Traits::padded_stride(m0_sta) == Traits::padded_extent(exts));
+
+    typename Layout<1>::mapping m1_sta(exts);
+    VERIFY(Traits::padded_stride(m1_sta) == Traits::padded_extent(exts));
+
+    typename Layout<2>::mapping m2_sta(exts);
+    VERIFY(Traits::padded_stride(m2_sta) == 4);
+
+    typename Layout<dyn>::mapping mdyn(exts);
+    VERIFY(Traits::padded_stride(mdyn) == Traits::padded_extent(exts));
+    return true;
+  }
+
+template<typename Layout, typename CustomPadType>
+  constexpr bool
+  test_from_pad_single()
+  {
+    using Traits =
LayoutTraits<DeducePaddingSide::from_typename<Layout>()>;
+    auto pad = 3;
+    auto exts = Traits::make_extents(std::dextents<size_t, 3>{pad + 1, 5,
7});
+    typename Layout::mapping m(exts, CustomPadType{pad});
+    VERIFY(std::cmp_equal(Traits::padded_stride(m), 2*pad));
+    VERIFY(m.extents() == exts);
+    return true;
+  }
+
+template<typename Layout>
+  constexpr void
+  test_from_pad()
+  {
+    test_from_pad_single<Layout, int>();
+    static_assert(test_from_pad_single<Layout, int>());
+
+    test_from_pad_single<Layout, IntLike>();
+    test_from_pad_single<Layout, MutatingInt>();
+    test_from_pad_single<Layout, RValueInt>();
+
+    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>);
+  }
+
+template<template<size_t> typename Layout>
+  constexpr void
+  test_from_pad_all()
+  {
+    test_from_pad<Layout<3>>();
+    test_from_pad<Layout<dyn>>();
+  }
+
+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 Layout>
+  constexpr void
+  test_from_same_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>;
+
+    using Traits =
LayoutTraits<DeducePaddingSide::from_typename<Layout>()>;
+    auto msta = typename Traits::layout_same::mapping(E1{});
+    check_convertible_variants<Layout, E1, E2, E3>(msta, cr);
+  }
+
+template<typename Layout>
+  constexpr void
+  test_from_same_2d()
+  {
+    using Traits =
LayoutTraits<DeducePaddingSide::from_typename<Layout>()>;
+    using E1 = typename Traits::extents_type<std::extents<int, 6, 5>>;
+    using E2 = typename Traits::extents_type<std::extents<int, dyn, 5>>;
+    using E3 = typename Traits::extents_type<std::extents<int, 6, 6>>;
+    constexpr auto cr = std::cw<ConversionRule::Regular>;
+
+    auto msta = typename Traits::layout_same::mapping(E1{});
+    check_convertible_variants<Layout, E1, E2, E3>(msta, cr);
+  }
+
+template<template<size_t> typename Layout>
+constexpr bool
+test_from_same()
+{
+  auto check = []<typename PaddedLayout>(PaddedLayout)
+  {
+    test_from_same_1d<PaddedLayout>();
+    test_from_same_2d<PaddedLayout>();
+  };
+
+  check(Layout<0>{});
+  check(Layout<1>{});
+  check(Layout<2>{});
+  check(Layout<6>{});
+  check(Layout<dyn>{});
+
+  // rank == 1 is more permissive:
+  test_from_same_1d<Layout<5>>();
+  return true;
+}
+
+template<template<size_t> typename Layout, typename E1_, typename E2_,
+        typename E3_>
+  constexpr bool
+  test_from_stride_nd(auto strides_)
+  {
+    using Traits =
LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
+    using E1 = typename Traits::extents_type<E1_>;
+    using E2 = typename Traits::extents_type<E2_>;
+    using E3 = typename Traits::extents_type<E3_>;
+    auto strides = Traits::make_expected(strides_);
+
+    auto check = [&strides]<typename PaddedLayout>(PaddedLayout)
+    {
+      auto exts = E1{};
+      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(Layout<0>{});
+    check(Layout<1>{});
+    check(Layout<2>{});
+    check(Layout<6>{});
+    check(Layout<dyn>{});
+    return true;
+  }
+
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_from_stride_2d()
+  {
+    using E1 = std::extents<size_t, 6, 5>;
+    using E2 = std::dims<2>;
+    using E3 = std::extents<size_t, 6, 6>;
+
+    auto strides = std::array<int, 2>{1, 6};
+    test_from_stride_nd<Layout, E1, E2, E3>(strides);
+    return true;
+  }
+
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_from_stride_3d()
+  {
+    using E1 = std::extents<size_t, 6, 5, 7>;
+    using E2 = std::dims<3>;
+    using E3 = std::extents<size_t, 6, 6, 7>;
+
+    auto strides = std::array<int, 3>{1, 6, 6*5};
+    test_from_stride_nd<Layout, E1, E2, E3>(strides);
+    return true;
+  }
+
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_from_stride()
+  {
+    test_from_stride_2d<Layout>();
+    test_from_stride_3d<Layout>();
+    return true;
+  }
+
+template<template<size_t> typename Layout>
+  constexpr void
+  test_from_samepad_0d()
+  {
+    using E1 = std::extents<uint16_t>;
+    using E2 = std::extents<uint8_t>;
+    using E3 = std::extents<uint8_t, 1>;
+
+    typename Layout<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(Layout<6>{}, msta);
+    check(Layout<dyn>{}, msta);
+  }
+
+template<template<size_t> typename Layout>
+  constexpr void
+  test_from_samepad_1d()
+  {
+    using E1 = std::extents<int, 6>;
+    using E2 = std::extents<int, dyn>;
+    using E3 = std::extents<int, 6, 6>;
+
+    typename Layout<6>::mapping<E1> msta{E1{}};
+    typename Layout<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(Layout<6>{}, msta);
+    check(Layout<6>{}, mdyn);
+    check(Layout<dyn>{}, msta);
+    check(Layout<dyn>{}, mdyn);
+  }
+
+template<template<size_t> typename Layout>
+  constexpr void
+  test_from_samepad_2d()
+  {
+    using Traits =
LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
+    using E1 = typename Traits::extents_type<std::extents<int, 6, 5>>;
+    using E2 = typename Traits::extents_type<std::extents<int, dyn, 5>>;
+    using E3 = typename Traits::extents_type<std::extents<int, 6, 6>>;
+
+    typename Layout<6>::mapping<E1> msta{E1{}};
+    typename Layout<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(Layout<6>{}, msta, cnever);
+    check(Layout<6>{}, mdyn, cnever);
+    check(Layout<dyn>{}, msta, calways);
+    check(Layout<dyn>{}, mdyn, cnever);
+  }
+
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_from_samepad()
+  {
+    test_from_samepad_0d<Layout>();
+    test_from_samepad_1d<Layout>();
+    test_from_samepad_2d<Layout>();
+    return true;
+  }
+
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_from_other()
+  {
+    using Traits =
LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
+    using E1 = std::extents<size_t, 3>;
+    using E2 = std::dims<1>;
+    using E3 = std::extents<size_t, 5>;
+
+    auto check = []<typename PaddedLayout>(PaddedLayout)
+    {
+      constexpr auto cr = std::cw<ConversionRule::Regular>;
+
+      using layout_other = typename Traits::layout_other;
+      auto msta = typename layout_other::mapping(E1{});
+      check_convertible_variants<PaddedLayout, E1, E2, E3>(msta, cr);
+    };
+
+
+    // Remember, the padding_value has no effect for rank <= 1.
+    check(Layout<0>{});
+    check(Layout<1>{});
+    check(Layout<2>{});
+    check(Layout<5>{});
+    check(Layout<6>{});
+    check(Layout<dyn>{});
+    return true;
+  }
+
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_to_same()
+  {
+    using Traits =
LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
+    using E1 = typename Traits::extents_type<std::extents<int, 6, 5>>;
+    using E2 = typename Traits::extents_type<std::extents<int, dyn, 5>>;
+    using E3 = typename Traits::extents_type<std::extents<int, 6, 6>>;
+
+    auto check = [](auto msta)
+    {
+      constexpr auto cr = std::cw<ConversionRule::Regular>;
+      using LayoutSame = typename Traits::layout_same;
+      check_convertible_variants<LayoutSame, E1, E2, E3>(msta, cr);
+    };
+
+    check(typename Layout<0>::mapping(E1{}));
+    check(typename Layout<2>::mapping(E1{}));
+    check(typename Layout<6>::mapping(E1{}));
+    check(typename Layout<dyn>::mapping(E1{}, 0));
+    check(typename Layout<dyn>::mapping(E1{}, 2));
+    check(typename Layout<dyn>::mapping(E1{}, 6));
+    return true;
+  }
+
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_never_to_other()
+  {
+    using Traits =
LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
+    using E1 = std::extents<size_t, 3>;
+    using E2 = std::dims<1>;
+
+    auto check = []<typename PaddedLayout>(PaddedLayout, auto exts)
+    {
+      using LayoutOther = typename Traits::layout_other;
+      auto mr = typename LayoutOther::mapping(exts);
+      auto mlp = typename PaddedLayout::mapping<decltype(exts)>{mr};
+      static_assert(!std::is_constructible_v<decltype(mr),
decltype(mlp)>);
+    };
+
+    check(Layout<2>{}, E1{});
+    check(Layout<2>{}, E2{E1{}});
+    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));
+  }
+
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_strides_all()
+  {
+    test_strides<Layout<0>>();
+    test_strides<Layout<1>>();
+    test_strides<Layout<3>>();
+    test_strides<Layout<dyn>>();
+    return true;
+  }
+
+template<template<size_t> typename Layout>
+  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(typename Layout<0>::mapping(exts));
+    check(typename Layout<1>::mapping(exts));
+    check(typename Layout<2>::mapping(exts));
+    check(typename Layout<dyn>::mapping(exts));
+  }
+
+template<template<size_t> typename Layout>
+constexpr void
+  test_exhaustive_1d()
+  {
+    auto check_dyn_and_sta = []<typename PaddedLayout>(PaddedLayout)
+    {
+      auto check = [](auto exts)
+      {
+       auto m = typename PaddedLayout::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(Layout<1>{});
+    check_dyn_and_sta(Layout<2>{});
+    check_dyn_and_sta(Layout<6>{});
+    check_dyn_and_sta(Layout<dyn>{});
+  }
+
+template<template<size_t> typename Layout>
+  constexpr void
+  test_exhaustive_3d()
+  {
+    using Traits =
LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
+    auto exts_dyn = Traits::make_extents(std::extents(4, 5, 7));
+    auto exts_sta = Traits::make_extents(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(typename Layout<0>::mapping(exts_sta), ctrue, true);
+    check(typename Layout<0>::mapping(exts_dyn), cfalse, true);
+    check(typename Layout<1>::mapping(exts_sta), ctrue, true);
+    check(typename Layout<1>::mapping(exts_dyn), cfalse, true);
+    check(typename Layout<2>::mapping(exts_sta), ctrue, true);
+    check(typename Layout<2>::mapping(exts_dyn), cfalse, true);
+    check(typename Layout<6>::mapping(exts_dyn), cfalse, false);
+    check(typename Layout<6>::mapping(exts_sta), cfalse, false);
+    check(typename Layout<dyn>::mapping(exts_sta), cfalse, true);
+    check(typename Layout<dyn>::mapping(exts_dyn, 2), cfalse, true);
+    check(typename Layout<dyn>::mapping(exts_dyn, 3), cfalse, false);
+  }
+
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_exhaustive()
+  {
+    test_exhaustive_0d<Layout>();
+    test_exhaustive_1d<Layout>();
+    test_exhaustive_3d<Layout>();
+    return true;
+  }
+
+template<template<size_t> typename Layout>
+  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 Traits =
LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
+
+    auto exts_sta = Traits::make_extents(std::extents<size_t, 6, 5, 7>{});
+    auto exts_dyn = std::dims<3>(exts_sta);
+    auto exts_other = Traits::make_extents(std::extents<size_t, 7, 5,
7>{});
+
+    auto m1 = typename Layout<0>::mapping(exts_sta);
+    auto m2 = typename Layout<7>::mapping(exts_sta);
+
+    VERIFY(m1 == typename Layout<0>::mapping(exts_sta));
+    VERIFY(m1 == typename Layout<1>::mapping(exts_sta));
+    VERIFY(m1 == typename Layout<2>::mapping(exts_sta));
+    VERIFY(m1 == typename Layout<6>::mapping(exts_sta));
+    VERIFY(m1 != typename Layout<7>::mapping(exts_sta));
+    VERIFY(m1 == typename Layout<dyn>::mapping(exts_sta));
+    VERIFY(m1 != typename Layout<dyn>::mapping(exts_sta, 7));
+
+    VERIFY(m1 == typename Layout<0>::mapping(exts_dyn));
+    VERIFY(m1 == typename Layout<1>::mapping(exts_dyn));
+    VERIFY(m1 == typename Layout<2>::mapping(exts_dyn));
+    VERIFY(m1 == typename Layout<6>::mapping(exts_dyn));
+    VERIFY(m1 != typename Layout<7>::mapping(exts_dyn));
+    VERIFY(m1 == typename Layout<dyn>::mapping(exts_dyn));
+    VERIFY(m1 != typename Layout<dyn>::mapping(exts_dyn, 7));
+
+    VERIFY(m2 == typename Layout<7>::mapping(exts_sta));
+    VERIFY(m2 == typename Layout<dyn>::mapping(exts_sta, 7));
+    VERIFY(m2 == typename Layout<7>::mapping(exts_dyn));
+    VERIFY(m2 == typename Layout<dyn>::mapping(exts_dyn, 7));
+
+    VERIFY(m2 != typename Layout<7>::mapping(exts_other));
+    VERIFY(m2 != typename Layout<dyn>::mapping(exts_other, 7));
+    return true;
+  }
+
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_required_span_size_overflow()
+  {
+    using Traits =
LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
+    using Extents = std::dextents<uint8_t, 2>;
+    auto exts = Traits::make_extents(Extents{64, 2});
+    auto strides = Traits::make_expected(std::array<uint8_t, 2>{1, 128});
+    auto ms = std::layout_stride::mapping(exts, strides);
+    auto m = typename Layout<dyn>::mapping<Extents>(ms);
+    VERIFY(is_same_mapping(m, ms));
+    VERIFY(m.required_span_size() == ms.required_span_size());
+    return true;
+  }
+
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_all()
+  {
+    test_representable_padded_size<std::layout_left_padded>();
+    test_default_ctor<Layout>();
+    test_from_exts<Layout>();
+    test_from_stride<Layout>();
+    test_from_samepad<Layout>();
+    test_from_same<Layout>();
+    test_from_other<Layout>();
+    test_to_same<Layout>();
+    test_never_to_other<Layout>();
+    test_strides_all<Layout>();
+    test_exhaustive<Layout>();
+    test_op_eq<Layout>();
+    test_required_span_size_overflow<Layout>();
+    return true;
+  }
+
+int
+main()
+{
+  test_all<std::layout_left_padded>();
+  static_assert(test_all<std::layout_left_padded>());
+
+  test_from_pad_all<std::layout_left_padded>();
+  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..29f12aa4de2
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_neg.cc
@@ -0,0 +1,324 @@
+// { dg-do compile { target c++26 } }
+#include <mdspan>
+
+#include "padded_traits.h"
+#include <cstdint>
+
+constexpr size_t dyn = std::dynamic_extent;
+
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_from_extens_representable_sta()
+  {
+    using E1 = std::extents<uint8_t, 8, 128>;
+    auto m = typename Layout<dyn>::mapping(E1{}); // { dg-error "required
from" }
+    return true;
+  }
+static_assert(test_from_extens_representable_sta<std::layout_left_padded>());
// { dg-error "from here" }
+
+template<template<size_t> typename Layout>
+  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 = typename Layout<dyn>::mapping(E2{E1{}}); // { dg-error
"expansion of" }
+    (void) m;
+    return true;
+  }
+static_assert(test_from_extents_representable_padded_size<std::layout_left_padded>());
// { dg-error "expansion of" }
+
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_from_extents_representable_stride()
+  {
+    using Traits =
LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
+    using E1 = typename Traits::extents_type<std::extents<uint8_t, dyn,
1>>;
+    auto m = typename Layout<128>::mapping(E1{129}); // { dg-error
"expansion of" }
+    (void) m;
+    return true;
+  }
+static_assert(test_from_extents_representable_stride<std::layout_left_padded>());
// { dg-error "expansion of" }
+
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_from_pad_representable_stride()
+  {
+    using Traits =
LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
+    auto exts = Traits::make_extents(std::dextents<uint8_t, 2>(129, 2));
+    auto m = typename Layout<dyn>::mapping(exts, 128); // { dg-error
"expansion of" }
+    return true;
+  }
+static_assert(test_from_pad_representable_stride<std::layout_left_padded>());
// { dg-error "expansion of" }
+
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_from_pad_representable_padded_size()
+  {
+    using Traits =
LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
+    auto exts = Traits::make_extents(std::dextents<uint8_t, 2>(64, 2));
+    auto m = typename Layout<dyn>::mapping(exts, 128); // { dg-error
"expansion of" }
+    return true;
+  }
+static_assert(test_from_pad_representable_padded_size<std::layout_left_padded>());
// { dg-error "expansion of" }
+
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_from_left()
+  {
+    using Traits =
LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
+    using LayoutSame = typename Traits::layout_same;
+    auto exts = Traits::make_extents(std::extents<uint8_t, 6, dyn>{4});
+    auto ml = typename LayoutSame::mapping(exts);
+
+    typename Layout<4>::mapping<decltype(exts)> m(ml); // { dg-error
"expansion of" }
+    return true;
+  }
+static_assert(test_from_left<std::layout_left_padded>()); // { dg-error
"required from here" }
+
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_from_left_bad_runtime_stride()
+  {
+    using Traits =
LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
+    auto exts = Traits::make_extents(std::extents<uint8_t, dyn, dyn>{6,
4});
+    auto ml = typename Traits::layout_same::mapping(exts);
+
+    typename Layout<4>::mapping<decltype(exts)> m(ml); // { dg-error
"expansion of" }
+    (void) m;
+    return true;
+  }
+static_assert(test_from_left_bad_runtime_stride<std::layout_left_padded>());
// { dg-error "expansion of" }
+
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_from_left_representable_extents()
+  {
+    using Traits =
LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
+    auto exts = Traits::make_extents(std::extents<uint16_t, dyn, dyn>{8,
128});
+    auto ml = typename Traits::layout_same::mapping(exts);
+
+    typename Layout<8>::mapping<std::extents<uint8_t, dyn, dyn>> m(ml);
// { dg-error "expansion of" }
+    return true;
+  }
+static_assert(test_from_left_representable_extents<std::layout_left_padded>());
// { dg-error "expansion of" }
+
+template<template<size_t> typename Layout, 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 Layout<PaddingValue>::mapping(exts, n);
+    (void) m;
+    return true;
+  }
+static_assert(test_pad_overflow<std::layout_left_padded, 1>());   // {
dg-error "expansion of" }
+static_assert(test_pad_overflow<std::layout_left_padded, dyn>()); // {
dg-error "expansion of" }
+
+template<template<size_t> typename Layout, size_t PaddingValue>
+  constexpr bool
+  test_from_pad_negative()
+  {
+    auto exts = std::extents(4);
+    auto m = typename Layout<PaddingValue>::mapping(exts, -1);
+    (void) m;
+    return true;
+  }
+static_assert(test_from_pad_negative<std::layout_left_padded, 1>());   //
{ dg-error "expansion of" }
+static_assert(test_from_pad_negative<std::layout_left_padded, dyn>()); //
{ dg-error "expansion of" }
+
+template<template<size_t> typename Layout, size_t Pad>
+  constexpr bool
+  test_static_pad_same()
+  {
+    using Extents = std::extents<int, dyn>;
+    using Mapping = typename Layout<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<std::layout_left_padded, 1>()); // {
dg-error "expansion of" }
+static_assert(test_static_pad_same<std::layout_left_padded, 3>()); // {
dg-error "expansion of" }
+
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_from_stride_wrong_stride0()
+  {
+    using Traits =
LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
+    auto e = Traits::make_extents(std::extents{3, 5});
+    auto s = Traits::make_expected(std::array<int, 2>{2, 7});
+    auto ms = std::layout_stride::mapping(e, s);
+    auto m = typename Layout<dyn>::mapping<decltype(e)>(ms); // {
dg-error "expansion of" }
+    (void) m;
+    return true;
+  }
+static_assert(test_from_stride_wrong_stride0<std::layout_left_padded>());
// { dg-error "expansion of" }
+
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_from_stride_wrong_stride1()
+  {
+    using Traits =
LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
+    auto e = Traits::make_extents(std::extents(3, 5));
+    auto s = Traits::make_expected(std::array<int, 2>{1, 3});
+    auto ms = std::layout_stride::mapping(e, s);
+    auto m = typename Layout<2>::mapping<decltype(e)>(ms); // { dg-error
"expansion of" }
+    (void) m;
+    return true;
+  }
+static_assert(test_from_stride_wrong_stride1<std::layout_left_padded>());
// { dg-error "expansion of" }
+
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_from_stride_wrong_stride2()
+  {
+    using Traits =
LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
+    auto e = Traits::make_extents(std::extents(3, 5, 7));
+    auto s = Traits::make_expected(std::array<int, 3>{1, 4, 21});
+    auto ms = std::layout_stride::mapping(e, s);
+    auto m = typename Layout<dyn>::mapping<decltype(e)>(ms); // here (not
implemented)
+    (void) m;
+    return true;
+  }
+static_assert(test_from_stride_wrong_stride2<std::layout_left_padded>());
+
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_from_stride_oversized()
+  {
+    using Traits =
LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
+    auto exts = Traits::make_extents(std::extents<uint16_t, dyn, dyn>{3,
6});
+    auto s = Traits::make_expected(std::array<uint16_t, 2>{1, 128});
+    auto ms = std::layout_stride::mapping(exts, s);
+
+    using Mapping = typename Layout<dyn>::mapping<std::dextents<uint8_t,
2>>;
+    Mapping m(ms); // { dg-error "expansion of" }
+    (void) m;
+    return true;
+  }
+static_assert(test_from_stride_oversized<std::layout_left_padded>()); //
{ dg-error "expansion of" }
+
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_from_samepad_dyn()
+  {
+    using Traits =
LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
+    auto e = Traits::make_extents(std::extents(3, 5));
+    auto mlp = typename Layout<dyn>::mapping(e);
+    auto m = typename Layout<2>::mapping<decltype(e)>(mlp); // { dg-error
"expansion of" }
+    (void) m;
+    return true;
+  }
+static_assert(test_from_samepad_dyn<std::layout_left_padded>()); // {
dg-error "expansion of" }
+
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_from_samepad_sta()
+  {
+    using Traits =
LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
+    auto e = Traits::make_extents(std::extents{3, 5});
+    auto mlp = typename Layout<3>::mapping(e);
+    auto m = typename Layout<2>::mapping<decltype(e)>(mlp); // { dg-error
"expansion of" }
+    (void) m;
+    return true;
+  }
+static_assert(test_from_samepad_sta<std::layout_left_padded>()); // {
dg-error "expansion of" }
+
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_from_samepad_oversized()
+  {
+    using Traits =
LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
+    using E1 = typename Traits::extents_type<std::extents<uint16_t, 8,
128>>;
+    using E2 = typename Traits::extents_type<std::extents<uint8_t, dyn,
dyn>>;
+    auto mlp = typename Layout<dyn>::mapping<E1>(E1{});
+    auto m = typename Layout<dyn>::mapping<E2>(mlp); // { dg-error
"expansion of" }
+    (void) m;
+    return true;
+  }
+static_assert(test_from_samepad_oversized<std::layout_left_padded>()); //
{ dg-error "expansion of" }
+
+template<template<size_t> typename Layout, size_t RunId>
+  constexpr bool
+  test_to_same_not_exhaustive()
+  {
+    using Traits =
LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
+    using E1 = typename Traits::extents_type<std::extents<int, 6, 5>>;
+    using E2 = typename Traits::extents_type<std::extents<int, dyn, 5>>;
+
+    [[maybe_unused]] auto msta = typename Layout<7>::mapping(E1{});
+    if constexpr (RunId == 0)
+      {
+       auto m = typename Traits::layout_same::mapping<E1>(msta); // {
dg-error "required from" }
+       (void) m;
+      }
+    if constexpr (RunId == 1)
+      {
+       auto m = typename Traits::layout_same::mapping<E2>(msta); // {
dg-error "expansion of" }
+       (void) m;
+      }
+
+    [[maybe_unused]] auto mdyn = typename Layout<dyn>::mapping(E2{E1{}},
7);
+    if constexpr (RunId == 2)
+      {
+       auto m = typename Traits::layout_same::mapping<E1>(mdyn); // {
dg-error "expansion of" }
+       (void) m;
+      }
+    if constexpr (RunId == 3)
+      {
+       auto m = typename Traits::layout_same::mapping<E2>(mdyn); // {
dg-error "expansion of" }
+       (void) m;
+      }
+    return true;
+  }
+static_assert(test_to_same_not_exhaustive<std::layout_left_padded, 0>());
// { dg-error "expansion of" }
+static_assert(test_to_same_not_exhaustive<std::layout_left_padded, 1>());
// { dg-error "expansion of" }
+static_assert(test_to_same_not_exhaustive<std::layout_left_padded, 2>());
// { dg-error "expansion of" }
+static_assert(test_to_same_not_exhaustive<std::layout_left_padded, 3>());
// { dg-error "expansion of" }
+
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_statically_bad_padding_value1()
+  {
+    using Traits =
LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
+    constexpr auto N = std::numeric_limits<size_t>::max() - 1;
+    using Extents = typename Traits::extents_type<std::extents<size_t, N,
0>>;
+    typename Layout<10>::mapping<Extents> m; // { dg-error "required
from" }
+    return true;
+  }
+static_assert(test_statically_bad_padding_value1<std::layout_left_padded>());
// { dg-error "required from" }
+
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_statically_bad_padding_value2()
+  {
+    using Traits =
LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
+    using Extents = typename Traits::extents_type<std::extents<uint8_t,
255, 0>>;
+    typename Layout<2>::mapping<Extents> m; // { dg-error "required from"
}
+    return true;
+  }
+static_assert(test_statically_bad_padding_value2<std::layout_left_padded>());
// { dg-error "required from" }
+
+template<template<size_t> typename Layout>
+  constexpr bool
+  test_statically_oversized()
+  {
+    using Traits =
LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
+    using Extents = typename Traits::extents_type<std::extents<uint8_t,
127, 2>>;
+    typename Layout<2>::mapping<Extents> m; // { dg-error "required from"
}
+    return true;
+  }
+static_assert(test_statically_oversized<std::layout_left_padded>()); // {
dg-error "from here" }
+
+// { 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" }
diff --git
a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_traits.h
b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_traits.h
new file mode 100644
index 00000000000..1f0169d7c02
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_traits.h
@@ -0,0 +1,63 @@
+#ifndef TEST_MDSPAN_PADDED_TRAITS_H
+#define TEST_MDSPAN_PADDED_TRAITS_H
+
+#if __glibcxx_padded_layouts
+enum class PaddingSide
+{
+  Left
+};
+
+template<typename Layout>
+  constexpr static bool is_left_padded = false;
+
+template<size_t PaddingValue>
+  constexpr static bool
is_left_padded<std::layout_left_padded<PaddingValue>> = true;
+
+struct DeducePaddingSide
+{
+  template<template<size_t> typename Layout>
+    constexpr static PaddingSide
+    from_template()
+    { return PaddingSide::Left; }
+
+  template<typename Layout>
+    constexpr static PaddingSide
+    from_typename()
+    { return PaddingSide::Left; }
+};
+
+template<PaddingSide Side>
+  struct LayoutTraits;
+
+template<>
+  struct LayoutTraits<PaddingSide::Left>
+  {
+    using layout_same = std::layout_left;
+    using layout_other = std::layout_right;
+
+    template<typename Extents>
+      using extents_type = Extents;
+
+    template<typename Extents>
+      constexpr static extents_type<Extents>
+      make_extents(const Extents& exts)
+      { return exts; }
+
+    template<typename T, size_t N>
+      constexpr static std::array<T, N>
+      make_expected(const std::array<T, N>& expected)
+      { return expected; }
+
+    template<typename Mapping>
+      constexpr static auto
+      padded_stride(const Mapping& m)
+      { return m.stride(1); }
+
+    template<typename Extents>
+      constexpr static auto
+      padded_extent(const Extents& exts)
+      { return exts.extent(0); }
+  };
+
+#endif
+#endif
--
2.50.1




Reply via email to