On 9/9/25 12:00 PM, Tomasz Kaminski wrote:
On Mon, Sep 8, 2025 at 10:14 PM Luc Grosheintz <luc.groshei...@gmail.com>
wrote:

I decided to deviate once (described in detail in the commit message).
Without this deviation writing tests is very clumsy and sometimes not
possible, e.g. when mixing uint8_t and dynamic padding values to check
mandates w.r.t. representability. Previously, we discussed other ways of
fixing the issue. The reason I chose this version is because I believe
it's the smallest deviation from the standard; and we can deviate
differently in followup commits.

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

The tests contain a function:

     constexpr bool
     is_same_mapping(const auto& lhs, const auto& rhs)
       // ...
       for (size_t i = 0; i < lhs.extents().rank(); ++i)
         if (lhs.stride(i) != rhs.stride(i))
           return false;
       return true;
     }

The above is accidentally quadratic, because computing all strides can
be done in linear time, but not if one computes them one by one, then
it's quadratic. It's annoyingly hard to do in linear time, because it
requires both precise knowledge of the definition of `stride` for each
layout; and ideally it also requires access to implementation details
related to how we separate static and dynamic extents to be able to
compute static products at compile time.

Do we need a member strides() for all strided layout mappings to avoid
the issue?

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

The left-padded conversion rules are unexpected in two ways:

1. It allows conversion between mappings that have extent_types that
aren't convertable (because it's not guaranteed at compile-time that the
conversion is safe). AFAICT, this is covered by LWG4272 [1].
[1]: https://cplusplus.github.io/LWG/issue4272

2. The rules for converting between left-padded layouts with different
padding values is different. For extents the rule is to allow sta ->
sta, sta -> dyn and dyn -> dyn; but prevent dyn -> sta (because it can't
be guaranteed at compile-time that the dynamic padding values matches
the dynamic padding value match). For padding values we also disallow
conversion for dyn -> dyn.

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

If the padding value is statically 0 or 1, then it's effectively never
padded, because:

     least_multiple_as_least(padding_value, extent(0)) == extent(0)

either because of a special case (for padding_value == 0), or because `n
% 1 == 0` and `n / 1 == n` (for padding_value == 1). Put differently,
it's always exhaustive. However, is_always_exhaustive isn't true if
exts.extent(0) is a dynamic extent. Even though that has no impact for
padding_value <= 1.

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

It's not possible to round-trip: layout_right -> layout_left_padded ->
layout_right even for rank <= 1 where usually the difference between
different mappings is ignored.

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

There's similar problems with operator== as noted before; and for which
I promised to (and never did) report a non-editorial issue.

--- 8< ---

This commit adds a new layout layout_left_padded as standardized in
N5014 but with one deviation. This includes checking of mandates and
prerequisites, with exception of "slow" prerequisites, i.e. those that
requires a for loop. It adds the feature testing macro submdspan as a
purely internal macro and registers layout_left_padded in the std
module.

The standard mandates that the padding_value is representable as
index_type. However, padding_value can be dynamic_extent which is
defined as `size_t(-1)`. Hence, it's impossible to use a dynamic
padding_value if index_type is int. The deviation is to only require
static padding values to be representable as index_type; and thereby
allow dynamic padding values for every index_type.

libstdc++-v3/ChangeLog:

         * include/bits/version.def (submdspan): Add as internal
         only.
         * include/bits/version.h: Regenerate.
         * include/std/mdspan (__fwd_prod): New overload.
         (layout_left_padded): Add declaration and implementation.
         (layout_right_padded): Add declaration only.
         (__is_left_padded_mapping): New concept.
         (__is_right_padded_mapping): Ditto.
         (__standardized_mapping): Recognize left and right padded
         mappings.
         (layout_left::mapping::mapping): New overload for left
         padded mappings.
         * src/c++23/std.cc.in (layout_left_padded): Add.
         * testsuite/23_containers/mdspan/layouts/ctors.cc: Add tests for
         layout_left_padded.
         * testsuite/23_containers/mdspan/layouts/empty.cc: Ditto.
         * testsuite/23_containers/mdspan/layouts/mapping.cc: Ditto.
         * testsuite/23_containers/mdspan/layouts/debug/padded_neg.cc: New
test.
         * testsuite/23_containers/mdspan/layouts/padded.cc: New test.
         * testsuite/23_containers/mdspan/layouts/padded_neg.cc: New test.

Signed-off-by: Luc Grosheintz <luc.groshei...@gmail.com>

Thanks for the patch. I have looked through the implementation, and left a
few suggestions there.
After merging this, I would like to look if we can merge the functions for
indexing and producs somehow with
the non-padded facilities. My general idea would be passing an stride there.

---
  libstdc++-v3/include/bits/version.def         |   9 +
  libstdc++-v3/include/bits/version.h           |   9 +
  libstdc++-v3/include/std/mdspan               | 416 +++++++++++++-
  libstdc++-v3/src/c++23/std.cc.in              |   8 +-
  .../23_containers/mdspan/layouts/ctors.cc     |  61 +-
  .../mdspan/layouts/debug/padded_neg.cc        |   3 +
  .../23_containers/mdspan/layouts/empty.cc     |  10 +-
  .../23_containers/mdspan/layouts/mapping.cc   | 198 +++++--
  .../23_containers/mdspan/layouts/padded.cc    | 537 ++++++++++++++++++
  .../mdspan/layouts/padded_neg.cc              | 225 ++++++++
  10 files changed, 1414 insertions(+), 62 deletions(-)
  create mode 100644
libstdc++-v3/testsuite/23_containers/mdspan/layouts/debug/padded_neg.cc
  create mode 100644
libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc
  create mode 100644
libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_neg.cc

diff --git a/libstdc++-v3/include/bits/version.def
b/libstdc++-v3/include/bits/version.def
index 65b9a278776..1895078210a 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -1050,6 +1050,15 @@ ftms = {
      cxxmin = 26;
      extra_cond = "__glibcxx_assume_aligned "
      "&& __glibcxx_is_sufficiently_aligned";
+    };
+};
+
+ftms = {
+  name = submdspan;

The fact that this uses a submdspan macro is very unfortunate.
I think instead of using submdspan, I would add an internal  no_stdname
padded_layouts macro,
that will keep using, even after defining submdspan.



+  no_stdname = true; // FIXME: remove
+  values = {
+    v = 202306;

Also use value 1 here, and add TODO to update.

+    cxxmin = 26;
    };
  };

diff --git a/libstdc++-v3/include/bits/version.h
b/libstdc++-v3/include/bits/version.h
index b05249857d2..c6de494da08 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -1178,6 +1178,15 @@
  #endif /* !defined(__cpp_lib_aligned_accessor) &&
defined(__glibcxx_want_aligned_accessor) */
  #undef __glibcxx_want_aligned_accessor

+#if !defined(__cpp_lib_submdspan)
+# if (__cplusplus >  202302L)
+#  define __glibcxx_submdspan 202306L
+#  if defined(__glibcxx_want_all) || defined(__glibcxx_want_submdspan)
+#  endif
+# endif
+#endif /* !defined(__cpp_lib_submdspan) &&
defined(__glibcxx_want_submdspan) */
+#undef __glibcxx_want_submdspan
+
  #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 678b2619ebe..1270640ab6b 100644
--- a/libstdc++-v3/include/std/mdspan
+++ b/libstdc++-v3/include/std/mdspan
@@ -44,6 +44,7 @@

  #define __glibcxx_want_mdspan
  #define __glibcxx_want_aligned_accessor
+#define __glibcxx_want_submdspan
  #include <bits/version.h>

  #ifdef __glibcxx_mdspan
@@ -476,6 +477,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
+      {

For the future (not in this patch), I would like us to experiment in
defining __fwd_prod as follows:
template<typename _Extents, size_t __r)
__fwd_prod(const _Extents& __exts, size_t __stride1, size_t __r) noexcept
{
      if constexpr (__rank == 1)


         return 1;
      else if (__r == 0)
         return 1;
      else if constexpr (__rank == 2)
         return __stride1;
      else if (__r == 1)
          return __stride1;
      else if constexpr (__all_dynamic(std::span(__sta_exts).substr(1,
__rank-1)))

         return __stride1 * __extents_prod(__exts, 1, 1, __r);
      else
        {
          size_t __sta_prod = __fwd_partial_prods<__sta_exts>[__r-2];


          return __stride1 * __extents_prod(__exts, __sta_prod, 1, __r);


        }
}
Then reduce __fwd_partial_prods to contain multiplication without the first
two dimensions.
Then we call it passing extent(1) or __stride1. We could also mark as
__always_inline__.




+       size_t __sta_prod = [__begin, __end] {
+         span<const size_t> __sta_exts = __static_extents<_Extents>();
+         size_t __ret = 1;
+         for(auto __ext : __sta_exts.subspan(__begin, __end - __begin))
+           if (__ext != dynamic_extent)
+             __ret *= __ext;
+         return __ret;
+       }();
+       return __extents_prod(__exts, __sta_prod, __begin, __end);
+      }
+
      template<typename _Extents>
        constexpr typename _Extents::index_type
        __fwd_prod(const _Extents& __exts, size_t __r) noexcept
@@ -567,6 +583,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        class mapping;
    };

