--- 8< ---
This commit adds a new layout layout_left_padded as standardized in
N5014 but with one deviation. This includes checking of mandates and
prerequisites, with exception of "slow" prerequisites, i.e. those that
requires a for loop. It adds the feature testing macro submdspan as a
purely internal macro and registers layout_left_padded in the std
module.
The standard mandates that the padding_value is representable as
index_type. However, padding_value can be dynamic_extent which is
defined as `size_t(-1)`. Hence, it's impossible to use a dynamic
padding_value if index_type is int. The deviation is to only require
static padding values to be representable as index_type; and thereby
allow dynamic padding values for every index_type.
libstdc++-v3/ChangeLog:
* include/bits/version.def (submdspan): Add as internal
only.
* include/bits/version.h: Regenerate.
* include/std/mdspan (__fwd_prod): New overload.
(layout_left_padded): Add declaration and implementation.
(layout_right_padded): Add declaration only.
(__is_left_padded_mapping): New concept.
(__is_right_padded_mapping): Ditto.
(__standardized_mapping): Recognize left and right padded
mappings.
(layout_left::mapping::mapping): New overload for left
padded mappings.
* src/c++23/std.cc.in (layout_left_padded): Add.
* testsuite/23_containers/mdspan/layouts/ctors.cc: Add tests for
layout_left_padded.
* testsuite/23_containers/mdspan/layouts/empty.cc: Ditto.
* testsuite/23_containers/mdspan/layouts/mapping.cc: Ditto.
* testsuite/23_containers/mdspan/layouts/debug/padded_neg.cc: New
test.
* testsuite/23_containers/mdspan/layouts/padded.cc: New test.
* testsuite/23_containers/mdspan/layouts/padded_neg.cc: New test.
Signed-off-by: Luc Grosheintz <luc.groshei...@gmail.com>
---
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>());
+
+// { 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