[PATCH v1] libstdc++: Implement std::layout_left_padded.

Luc Grosheintz luc.grosheintz@gmail.com
Tue Sep 9 07:41:39 GMT 2025



On 9/8/25 10:12 PM, Luc Grosheintz 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

The description of slow is bad, because I've also not implemented
the representability checks. Maybe that's just something I should
implement (and test).

> 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.
> 

I forgot to add the PR number to the commit message:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110352

> 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.grosheintz@gmail.com>
> ---
>   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;
> +  no_stdname = true; // FIXME: remove
> +  values = {
> +    v = 202306;
> +    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
> +      {
> +	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
> +      {
> +	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
> +    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
> +      __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>;
>   
>       // 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); }
>   
> +      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
> +      __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);
> +      }
> +
> +    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");
> +
> +      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
> +		__stride = __mdspan::__least_multiple_at_least<index_type>(
> +		  padding_value, __exts.extent(0));
> +
> +	      _M_stride = std::extents<index_type, _S_static_stride>{__stride};
> +	    }
> +	}
> +
> +	template<__mdspan::__valid_index_type<index_type> _OIndexType>
> +	  constexpr mapping(const extents_type& __exts, _OIndexType __opad)
> +	  : _M_extents(__exts)
> +	  {
> +	    if constexpr (std::is_integral_v<_OIndexType>)
> +	      {
> +		__glibcxx_assert(cmp_less_equal(__opad,
> +		    numeric_limits<index_type>::max()));
> +		if constexpr (std::is_signed_v<_OIndexType>)
> +		  __glibcxx_assert(__opad >= 0);
> +	      }
> +
> +	    auto __pad = static_cast<index_type>(std::move(__opad));
> +	    if constexpr (std::is_signed_v<index_type>)
> +	      __glibcxx_assert(__pad >= 0);
> +	    if constexpr (padding_value != dynamic_extent)
> +	      __glibcxx_assert(cmp_equal(padding_value, __pad));
> +	    if constexpr (_S_rank > 1)
> +	      _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
> +	    //   __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))));
> +
> +	    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)");
> +	    }
> +	    __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));
> +	}
> +
> +	// _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>;
> +	[[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>)
> +      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>
> 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>;
> +
> +  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)
> +{
> +  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()
> +{
> +  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>());

I need to also need the check that the required span size
is small enough when creating layout_left from a left-
padded layout.

> +
> +// { 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" }



More information about the Libstdc++ mailing list