+#ifdef __glibcxx_submdspan
+  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>
@@ -660,10 +692,42 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
         is_same_v<typename _Layout::template mapping<typename
_Mapping::extents_type>,
                   _Mapping>;

+    template<template<size_t> typename _Layout, typename _Mapping>
+      concept __is_padded_mapping_of = requires
I think this could be equialy implemented as:
        = __mapping_of<_Layout<_Mapping::padding_value>, _Mapping>;
+      {
+       typename _Mapping::extents_type;
+       { _Mapping::padding_value } -> same_as<const size_t&>;
+      }
+      && same_as<_Mapping, typename
_Layout<_Mapping::padding_value>::mapping<
+         typename _Mapping::extents_type>>;
+
+#ifdef __glibcxx_submdspan
+    template<typename _Mapping>
+      constexpr bool __is_left_padded_mapping = __is_padded_mapping_of<
+       layout_left_padded, _Mapping>;
+
+    template<typename _Mapping>
+      constexpr bool __is_right_padded_mapping = __is_padded_mapping_of<
+       layout_right_padded, _Mapping>;
+#else

I would remove  this definitions.

+    template<typename _Mapping>
+      constexpr bool __is_left_padded_mapping = false;
+
+    template<typename _Mapping>
+      constexpr bool __is_right_padded_mapping = false;
+#endif
+
+    template<typename _PaddedMapping>
+      constexpr size_t

  This can be consteval.

+      __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>
+                                      ||
__is_left_padded_mapping<_Mapping>
+                                      ||
__is_right_padded_mapping<_Mapping>;

And I would add if def here:
       template<typename _Mapping>
        concept __standardized_mapping = __mapping_of<layout_left, _Mapping>
                                        || __mapping_of<layout_right,
_Mapping>
-                                      || __mapping_of<layout_stride,
_Mapping>
#ifdef __glibcxx_submdspan
__is_padded_mapping_of<layout_left_padded, _Mapping>
__is_padded_mapping_of<layout_right_padded, _Mapping>
#endif __glibcxx_submdspan
            ; // semicolon


      // A tag type to create internal ctors.
      class __internal_ctor
@@ -717,6 +781,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
         : mapping(__other.extents(), __mdspan::__internal_ctor{})
         { __glibcxx_assert(*this == __other); }

This should be under  __glibcxx_submdspan macro.

+      template<class _LeftPaddedMapping>
+       requires __mdspan::__is_left_padded_mapping<_LeftPaddedMapping>
+                && is_constructible_v<extents_type,
+                                      typename
_LeftPaddedMapping::extents_type>
+       constexpr
+       explicit(!is_convertible_v<typename
_LeftPaddedMapping::extents_type,
+                                  extents_type>)
+       mapping(const _LeftPaddedMapping& __other) noexcept
+       : mapping(__other.extents(), __mdspan::__internal_ctor{})
+       {
+         constexpr size_t __ostride_sta = __mdspan::__get_static_stride<
+           _LeftPaddedMapping>();
+
+         if constexpr (extents_type::rank() > 1
+             && extents_type::static_extent(0) != dynamic_extent
+             && __ostride_sta != dynamic_extent)
+           static_assert(extents_type::static_extent(0) == __ostride_sta);
+
+         __glibcxx_assert(extents_type::rank() <= 1
+           || __other.stride(1) == __other.extents().extent(0));

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

@@ -1164,6 +1250,334 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
        [[no_unique_address]] _S_strides_t _M_strides;
      };

