On 9/26/25 2:22 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.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]>
---
  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.
+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 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));
+       }
+#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)
+      {
+       // 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;
+
+       template<typename _Mapping>
+         constexpr static bool _S_is_same_padded_mapping =
+         __is_left_padded_mapping<_Mapping>;
+
+       template<typename _Mapping>
+         constexpr static bool _S_is_other_padded_mapping =
+         __is_right_padded_mapping<_Mapping>;
+
+       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));
+
+       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");
+
+       constexpr
+       _PaddedStorage() noexcept
+       {
+         if constexpr (_S_rank > 0)
+           {
+             constexpr size_t __sta_ext = _S_static_padded_extent();
+             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();
+
+             if constexpr (_PaddingValue != dynamic_extent)
+               __glibcxx_assert(
+                   __is_representable_least_multiple<_IndexType>(
+                     _PaddingValue, __dyn_pad_ext));
+
+             _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>
+         constexpr
+         _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>
+         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
+               || _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>
+         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 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>
+         constexpr explicit
+         _PaddedStorage(const _SamePaddedMapping& __other)
+         : _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;
+             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>
+         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
+       { return _Extents::static_extent(_LayoutTraits::_S_ext_idx); }
+
+       constexpr _IndexType
+       _M_dynamic_padded_extent() const noexcept
+       { return _M_extents.extent(_LayoutTraits::_S_ext_idx); }
+
+       constexpr size_t
+       _M_dynamic_padded_stride() const noexcept
+       { 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()));
+       }
+
+       using _Stride = std::extents<_IndexType, _S_static_stride>;
+       [[no_unique_address]] _Stride _M_stride;
+       [[no_unique_address]] _Extents _M_extents{};

We do not need braces here, otherwise we would need them on _M_stride.


The argument being, that because the default ctor is implemented
by calling mapping(extents_type{}) we can be sure they'll always
get intialized?

The standard has it with:
https://eel.is/c++draft/mdspan.layout.leftpad.overview#1

+      };
+  }
+
+  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;
+
+       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)
+         { }
+
+       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)
+         { }
+
+       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
+template<typename Layout>
+  constexpr bool
+  is_padded_layout = is_left_padded<Layout>;
+#else
+template<typename>
+  constexpr bool
+  is_padded_layout = 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>

+
  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