[PATCH v1] libstdc++: Implement std::layout_left_padded.
Luc Grosheintz
luc.grosheintz@gmail.com
Tue Sep 9 14:12:30 GMT 2025
On 9/9/25 15:54, Tomasz Kaminski wrote:
> On Mon, Sep 8, 2025 at 10:14 PM Luc Grosheintz <luc.grosheintz@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.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>;
>>
> Rename this to __stride_type.
True, I suspect there'll be more of those in scatter all over <mdspan>.
They'll also get cleaned up.
While we're on cleaning up: should I be writing docstrings? <span> doesn't
have them and I didn't even know there was a GNU C++ standard library
documentation. I always use cppreference, so I thought it was acceptable
to not document, because it's already documented elsewhere.
>
>> + [[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>());
>> +
>> +// { 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
>>
>>
>
More information about the Libstdc++
mailing list