+#ifdef __glibcxx_submdspan
+  namespace __mdspan
+  {
+    template<typename _Io, typename _I1, typename _I2>
+      constexpr _Io

I would use size_t here, see below.

+      __least_multiple_at_least(_I1 __x, _I2 __y)
+      {
+       if (__x == 0)
+         return static_cast<_Io>(__y);
+       return static_cast<_Io>((__y / __x + (__y % __x != 0)) * __x);

I think this could be implemented as:
     ((__y + __x - 1) / __x) * __x;

This was the first version; but while reading io_uring examples,
this variant kept coming up. IIUC, the problem is overflow.

For example assume `size_t` is uint8_t, i.e. 255 is the largest
representable value. Then,

  __least_multiple_at_least(5, 254)
  == (254 + 5 - 1) / 5 * 5
  == (258) / 5 * 5
  == 2 / 5 * 5
  == 0

whereas we'd like it to be 255. I think the trick is sound
if __x were a power of two; but not for other values of __x.
Good news, if the compiler knows __x is a power of two, then
division and modulo are cheap (i.e. a shift and mask, respectively).


+      }
+
+    template<typename _Extents, typename _Stride, typename... _Indices>
+      constexpr typename _Extents::index_type
+      __linear_index_leftpad(const _Extents& __exts, _Stride __stride,
+                            _Indices... __indices)
+      {
+       // i0 + stride*(i1 + extents.extent(1)*...)
+       using _IndexType = typename _Extents::index_type;
+       _IndexType __res = 0;

+       if constexpr (sizeof...(__indices) > 0)
+         {
+           _IndexType __mult = 1;
+
+           auto __update_rest = [&, __pos = 1u](_IndexType __idx) mutable
+             {
+               __res += __idx * __mult;
+               __mult *= __exts.extent(__pos);
+               ++__pos;
+             };
+
+           auto __update = [&](_IndexType __idx, auto... __rest)
+             {
+               __res += __idx;
+               __mult = __stride.extent(0);
+               (__update_rest(__rest), ...);
+             };
+           __update(__indices...);
+         }
+       return __res;
+      }
+  }
+
+  template<size_t _PaddingValue>
+    template<typename _Extents>
+      class layout_left_padded<_PaddingValue>::mapping
+      {
+      public:
+       static constexpr size_t padding_value = _PaddingValue;
+
+       using extents_type = _Extents;
+       using index_type = typename extents_type::index_type;
+       using size_type = typename extents_type::size_type;
+       using rank_type = typename extents_type::rank_type;
+       using layout_type = layout_left_padded<padding_value>;
+
+       static_assert(__mdspan::__representable_size<extents_type,
index_type>,
+         "The size of extents_type must be representable as index_type");
+
+       // DEVIATION the standard requires that _PaddingValue is
representable
+       // as index_type. This seems unlikely, because if index_type <
size_t
+       // then the mandate is violated for `padding_value ==
dynamic_extent`.
+       // Which means that padding_value == dynamic_extent only reliably
work
+       // if index_type == size_t (e.g. it fails for index_type == int on
a
+       // 64bit system).
+       static_assert((padding_value == dynamic_extent)
+           || (padding_value <= numeric_limits<index_type>::max()),
+         "padding_value must be representable as index_type");



We are missing this mandate from here:
https://eel.is/c++draft/mdspan.layout#leftpad.overview-5.3
https://eel.is/c++draft/mdspan.layout#leftpad.overview-5.4.
Because the static_stride is always greater or equal extent, I would
suggest adding start index
to static quotient, and having:
__static_quotient<_Extents, _IndexType>(1) / static_stride().
Of cource with check for contains_zero etc.


Thank you! Somehow I thought these were super hard, but they're
not :) There's many more of those, in form of pre-requisites,
I did those too.


+
+      private:
+       static constexpr size_t _S_rank = _Extents::rank();
+       static constexpr size_t _S_static_stride = [] consteval
+       {
+         if constexpr (_S_rank <= 1)
+           return size_t(0);
+         else if constexpr (padding_value == dynamic_extent ||
+                       _Extents::static_extent(0) == dynamic_extent)
+           return dynamic_extent;
+         else
+           return
__mdspan::__least_multiple_at_least<size_t>(padding_value,
+             size_t(_Extents::static_extent(0)));
+       }();
+
+       constexpr friend size_t
+       __mdspan::__get_static_stride<mapping>();
+
+      public:
+       constexpr
+       mapping() noexcept
+       : mapping(extents_type{})
+       { }
+
+       constexpr
+       mapping(const mapping&) noexcept = default;
+
+       constexpr
+       mapping(const extents_type& __exts)
+       : _M_extents(__exts)
+       {
+
  __glibcxx_assert(__mdspan::__is_representable_extents(_M_extents));
+         if (_S_rank > 1)
+           {
+             index_type __stride;
+             if constexpr (padding_value == dynamic_extent)
+               __stride = __exts.extent(0);
+             else

We should have another if constexpr branch, where _S_static_stride is not
equal dynamic_extent,
then we do not need to do runtime computation:
                   if constexpr(_S_static_stride != dynamic_extent)
                         return
                   else
                          { .... }


+               __stride = __mdspan::__least_multiple_at_least<index_type>(
+                 padding_value, __exts.extent(0));

Check if this value can fit.

+
+             _M_stride = std::extents<index_type,
_S_static_stride>{__stride};

Use _S_stride_type here.

+           }
+       }
+
+       template<__mdspan::__valid_index_type<index_type> _OIndexType>
+         constexpr mapping(const extents_type& __exts, _OIndexType __opad)
+         : _M_extents(__exts)
+         {
+           if constexpr (std::is_integral_v<_OIndexType>)
+             {
+               __glibcxx_assert(cmp_less_equal(__opad,
+                   numeric_limits<index_type>::max()));
+               if constexpr (std::is_signed_v<_OIndexType>)
+                 __glibcxx_assert(__opad >= 0);
+             }
+
+           auto __pad = static_cast<index_type>(std::move(__opad));
+           if constexpr (std::is_signed_v<index_type>)
+             __glibcxx_assert(__pad >= 0);
+           if constexpr (padding_value != dynamic_extent)
+             __glibcxx_assert(cmp_equal(padding_value, __pad));
+           if constexpr (_S_rank > 1)

This should be combined with  _S_static_stride != dynamic_extent, we do not
need to
assign _Stride_type in that case, and more importantly
compute __least_multiple_at_least..>
+             _M_stride = _S_stride_type{
+               __mdspan::__least_multiple_at_least<index_type>(__pad,
+                 __exts.extent(0))};
+         }
+
+       template<typename _OExtents>
+         requires is_constructible_v<extents_type, _OExtents>
+         constexpr explicit(!is_convertible_v<_OExtents, extents_type>)
+         mapping(const layout_left::mapping<_OExtents>& __other)
+         : mapping(extents_type(__other.extents()))
+         {
+           if constexpr (_OExtents::rank() > 1)
+             static_assert(_S_static_stride == dynamic_extent
+                 || _OExtents::static_extent(0) == dynamic_extent
+                 || _S_static_stride == _OExtents::static_extent(0),
+               "(*this).stride(1) must be compatible with
other.stride(1)");
+
+           // REVIEW: can this one go, because we check

Yeas, remove it.

+           //   __is_representable_extents in mapping(const mapping&)
+           __glibcxx_assert(cmp_less_equal(__other.required_span_size(),
+
  numeric_limits<index_type>::max()));
+           __glibcxx_assert((!(_S_rank > 1 && padding_value !=
dynamic_extent))
+             || (_M_stride.extent(0) == __other.extents().extent(0)));
+         }
+
+       template<typename _OExtents>
+         requires is_constructible_v<_OExtents, extents_type>
+         constexpr explicit(_OExtents::rank() > 0)
+         mapping(const typename layout_stride::mapping<_OExtents>&
__other)
+         : _M_extents(__other.extents())
+         {
+           if constexpr (_S_rank > 1 && padding_value != dynamic_extent)
+             __glibcxx_assert(cmp_equal(__other.stride(1),
+
  __mdspan::__least_multiple_at_least<index_type>(padding_value,
+                   __other.extents().extent(0))));

Replace this check is similar to the manner I have described below, using
strid(1),
or _M_stride.

Not sure, see below.


+
+           if constexpr (_S_rank > 0)
+             __glibcxx_assert(__other.stride(0) == 1);
+
+           __glibcxx_assert(cmp_less_equal(__other.required_span_size(),
+
  numeric_limits<index_type>::max()));
+
+           if constexpr (_S_rank > 1 && _S_static_stride ==
dynamic_extent)
+             _M_stride = _S_stride_type{__other.stride(1)};
+         }
+
+       template<typename _LeftPaddedMapping>
+         requires __mdspan::__is_left_padded_mapping<_LeftPaddedMapping>
+             && is_constructible_v<extents_type,
+                                   typename
_LeftPaddedMapping::extents_type>
+         constexpr explicit(_S_rank > 1 && (padding_value !=
dynamic_extent
+               || _LeftPaddedMapping::padding_value == dynamic_extent))
+         mapping(const _LeftPaddedMapping& __other)
+         : _M_extents(__other.extents())
+         {
+           if constexpr (_S_rank > 1)
+           {
+             static_assert(padding_value == dynamic_extent
+                 || _LeftPaddedMapping::padding_value == dynamic_extent
+                 || padding_value == _LeftPaddedMapping::padding_value,
+               "If neither padding_value is dynamic_extent, then they
must "
+               "be equal");
+
+             __glibcxx_assert((padding_value == dynamic_extent
+                 || cmp_equal(__other.stride(1),
+                      __mdspan::__least_multiple_at_least<index_type>(
+                        padding_value, __other.extents().extent(0))))
+               && "other.stride(1) must be the next larger multiple of "
+                  "padding_value that is greater or equal to
other.extent(0)");

I think this checks can be make conditionally as follows:
if constexpr (_S_static_stride != dynamic_extent)
      assert(cmp_equal(__other.stride(1), _S_static_stride)
else if constexpr (padding_value != dynamic_extent)
       asssert(cmp_equal(other.stride(1), this.stride(1));
And do them after initializing _M_stride.

I might be missing something, but it sounds like:

  _M_stride = other.stride(1);
  assert(other.stride(1) == _M_stride);

(which is trivially true, and doesn't assert internal consistency).


+           }
+           __glibcxx_assert(cmp_less_equal(__other.required_span_size(),
+               numeric_limits<index_type>::max()));
+
+           if constexpr (_S_rank > 1 && (_S_static_stride ==
dynamic_extent))
+             _M_stride = _S_stride_type{__other.stride(1)};
+         }
+
+       template<typename _RightPaddedMapping>
+         requires
(__mdspan::__is_right_padded_mapping<_RightPaddedMapping>
+             || __mdspan::__mapping_of<layout_right, _RightPaddedMapping>)
+           && (_S_rank <= 1)
+           && is_constructible_v<extents_type,
+                                 typename
_RightPaddedMapping::extents_type>
+         constexpr explicit(!is_convertible_v<
+             typename _RightPaddedMapping::extents_type, extents_type>)
+         mapping(const _RightPaddedMapping& __other) noexcept
+         : _M_extents(__other.extents())
+         {
+           __glibcxx_assert(cmp_less_equal(__other.required_span_size(),
+               numeric_limits<index_type>::max()));
+         }
+
+       constexpr mapping&
+       operator=(const mapping&) noexcept = default;
+
+       constexpr const extents_type&
+       extents() const noexcept { return _M_extents; }
+
+       constexpr array<index_type, _S_rank>
+       strides() const noexcept
+       {
+         array<index_type, _S_rank> __ret;
+         if constexpr (_S_rank > 0)
+           __ret[0] = 1;
+         if constexpr (_S_rank > 1)
+           __ret[1] = _M_stride.extent(0);
+         if constexpr (_S_rank > 2)
+           for(size_t __i = 2; __i < _S_rank; ++__i)
+             __ret[__i] = __ret[__i - 1] * _M_extents.extent(__i - 1);
+         return __ret;
+       }
+
+       constexpr index_type
+       required_span_size() const noexcept
+       {
+         if constexpr (_S_rank == 0)
+           return 1;
+         else if (__mdspan::__empty(_M_extents))
+           return 0;
+         else
+           return _M_stride.extent(0) * __mdspan::__rev_prod(_M_extents,
0)
+                  - (_M_stride.extent(0) - _M_extents.extent(0));

It took me some time to realize, but indeed this looks correct.


+       }
+
+       // _GLIBCXX_RESOLVE_LIB_DEFECTS
+       // 4314. Missing move in mdspan layout mapping::operator()
+       template<__mdspan::__valid_index_type<index_type>... _Indices>
+         requires (sizeof...(_Indices) == _S_rank)
+         constexpr index_type
+         operator()(_Indices... __indices) const noexcept
+         {
+           return __mdspan::__linear_index_leftpad(_M_extents, _M_stride,
+             static_cast<index_type>(std::move(__indices))...);
+         }
+
+       static constexpr bool
+       is_always_unique() noexcept { return true; }
+
+       static constexpr bool
+       is_always_exhaustive() noexcept
+       {
+         if constexpr (_S_rank <= 1)
+           return true;
+         else
+           return extents_type::static_extent(0) != dynamic_extent
+                  && _S_static_stride != dynamic_extent
+                  && extents_type::static_extent(0) == _S_static_stride;
+       }
+
+       static constexpr bool
+       is_always_strided() noexcept { return true; }
+
+       static constexpr bool
+       is_unique() noexcept { return true; }
+
+       constexpr bool
+       is_exhaustive() const noexcept
+       {
+         if constexpr (is_always_exhaustive())
+           return true;
+         else
+           return _M_extents.extent(0) == _M_stride.extent(0);
+       }
+
+       static constexpr bool
+       is_strided() noexcept { return true; }
+
+       constexpr index_type
+       stride(rank_type __r) const noexcept
+       {
+         __glibcxx_assert(__r < _S_rank);
+         if (__r == 0)
+           return 1;
+         else
+           return static_cast<index_type>(
+             static_cast<size_t>(_M_stride.extent(0)) *
+             static_cast<size_t>(__mdspan::__fwd_prod(_M_extents, 1,
__r)));
+       }
+
+       template<typename _LeftPaddedMapping>
+         requires(__mdspan::__is_left_padded_mapping<_LeftPaddedMapping>
+                  && _LeftPaddedMapping::extents_type::rank() == _S_rank)
+         friend constexpr bool
+         operator==(const mapping& __self, const _LeftPaddedMapping&
__other)
+         noexcept
+         {
+           return __self.extents() == __other.extents()
+             && (_S_rank < 2 || __self.stride(1) == __other.stride(1));
+         }
+
+      private:
+       using _S_stride_type = std::extents<index_type, _S_static_stride>;

Hmm, that interesting approach to storing stride. I was expecting
integral_constant
here, but this seems to also work ok.

It a tip in the standard itself :)


+       [[no_unique_address]] _S_stride_type _M_stride;
+       [[no_unique_address]] extents_type _M_extents{};
+      };
+#endif
+
    template<typename _ElementType>
      struct default_accessor
      {
diff --git a/libstdc++-v3/src/c++23/std.cc.in b/libstdc++-v3/src/c++23/
std.cc.in
index a217a87330b..c2854cb1828 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_submdspan
+  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/ctors.cc
b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
index 23c0a55dae1..290c53647cf 100644
--- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
@@ -6,6 +6,16 @@

  constexpr size_t dyn = std::dynamic_extent;

+template<typename Layout>
+  constexpr bool
+  is_layout_leftpad = false;
+
+#ifdef __glibcxx_submdspan
+template<size_t Pad>
+  constexpr bool
+  is_layout_leftpad<std::layout_left_padded<Pad>> = true;
+#endif
+
  template<typename Mapping, typename IndexType, size_t... Extents>
    constexpr void
    verify(std::extents<IndexType, Extents...> oexts)
@@ -27,7 +37,6 @@ template<typename Mapping, typename OMapping>
         VERIFY(std::cmp_equal(m.stride(i), other.stride(i)));
    }

-
  template<typename To, typename From>
    constexpr void
    verify_convertible(From from)
@@ -40,7 +49,10 @@ template<typename To, typename From>
    constexpr void
    verify_nothrow_convertible(From from)
    {
-    static_assert(std::is_nothrow_constructible_v<To, From>);
+    if constexpr (is_layout_leftpad<typename To::layout_type>)

I would prefer if particular test cases were adjusted, instead of having
generic
workaround for leftpad. The difference looks a bit meaningless otherwise.
Or, maybe create a separate patch, that's add noexcept (as extension) where
they would be needed to pass the test.

It's certainly clumsy, but the pattern is that anything that's C++23
is likely noexcept, while anything that was added in C++26 is likely
not noexcept. This approach is pretty explicit about it and avoid
repeating that logic thoughout the file.


Even if the first commit, I would make constructor from extents only
noexcept,
as it does not even have precondtion.

The ctor mapping(const extents_type&) has three preconditions:
https://eel.is/c++draft/mdspan.layout#leftpad.cons-1


+      static_assert(std::is_constructible_v<To, From>);
+    else
+      static_assert(std::is_nothrow_constructible_v<To, From>);
      verify_convertible<To>(from);
    }

@@ -57,7 +69,10 @@ template<typename To, typename From>
    constexpr void
    verify_nothrow_constructible(From from)
    {
-    static_assert(std::is_nothrow_constructible_v<To, From>);
+    if constexpr (is_layout_leftpad<typename To::layout_type>)
+      static_assert(std::is_constructible_v<To, From>);
+    else
+      static_assert(std::is_nothrow_constructible_v<To, From>);
      verify_constructible<To>(from);
    }

@@ -196,6 +211,16 @@ namespace from_extents
  // ctor: mapping(mapping<OExtents>)
  namespace from_same_layout
  {
+  template<typename Layout, typename Extents, typename OExtents>
+    constexpr void
+    verify_convertible(OExtents exts)
+    {
+      using Mapping = typename Layout::mapping<Extents>;
+      using OMapping = typename Layout::mapping<OExtents>;
+
+      ::verify_convertible<Mapping>(OMapping(exts));
+    }
+
    template<typename Layout, typename Extents, typename OExtents>
      constexpr void
      verify_nothrow_convertible(OExtents exts)
@@ -223,8 +248,12 @@ namespace from_same_layout
        verify_nothrow_convertible<Layout, std::extents<unsigned int>>(
         std::extents<int>{});

-      verify_nothrow_constructible<Layout, std::extents<int>>(
-       std::extents<unsigned int>{});
+      if constexpr (!is_layout_leftpad<Layout>)
+       verify_nothrow_constructible<Layout, std::extents<int>>(
+         std::extents<unsigned int>{});
+      else
+       verify_convertible<Layout, std::extents<int>>(
+         std::extents<unsigned int>{});

        assert_not_constructible<
         typename Layout::mapping<std::extents<int>>,
@@ -234,8 +263,12 @@ namespace from_same_layout
         typename Layout::mapping<std::extents<int, 1>>,
         typename Layout::mapping<std::extents<int>>>();

-      verify_nothrow_constructible<Layout, std::extents<int, 1>>(
-       std::extents<int, dyn>{1});
+      if constexpr (!is_layout_leftpad<Layout>)
+       verify_nothrow_constructible<Layout, std::extents<int, 1>>(
+         std::extents<int, dyn>{1});

+      else
+       verify_convertible<Layout, std::extents<int, 1>>(
+         std::extents<int, dyn>{1});

        verify_nothrow_convertible<Layout, std::extents<int, dyn>>(
         std::extents<int, 1>{});
@@ -247,8 +280,12 @@ namespace from_same_layout
        verify_nothrow_constructible<Layout, std::extents<int, 1, 2>>(
         std::extents<int, dyn, 2>{1});

-      verify_nothrow_convertible<Layout, std::extents<int, dyn, 2>>(
-       std::extents<int, 1, 2>{});
+      if constexpr (!is_layout_leftpad<Layout>)
+       verify_nothrow_convertible<Layout, std::extents<int, dyn, 2>>(
+         std::extents<int, 1, 2>{});
+      else
+       verify_nothrow_constructible<Layout, std::extents<int, dyn, 2>>(
+         std::extents<int, 1, 2>{});
        return true;
      }

@@ -429,6 +466,12 @@ main()
  {
    test_all<std::layout_left>();
    test_all<std::layout_right>();
+#ifdef __glibcxx_submdspan
+  test_all<std::layout_left_padded<0>>();
+  test_all<std::layout_left_padded<1>>();
+  test_all<std::layout_left_padded<2>>();
+  test_all<std::layout_left_padded<dyn>>();
+#endif

    from_left_or_right::test_all<std::layout_left, std::layout_right>();
    from_left_or_right::test_all<std::layout_right, std::layout_left>();
diff --git
a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/debug/padded_neg.cc
b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/debug/padded_neg.cc
new file mode 100644
index 00000000000..d4f9003fba6
--- /dev/null
+++
b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/debug/padded_neg.cc
@@ -0,0 +1,3 @@
+// { dg-do compile { target c++26 } }
+// { dg-require-debug-mode "" }
+#include<mdspan>

It seems that the test are missing here.


Sorry, this file shouldn't exist. There's no new debug assertions.


diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc
b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc
index 655b9b6d6c3..d1aff172b64 100644
--- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc
@@ -35,7 +35,8 @@ test_static_overflow()
  {
    constexpr Int n1 = std::numeric_limits<Int>::max();
    constexpr size_t n2 = std::dynamic_extent - 1;
-  constexpr size_t n = std::cmp_less(n1, n2) ? size_t(n1) : n2;
+  // Allow some room for padding.
+  constexpr size_t n = (std::cmp_less(n1, n2) ? size_t(n1) : n2) - 4;

    verify_all(typename Layout::mapping<std::extents<Int, n, n, 0, n,
n>>{});
    verify_all(typename Layout::mapping<std::extents<Int, 0, n, n, n>>{});
@@ -73,7 +74,8 @@ test_dynamic_overflow()
  {
    constexpr Int n1 = std::numeric_limits<Int>::max();
    constexpr size_t n2 = std::dynamic_extent - 1;
-  constexpr Int n = std::cmp_less(n1, n2) ? n1 : Int(n2);
+  // Allow some room for padding.
+  constexpr Int n = (std::cmp_less(n1, n2) ? n1 : Int(n2)) - 4;

    verify_all(make_mapping<Layout>(
        std::extents<Int, dyn, dyn, 0, dyn, dyn>{n, n, n, n}));
@@ -127,5 +129,9 @@ main()
    static_assert(test_all<std::layout_left>());
    static_assert(test_all<std::layout_right>());
    static_assert(test_all<std::layout_stride>());
+#ifdef __glibcxx_submdspan
+  static_assert(test_all<std::layout_left_padded<1>>());
+  static_assert(test_all<std::layout_left_padded<2>>());
+#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 58bce514435..3b9719eaf5d 100644
--- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc
@@ -7,6 +7,15 @@

  constexpr size_t dyn = std::dynamic_extent;

+template<typename Mapping>
+  concept has_static_is_exhaustive = requires
+  {
+    { Mapping::is_exhaustive() } -> std::same_as<bool>;
+  };
+

+static_assert(has_static_is_exhaustive<std::layout_right::mapping<std::extents<int>>>);

+static_assert(!has_static_is_exhaustive<std::layout_stride::mapping<std::extents<int>>>);
+
  template<typename Layout, typename Extents>
    constexpr bool
    test_mapping_properties()
@@ -32,7 +41,7 @@ template<typename Layout, typename Extents>

      static_assert(M::is_always_unique() && M::is_unique());
      static_assert(M::is_always_strided() && M::is_strided());
-    if constexpr (!std::is_same_v<Layout, std::layout_stride>)
+    if constexpr (has_static_is_exhaustive<M>)
        static_assert(M::is_always_exhaustive() && M::is_exhaustive());
      return true;
    }
@@ -306,7 +315,7 @@ template<typename Layout>
    constexpr void
    test_stride_1d()
    {
-    std::layout_left::mapping<std::extents<int, 3>> m;
+    typename Layout::mapping<std::extents<int, 3>> m;
      VERIFY(m.stride(0) == 1);
    }

@@ -321,73 +330,150 @@ template<>
    }

  template<typename Layout>
-  constexpr void
-  test_stride_2d();
+struct TestStride2D;

  template<>
-  constexpr void
-  test_stride_2d<std::layout_left>()
+  struct TestStride2D<std::layout_left>
    {
-    std::layout_left::mapping<std::extents<int, 3, 5>> m;
-    VERIFY(m.stride(0) == 1);
-    VERIFY(m.stride(1) == 3);
-  }
+    static constexpr void
+    run()
+    {
+      std::layout_left::mapping<std::extents<int, 3, 5>> m;
+      VERIFY(m.stride(0) == 1);
+      VERIFY(m.stride(1) == 3);
+    }
+  };

  template<>
-  constexpr void
-  test_stride_2d<std::layout_right>()
+  struct TestStride2D<std::layout_right>
    {
-    std::layout_right::mapping<std::extents<int, 3, 5>> m;
-    VERIFY(m.stride(0) == 5);
-    VERIFY(m.stride(1) == 1);
-  }
+    static constexpr void
+    run()
+    {
+      std::layout_right::mapping<std::extents<int, 3, 5>> m;
+      VERIFY(m.stride(0) == 5);
+      VERIFY(m.stride(1) == 1);
+    }
+  };

  template<>
+  struct TestStride2D<std::layout_stride>
+  {
+    static constexpr void
+    run()
+    {
+      std::array<int, 2> strides{13, 2};
+      std::layout_stride::mapping m(std::extents<int, 3, 5>{}, strides);
+      VERIFY(m.stride(0) == strides[0]);
+      VERIFY(m.stride(1) == strides[1]);
+      VERIFY(m.strides() == strides);
+    }
+  };
+
+#if __glibcxx_submdspan
+template<size_t PaddingValue>
+  struct TestStride2D<std::layout_left_padded<PaddingValue>>
+  {
+    static constexpr void
+    run()
+    {
+      using Layout = std::layout_left_padded<PaddingValue>;
+      typename Layout::mapping<std::extents<int, 3, 5>> m;
+
+      VERIFY(m.stride(0) == 1);
+
+      // The next multiple of PaddingValue, that's greater or equal
+      // to exts.extent(0) is the unique value in the range:
+      //   [exts.extent(0), exts.extent(0) + PaddingValue)
+      // that is divisible by PaddingValue.
+      VERIFY((m.stride(1) % PaddingValue) == 0);
+      VERIFY(3 <= m.stride(1) && std::cmp_less(m.stride(1), 3 +
PaddingValue));
+    }
+  };
+#endif
+
+template<typename Layout>
    constexpr void
-  test_stride_2d<std::layout_stride>()
+  test_stride_2d()
    {
-    std::array<int, 2> strides{13, 2};
-    std::layout_stride::mapping m(std::extents<int, 3, 5>{}, strides);
-    VERIFY(m.stride(0) == strides[0]);
-    VERIFY(m.stride(1) == strides[1]);
-    VERIFY(m.strides() == strides);
+    TestStride2D<Layout>::run();
    }

  template<typename Layout>
-  constexpr void
-  test_stride_3d();
+struct TestStride3D;

  template<>
-  constexpr void
-  test_stride_3d<std::layout_left>()
+  struct TestStride3D<std::layout_left>
    {
-    std::layout_left::mapping m(std::dextents<int, 3>(3, 5, 7));
-    VERIFY(m.stride(0) == 1);
-    VERIFY(m.stride(1) == 3);
-    VERIFY(m.stride(2) == 3*5);
-  }
+    static constexpr void
+    run()
+    {
+      std::layout_left::mapping m(std::dextents<int, 3>(3, 5, 7));
+      VERIFY(m.stride(0) == 1);
+      VERIFY(m.stride(1) == 3);
+      VERIFY(m.stride(2) == 3*5);
+    }
+  };
+

  template<>
-  constexpr void
-  test_stride_3d<std::layout_right>()
+  struct TestStride3D<std::layout_right>
    {
-    std::layout_right::mapping m(std::dextents<int, 3>(3, 5, 7));
-    VERIFY(m.stride(0) == 5*7);
-    VERIFY(m.stride(1) == 7);
-    VERIFY(m.stride(2) == 1);
-  }
+    static constexpr void
+    run()
+    {
+      std::layout_right::mapping m(std::dextents<int, 3>(3, 5, 7));
+      VERIFY(m.stride(0) == 5*7);
+      VERIFY(m.stride(1) == 7);
+      VERIFY(m.stride(2) == 1);
+    }
+  };

  template<>
+  struct TestStride3D<std::layout_stride>
+  {
+    static constexpr void
+    run()
+    {
+      std::dextents<int, 3> exts(3, 5, 7);
+      std::array<int, 3> strides{11, 2, 41};
+      std::layout_stride::mapping<std::dextents<int, 3>> m(exts, strides);
+      VERIFY(m.stride(0) == strides[0]);
+      VERIFY(m.stride(1) == strides[1]);
+      VERIFY(m.stride(2) == strides[2]);
+      VERIFY(m.strides() == strides);
+    }
+  };
+
+#if __glibcxx_submdspan
+template<size_t PaddingValue>
+  struct TestStride3D<std::layout_left_padded<PaddingValue>>
+  {
+    static constexpr void
+    run()
+    {
+      using Layout = std::layout_left_padded<PaddingValue>;
+      typename Layout::mapping<std::extents<int, 3, 5, 7>> m;
+
+      VERIFY(m.stride(0) == 1);
+
+      // The next multiple of PaddingValue, that's greater or equal
+      // to exts.extent(0) is the unique value in the range:
+      //   [exts.extent(0), exts.extent(0) + PaddingValue)
+      // that is divisible by PaddingValue.
+      VERIFY((m.stride(1) % PaddingValue) == 0);
+      VERIFY(3 <= m.stride(1) && std::cmp_less(m.stride(1), 3 +
PaddingValue));
+
+      VERIFY(m.stride(1) * 5 == m.stride(2));
+    }
+  };
+#endif
+
+template<typename Layout>
    constexpr void
-  test_stride_3d<std::layout_stride>()
+  test_stride_3d()
    {
-    std::dextents<int, 3> exts(3, 5, 7);
-    std::array<int, 3> strides{11, 2, 41};
-    std::layout_stride::mapping<std::dextents<int, 3>> m(exts, strides);
-    VERIFY(m.stride(0) == strides[0]);
-    VERIFY(m.stride(1) == strides[1]);
-    VERIFY(m.stride(2) == strides[2]);
-    VERIFY(m.strides() == strides);
+    TestStride3D<Layout>::run();
    }

  template<typename Layout>
@@ -411,7 +497,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);
    }

@@ -561,10 +648,27 @@ main()
    test_all<std::layout_left>();
    test_all<std::layout_right>();
    test_all<std::layout_stride>();
+#ifdef __glibcxx_submdspan
+  test_all<std::layout_left_padded<1>>();
+  test_all<std::layout_left_padded<2>>();
+  test_all<std::layout_left_padded<5>>();
+#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_submdspan
+  test_has_op_eq<std::layout_left, std::layout_left_padded<0>, false>();
+  test_has_op_eq<std::layout_left, std::layout_left_padded<6>, false>();
+  test_has_op_eq<std::layout_left, std::layout_left_padded<dyn>, false>();
+  // REVIEW: The next one looks strange, because it's neither. Somehow,
the
+  // conversion rules seem to be playing a critical role again.
+  // test_has_op_eq<std::layout_right, std::layout_left_padded<0>,
false>();
+
+  test_has_op_eq<std::layout_left_padded<2>, std::layout_left_padded<6>,
true>();
+  test_has_op_eq<std::layout_left_padded<2>,
std::layout_left_padded<dyn>, true>();
+#endif
+
    test_has_op_eq_peculiar();
    return 0;
  }
diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc
b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc
new file mode 100644
index 00000000000..6e79e191ff7
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc
@@ -0,0 +1,537 @@
+// { dg-do run { target c++26 } }
+#include <mdspan>
+
+#include <cstdint>
+#include "../int_like.h"
+#include <testsuite_hooks.h>
+
+constexpr size_t dyn = std::dynamic_extent;
+
+constexpr bool
+test_default_ctor()
+{
+  using Extents = std::extents<size_t, 3, 5>;

Could you also test with dynamic extents here, in particular first extent
being
dynamic, and de-facto ignored?


+
+  std::layout_left_padded<2>::mapping<Extents> msta;
+  VERIFY(msta.stride(0) == 1);
+  VERIFY(msta.stride(1) == 4);
+
+  std::layout_left_padded<dyn>::mapping<Extents> mdyn;
+  VERIFY(mdyn.stride(0) == 1);
+  VERIFY(mdyn.stride(1) == 3);
+  return true;
+}
+
+constexpr bool
+test_from_exts()
+{
+  auto exts = std::dextents<size_t, 2>{3, 5};
+
+  std::layout_left_padded<0>::mapping m0_sta(exts);
+  VERIFY(m0_sta.stride(1) == exts.extent(0));
+
+  std::layout_left_padded<1>::mapping m1_sta(exts);
+  VERIFY(m1_sta.stride(1) == exts.extent(0));
+
+  std::layout_left_padded<2>::mapping m2_sta(exts);
+  VERIFY(m2_sta.stride(1) == 4);
+
+  std::layout_left_padded<dyn>::mapping mdyn(exts);
+  VERIFY(mdyn.stride(1) == exts.extent(0));
+  return true;
+}
+
+template<size_t PaddingValue, typename CustomPadType>
+constexpr bool
+test_from_pad()
+{
+  using Layout = std::layout_left_padded<PaddingValue>;
+  auto pad = 3;
+  auto exts = std::dextents<size_t, 3>{pad + 1, 5, 7};
+  typename Layout::mapping m(exts, CustomPadType{pad});
+  VERIFY(std::cmp_equal(m.stride(1), 2*pad));
+  VERIFY(m.extents() == exts);
+  return true;
+}
+
+template<size_t PaddingValue>
+constexpr void
+test_from_pad_all()
+{
+  test_from_pad<PaddingValue, int>();
+  static_assert(test_from_pad<PaddingValue, int>());
+
+  test_from_pad<PaddingValue, IntLike>();
+  test_from_pad<PaddingValue, MutatingInt>();
+  test_from_pad<PaddingValue, RValueInt>();
+
+  using Layout = std::layout_left_padded<PaddingValue>;
+  using Extents = std::dims<3>;
+  using Mapping = Layout::template mapping<Extents>;
+  static_assert(!std::is_constructible_v<Mapping, Extents, ThrowingInt>);
+  static_assert(!std::is_constructible_v<Mapping, Extents, NotIntLike>);
+}
+
+constexpr bool
+is_same_mapping(const auto& lhs, const auto& rhs)
+{
+  if (lhs.extents().rank() != rhs.extents().rank())
+    return false;
+
+  if (lhs.extents() != rhs.extents())
+    return false;
+
+  for (size_t i = 0; i < lhs.extents().rank(); ++i)
+    if (lhs.stride(i) != rhs.stride(i))
+      return false;
+  return true;
+}
+
+enum class ConversionRule
+{
+  Never,
+  Always,
+  Conventional
+};
+
+template<typename To, typename From>
+consteval bool
+should_convert(auto rule)

Instead of passing a rule as a constant wrapper, I would prefer
having a first template parameter of  ConversionRule type, for
this and rest of the functions,

I don't think I understand why it's not clean. It works nicely with
lambdas, which is quite convenient. Additionally, we don't need to
type the wordy ConversionRule as much. What do you want to achieve by avoiding constant_wrapper?


+{
+  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::Conventional>;
+
+  // There's a twist when both mappings are left-padded. There's two
distinct
+  // ctors: a defaulted copy ctor and a constrained template that enables
+  // construction from left-padded mappings even if their layout_type is
+  // different. The two ctors have different rules regarding conversion.
+
+  if constexpr (!std::same_as<LayoutTo, LayoutFrom>)
+    check_convertible<typename LayoutTo::mapping<Esta>>(msta,
conversion_rule);
+  else
+    check_convertible<typename LayoutTo::mapping<Esta>>(msta, cregular);
+
+  check_convertible<typename LayoutTo::mapping<Edyn>>(msta,
conversion_rule);
+
+  auto mdyn = typename LayoutFrom::mapping<Edyn>(msta);
+  check_convertible<typename LayoutTo::mapping<Esta>>(mdyn,
conversion_rule);
+  if constexpr (!std::same_as<LayoutTo, LayoutFrom>)
+    check_convertible<typename LayoutTo::mapping<Edyn>>(mdyn,
conversion_rule);
+  else
+    check_convertible<typename LayoutTo::mapping<Edyn>>(mdyn, cregular);
+
+  static_assert(!std::is_constructible_v<
+      typename LayoutTo::mapping<Esta>, typename
LayoutFrom::mapping<Ewrong>>);
+};
+
+template<typename PaddedLayout>
+  constexpr void
+  test_from_left_1d()
+  {
+    using E1 = std::extents<int, 6>;
+    using E2 = std::extents<int, dyn>;
+    using E3 = std::extents<int, 5>;
+    constexpr auto cr = std::cw<ConversionRule::Conventional>;
+
+    auto msta = std::layout_left::mapping(E1{});
+    check_convertible_variants<PaddedLayout, E1, E2, E3>(msta, cr);
+  }
+
+template<typename PaddedLayout>
+  constexpr void
+  test_from_left_2d()
+  {
+    using E1 = std::extents<int, 6, 5>;
+    using E2 = std::extents<int, dyn, 5>;
+    using E3 = std::extents<int, 6, 6>;
+    constexpr auto cr = std::cw<ConversionRule::Conventional>;
+
+    auto msta = std::layout_left::mapping(E1{});
+    check_convertible_variants<PaddedLayout, E1, E2, E3>(msta, cr);
+  }
+
+constexpr bool
+test_from_left()
+{
+  auto check = []<typename PaddedLayout>(PaddedLayout)
+  {
+    test_from_left_1d<PaddedLayout>();
+    test_from_left_2d<PaddedLayout>();
+  };
+
+  check(std::layout_left_padded<0>{});
+  check(std::layout_left_padded<1>{});
+  check(std::layout_left_padded<2>{});
+  check(std::layout_left_padded<6>{});
+  check(std::layout_left_padded<dyn>{});
+
+  // rank == 1 is more permissive:
+  test_from_left_1d<std::layout_left_padded<5>>();
+  return true;
+}
+
+constexpr bool
+test_from_stride()
+{
+  using E1 = std::extents<size_t, 6, 5, 7>;
+  using E2 = std::dims<3>;
+  using E3 = std::extents<size_t, 6, 6, 7>;
+
+  auto check = []<typename PaddedLayout>(PaddedLayout)
+  {
+    auto exts = E1{};
+    auto strides = std::array<int, 3>{
+      1, exts.extent(0), exts.extent(0) * exts.extent(1)};
+    constexpr auto cr = std::cw<ConversionRule::Never>;
+
+    auto m = std::layout_stride::mapping(exts, strides);
+    check_convertible_variants<PaddedLayout, E1, E2, E3>(m, cr);
+  };
+
+  check(std::layout_left_padded<0>{});
+  check(std::layout_left_padded<1>{});
+  check(std::layout_left_padded<2>{});
+  check(std::layout_left_padded<6>{});
+  check(std::layout_left_padded<dyn>{});
+  return true;
+}
+
+constexpr bool
+test_from_leftpad_0d()
+{
+  using E1 = std::extents<uint16_t>;
+  using E2 = std::extents<uint8_t>;
+  using E3 = std::extents<uint8_t, 1>;
+
+  std::layout_left_padded<6>::mapping<E1> msta{E1{}};
+
+  auto check = []<typename To>(To, auto m)
+  {
+    constexpr auto cr = std::cw<ConversionRule::Always>;
+    check_convertible_variants<To, E1, E2, E3>(m, cr);
+  };
+
+  check(std::layout_left_padded<6>{}, msta);
+  check(std::layout_left_padded<dyn>{}, msta);
+  return true;
+}
+
+constexpr bool
+test_from_leftpad_1d()
+{
+  using E1 = std::extents<int, 6>;
+  using E2 = std::extents<int, dyn>;
+  using E3 = std::extents<int, 6, 6>;
+
+  std::layout_left_padded<6>::mapping<E1> msta{E1{}};
+  std::layout_left_padded<dyn>::mapping<E1> mdyn{E1{}};
+
+  auto check = []<typename To>(To, auto m)
+  {
+    constexpr auto cr = std::cw<ConversionRule::Always>;
+    check_convertible_variants<To, E1, E2, E3>(m, cr);
+  };
+
+  // Remember, for rank <= 1 the padding_value is irrelevant.
+  check(std::layout_left_padded<6>{}, msta);
+  check(std::layout_left_padded<6>{}, mdyn);
+  check(std::layout_left_padded<dyn>{}, msta);
+  check(std::layout_left_padded<dyn>{}, mdyn);
+  return true;
+}
+
+constexpr bool
+test_from_leftpad_2d()
+{
+  using E1 = std::extents<int, 6, 5>;
+  using E2 = std::extents<int, dyn, 5>;
+  using E3 = std::extents<int, 6, 6>;
+
+  std::layout_left_padded<6>::mapping<E1> msta{E1{}};
+  std::layout_left_padded<dyn>::mapping<E1> mdyn{E1{}};
+
+  constexpr auto calways = std::cw<ConversionRule::Always>;
+  constexpr auto cnever = std::cw<ConversionRule::Never>;
+
+  auto check = []<typename To>(To, auto m, auto cr)
+  { check_convertible_variants<To, E1, E2, E3>(m, cr); };
+
+  check(std::layout_left_padded<6>{}, msta, cnever);
+  check(std::layout_left_padded<6>{}, mdyn, cnever);
+  check(std::layout_left_padded<dyn>{}, msta, calways);
+  check(std::layout_left_padded<dyn>{}, mdyn, cnever);
+  return true;
+}
+
+constexpr bool
+test_from_right()
+{
+  using E1 = std::extents<size_t, 3>;
+  using E2 = std::dims<1>;
+  using E3 = std::extents<size_t, 5>;
+  constexpr auto cr = std::cw<ConversionRule::Conventional>;
+
+  auto msta = std::layout_right::mapping(E1{});
+
+  // Remember, the padding_value has no effect for rank <= 1.
+  check_convertible_variants<std::layout_left_padded<0>, E1, E2,
E3>(msta, cr);
+  check_convertible_variants<std::layout_left_padded<1>, E1, E2,
E3>(msta, cr);
+  check_convertible_variants<std::layout_left_padded<2>, E1, E2,
E3>(msta, cr);
+  check_convertible_variants<std::layout_left_padded<5>, E1, E2,
E3>(msta, cr);
+  check_convertible_variants<std::layout_left_padded<6>, E1, E2,
E3>(msta, cr);
+  check_convertible_variants<std::layout_left_padded<dyn>, E1, E2,
E3>(msta, cr);
+  return true;
+}
+
+constexpr bool
+test_to_left()
+{
+  using E1 = std::extents<int, 6, 5>;
+  using E2 = std::extents<int, dyn, 5>;
+  using E3 = std::extents<int, 6, 6>;
+
+  auto exts_sta = E1{};
+
+  auto check = [](auto msta)
+  {
+    constexpr auto cr = std::cw<ConversionRule::Conventional>;
+    check_convertible_variants<std::layout_left, E1, E2, E3>(msta, cr);
+  };
+
+  check(std::layout_left_padded<0>::mapping(exts_sta));
+  check(std::layout_left_padded<2>::mapping(exts_sta));
+  check(std::layout_left_padded<6>::mapping(exts_sta));
+  check(std::layout_left_padded<dyn>::mapping(exts_sta, 0));
+  check(std::layout_left_padded<dyn>::mapping(exts_sta, 2));
+  check(std::layout_left_padded<dyn>::mapping(exts_sta, 6));
+  return true;
+}
+
+constexpr bool
+test_never_to_right()
+{
+  using E1 = std::extents<size_t, 3>;
+  using E2 = std::dims<1>;
+
+  auto mr_sta = std::layout_right::mapping(E1{});
+  auto mlp_sta = std::layout_left_padded<2>::mapping<E1>{mr_sta};
+  static_assert(!std::is_constructible_v<decltype(mr_sta),
decltype(mlp_sta)>);
+
+  auto mr_dyn = std::layout_right::mapping(E2{E1{}});
+  auto mlp_dyn = std::layout_left_padded<2>::mapping<E2>{mr_dyn};
+  static_assert(!std::is_constructible_v<decltype(mr_dyn),
decltype(mlp_dyn)>);
+  return true;
+}
+
+template<typename Layout>
+constexpr void
+test_strides()
+{
+  auto check = [](auto exts)
+  {
+    auto m = typename Layout::mapping(exts);
+    using IndexType = typename decltype(m)::index_type;
+    constexpr size_t rank = decltype(m)::extents_type::rank();
+
+    auto strides = m.strides();
+    static_assert(std::same_as<decltype(strides), std::array<IndexType,
rank>>);
+    VERIFY(strides.size() == rank);
+    for (size_t i = 0; i < strides.size(); ++i)
+      VERIFY(strides[i] == m.stride(i));
+  };
+
+  check(std::extents());
+  check(std::extents(0));
+  check(std::extents(3));
+  check(std::extents(3, 5, 7));
+  check(std::extents(3, 0, 7));
+}
+
+constexpr bool
+test_strides_all()
+{
+  test_strides<std::layout_left_padded<0>>();
+  test_strides<std::layout_left_padded<1>>();
+  test_strides<std::layout_left_padded<3>>();
+  test_strides<std::layout_left_padded<dyn>>();
+  return true;
+}
+
+constexpr void
+test_exhaustive_0d()
+{
+  auto exts = std::extents<int>{};
+
+  auto check = [](auto m)
+  {
+    static_assert(m.is_always_exhaustive());
+    VERIFY(m.is_exhaustive());
+  };
+
+  check(std::layout_left_padded<0>::mapping(exts));
+  check(std::layout_left_padded<1>::mapping(exts));
+  check(std::layout_left_padded<2>::mapping(exts));
+  check(std::layout_left_padded<dyn>::mapping(exts));
+}
+
+constexpr void
+test_exhaustive_1d()
+{
+  auto check_dyn_and_sta = []<typename Layout>(Layout)
+  {
+    auto check = [](auto exts)
+    {
+      auto m = typename Layout::mapping(exts);
+      static_assert(m.is_always_exhaustive());
+      VERIFY(m.is_exhaustive());
+    };
+
+    check(std::extents(4));
+    check(std::extents<int, 4>{});
+  };
+
+  check_dyn_and_sta(std::layout_left_padded<1>{});
+  check_dyn_and_sta(std::layout_left_padded<2>{});
+  check_dyn_and_sta(std::layout_left_padded<6>{});
+  check_dyn_and_sta(std::layout_left_padded<dyn>{});
+}
+
+constexpr void
+test_exhaustive_3d()
+{
+  auto exts_dyn = std::extents(4, 5, 7);
+  auto exts_sta = std::extents<int, 4, 5, 7>{};
+  auto ctrue = std::cw<true>;
+  auto cfalse= std::cw<false>;
+
+  auto check = [](auto m, auto static_expected, auto runtime_expected)
+  {
+    static_assert(m.is_always_exhaustive() == static_expected);
+    VERIFY(m.is_exhaustive() == runtime_expected);
+  };
+
+  check(std::layout_left_padded<0>::mapping(exts_sta), ctrue, true);
+  check(std::layout_left_padded<0>::mapping(exts_dyn), cfalse, true);
+  check(std::layout_left_padded<1>::mapping(exts_sta), ctrue, true);
+  check(std::layout_left_padded<1>::mapping(exts_dyn), cfalse, true);
+  check(std::layout_left_padded<2>::mapping(exts_sta), ctrue, true);
+  check(std::layout_left_padded<2>::mapping(exts_dyn), cfalse, true);
+  check(std::layout_left_padded<6>::mapping(exts_dyn), cfalse, false);
+  check(std::layout_left_padded<6>::mapping(exts_sta), cfalse, false);
+  check(std::layout_left_padded<dyn>::mapping(exts_sta), cfalse, true);
+  check(std::layout_left_padded<dyn>::mapping(exts_dyn, 2), cfalse, true);
+  check(std::layout_left_padded<dyn>::mapping(exts_dyn, 3), cfalse,
false);
+}
+
+constexpr bool
+test_exhaustive()
+{
+  test_exhaustive_0d();
+  test_exhaustive_1d();
+  test_exhaustive_3d();
+  return true;
+}
+
+constexpr bool
+test_op_eq()
+{
+  // The generic cases are handled in layouts/mapping.cc. Here we check
special
+  // cases related to non exhaustive layouts.
+  using E1 = std::extents<size_t, 6, 5, 7>;
+  using E2 = std::dims<3>;
+  using E3 = std::extents<size_t, 7, 5, 7>;
+
+  auto m1 = std::layout_left_padded<0>::mapping(E1{});
+  auto m2 = std::layout_left_padded<7>::mapping(E1{});
+
+  VERIFY(m1 == std::layout_left_padded<0>::mapping(E1{}));
+  VERIFY(m1 == std::layout_left_padded<1>::mapping(E1{}));
+  VERIFY(m1 == std::layout_left_padded<2>::mapping(E1{}));
+  VERIFY(m1 == std::layout_left_padded<6>::mapping(E1{}));
+  VERIFY(m1 != std::layout_left_padded<7>::mapping(E1{}));
+  VERIFY(m1 == std::layout_left_padded<dyn>::mapping(E1{}));
+  VERIFY(m1 != std::layout_left_padded<dyn>::mapping(E1{}, 7));
+
+  VERIFY(m1 == std::layout_left_padded<0>::mapping(E2{E1{}}));
+  VERIFY(m1 == std::layout_left_padded<1>::mapping(E2{E1{}}));
+  VERIFY(m1 == std::layout_left_padded<2>::mapping(E2{E1{}}));
+  VERIFY(m1 == std::layout_left_padded<6>::mapping(E2{E1{}}));
+  VERIFY(m1 != std::layout_left_padded<7>::mapping(E2{E1{}}));
+  VERIFY(m1 == std::layout_left_padded<dyn>::mapping(E2{E1{}}));
+  VERIFY(m1 != std::layout_left_padded<dyn>::mapping(E2{E1{}}, 7));
+
+  VERIFY(m2 == std::layout_left_padded<7>::mapping(E1{}));
+  VERIFY(m2 == std::layout_left_padded<dyn>::mapping(E1{}, 7));
+  VERIFY(m2 == std::layout_left_padded<7>::mapping(E2{E1{}}));
+  VERIFY(m2 == std::layout_left_padded<dyn>::mapping(E2{E1{}}, 7));
+
+  VERIFY(m2 != std::layout_left_padded<7>::mapping(E3{}));
+  VERIFY(m2 != std::layout_left_padded<dyn>::mapping(E3{}, 7));
+  return true;
+}
+
+int
+main()
+{
+  test_default_ctor();
+  static_assert(test_default_ctor());
+
+  test_from_exts();
+  static_assert(test_from_exts());
+
+  test_from_left();
+  static_assert(test_from_left());
+
+  test_from_pad_all<dyn>();
+  test_from_pad_all<3>();
+
+  test_from_stride();
+  static_assert(test_from_stride());
+
+  test_from_leftpad_0d();
+  static_assert(test_from_leftpad_0d());
+
+  test_from_leftpad_1d();
+  static_assert(test_from_leftpad_1d());
+
+  test_from_leftpad_2d();
+  static_assert(test_from_leftpad_2d());
+
+  test_from_right();
+  static_assert(test_from_right());
+
+  test_to_left();
+  static_assert(test_to_left());
+
+  test_never_to_right();
+  static_assert(test_never_to_right());
+
+  test_strides_all();
+  static_assert(test_strides_all());
+
+  test_exhaustive();
+  static_assert(test_exhaustive());
+  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..83b4402d8c5
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_neg.cc
@@ -0,0 +1,225 @@
+// { dg-do compile { target c++26 } }
+#include <mdspan>
+
+#include <cstdint>
+
+constexpr size_t dyn = std::dynamic_extent;
+
+constexpr bool
+test_from_extens_oversized()
+{

This test does not run.

+  using E1 = std::dextents<uint16_t, 2>;
+  using E2 = std::dextents<uint8_t, 2>;
+
+  auto mlp = std::layout_left_padded<dyn>::mapping(E1{8, 128});
+  auto m = std::layout_left_padded<dyn>::mapping<E2>(mlp);
+  (void) m;
+  return true;

+}
+
+constexpr bool
+test_from_left()
+{
+  auto exts = std::extents<uint8_t, 6, dyn>{4};
+  auto ml = std::layout_left::mapping(exts);
+
+  using Layout = std::layout_left_padded<4>;
+  Layout::mapping<decltype(exts)> m(ml); // { dg-error "from here" }
+  return true;
+}
+static_assert(test_from_left());
+
+constexpr bool
+test_from_left_bad_runtime_stride()
+{
+  auto exts = std::extents<uint8_t, dyn, dyn>{6, 4};
+  auto ml = std::layout_left::mapping(exts);
+
+  using Layout = std::layout_left_padded<4>;
+  Layout::mapping<decltype(exts)> m(ml); // { dg-error "expansion of" }
+  return true;
+}
+static_assert(test_from_left_bad_runtime_stride());
+
+constexpr bool
+test_from_left_oversized_extents()
+{
+  auto exts = std::extents<uint16_t, dyn, dyn>{8, 128};
+  auto ml = std::layout_left::mapping(exts);
+
+  using Layout = std::layout_left_padded<8>;
+  Layout::mapping<std::extents<uint8_t, dyn, dyn>> m(ml); // { dg-error
"expansion of" }
+  return true;
+}
+static_assert(test_from_left_oversized_extents());
+
+template<size_t PaddingValue>
+  constexpr bool
+  test_pad_overflow()
+  {
+    auto exts = std::extents<uint8_t, dyn>{4};
+    auto n = size_t(1) << 9;
+    auto m = typename
std::layout_left_padded<PaddingValue>::mapping(exts, n);
+    (void) m;
+    return true;
+  }
+static_assert(test_pad_overflow<1>());   // { dg-error "expansion of" }
+static_assert(test_pad_overflow<dyn>()); // { dg-error "expansion of" }
+
+template<size_t PaddingValue>
+  constexpr bool
+  test_from_pad_negative()
+  {
+    auto exts = std::extents(4);
+    auto m = typename
std::layout_left_padded<PaddingValue>::mapping(exts, -1);
+    (void) m;
+    return true;
+  }
+static_assert(test_from_pad_negative<1>());   // { dg-error "expansion
of" }
+static_assert(test_from_pad_negative<dyn>()); // { dg-error "expansion
of" }
+
+template<size_t Pad>
+  constexpr bool
+  test_static_pad_same()
+  {
+    using Extents = std::extents<int, dyn>;
+    using Mapping = typename
std::layout_left_padded<Pad>::mapping<Extents>;
+    auto exts = Extents{4};
+    auto m = Mapping(exts, Pad + 1);      // { dg-error "expansion of" }
+    (void) m;
+    return true;
+  }
+static_assert(test_static_pad_same<1>()); // { dg-error "expansion of" }
+static_assert(test_static_pad_same<3>()); // { dg-error "expansion of" }
+
+constexpr bool
+test_from_stride_wrong_stride0()
+{
+  using Extents = std::dextents<size_t, 2>;
+  auto e = Extents{3, 5};
+  auto ms = std::layout_stride::mapping(e, std::array<int, 2>{2, 7});
+  auto m = std::layout_left_padded<dyn>::mapping<Extents>(ms); // {
dg-error "expansion of" }
+  (void) m;
+  return true;
+}
+static_assert(test_from_stride_wrong_stride0());
+
+constexpr bool
+test_from_stride_wrong_stride1()
+{
+  using Extents = std::dextents<size_t, 2>;
+  auto e = Extents{3, 5};
+  auto ms = std::layout_stride::mapping(e, std::array<int, 2>{1, 3});
+  auto m = std::layout_left_padded<2>::mapping<Extents>(ms); // {
dg-error "expansion of" }
+  (void) m;
+  return true;
+}
+static_assert(test_from_stride_wrong_stride1());
+
+constexpr bool
+test_from_stride_wrong_stride2()
+{
+  using Extents = std::dims<3>;
+  auto e = Extents{3, 5, 7};
+  auto ms = std::layout_stride::mapping(e, std::array<int, 3>{1, 4, 21});
+  auto m = std::layout_left_padded<dyn>::mapping<Extents>(ms); // here
(not implemented)
+  (void) m;
+  return true;
+}
+static_assert(test_from_stride_wrong_stride2());
+
+constexpr bool
+test_from_stride_oversized()
+{
+  auto exts = std::extents<uint16_t, dyn, dyn>{2, 6};
+  auto ms = std::layout_stride::mapping(exts, std::array<uint16_t, 2>{1,
128});
+
+  using Layout = std::layout_left_padded<dyn>;
+  Layout::mapping<std::extents<uint8_t, dyn, dyn>> m(ms); // { dg-error
"expansion of" }
+  (void) m;
+  return true;
+}
+static_assert(test_from_stride_oversized());
+
+constexpr bool
+test_from_leftpad_dyn()
+{
+  using Extents = std::dextents<size_t, 2>;
+  auto e = Extents{3, 5};
+  auto mlp = std::layout_left_padded<dyn>::mapping(e);
+  auto m = std::layout_left_padded<2>::mapping<Extents>(mlp); // {
dg-error "expansion of" }
+  (void) m;
+  return true;
+}
+static_assert(test_from_leftpad_dyn());
+
+constexpr bool
+test_from_leftpad_sta()
+{
+  using Extents = std::dextents<size_t, 2>;
+  auto e = Extents{3, 5};
+  auto mlp = std::layout_left_padded<3>::mapping(e);
+  auto m = std::layout_left_padded<2>::mapping<Extents>(mlp); // {
dg-error "required from" }
+  (void) m;
+  return true;
+}
+static_assert(test_from_leftpad_sta());
+
+constexpr bool
+test_from_leftpad_oversized()
+{
+  using E1 = std::extents<uint16_t, 8, 128>;
+  using E2 = std::extents<uint8_t, dyn, dyn>;
+  auto mlp = std::layout_left_padded<dyn>::mapping<E1>(E1{});
+  auto m = std::layout_left_padded<dyn>::mapping<E2>(mlp); // { dg-error
"expansion of" }
+  (void) m;
+  return true;
+}
+static_assert(test_from_leftpad_oversized());
+
+template<size_t RunId>
+  constexpr bool
+  test_to_left_not_exhaustive()
+  {
+    using E1 = std::extents<int, 6, 5>;
+    using E2 = std::extents<int, dyn, 5>;
+
+    [[maybe_unused]] auto msta =
std::layout_left_padded<7>::mapping(E1{});
+    if constexpr (RunId == 0)
+      {
+       auto m = std::layout_left::mapping<E1>(msta); // { dg-error
"required from" }
+       (void) m;
+      }
+    if constexpr (RunId == 1)
+      {
+       auto m = std::layout_left::mapping<E2>(msta); // { dg-error
"expansion of" }
+       (void) m;
+      }
+
+    [[maybe_unused]] auto mdyn =
std::layout_left_padded<dyn>::mapping(E2{E1{}}, 7);
+    if constexpr (RunId == 2)
+      {
+       auto m = std::layout_left::mapping<E1>(mdyn); // { dg-error
"expansion of" }
+       (void) m;
+      }
+    if constexpr (RunId == 3)
+      {
+       auto m = std::layout_left::mapping<E2>(mdyn); // { dg-error
"expansion of" }
+       (void) m;
+      }
+    return true;
+  }
+static_assert(test_to_left_not_exhaustive<0>());
+static_assert(test_to_left_not_exhaustive<1>());
+static_assert(test_to_left_not_exhaustive<2>());
+static_assert(test_to_left_not_exhaustive<3>());
+
+// { dg-prune-output "padding_value must be representable as index_type" }
+// { dg-prune-output "non-constant condition for static assertion" }
+// { dg-prune-output "called in a constant expression" }
+// { dg-prune-output "no matching function" }
+// { dg-prune-output "static assertion failed" }
+// { dg-prune-output "__glibcxx_assert_fail()" }
+// { dg-prune-output "must be compatible with other.stride" }
+// { dg-prune-output "padding_value is dynamic_extent" }
+// { dg-prune-output "_S_rank <= 1" }
--
2.50.1





Reply via email to