On Fri, Sep 26, 2025 at 2:22 PM Luc Grosheintz <[email protected]> wrote:
> > > On 9/26/25 9:45 AM, Tomasz Kaminski wrote: > > On Tue, Sep 23, 2025 at 3:41 PM Luc Grosheintz <[email protected] > > > > wrote: > > > >> This commit adds the right padded layout as described in N5014, with > >> LWG4372 (dynamic padding value) and LWG4314 (move in operator()). > >> > >> libstdc++-v3/ChangeLog: > >> > >> * include/std/mdspan (_RightPaddedIndices): New class. > >> (layout_right_padded): New class. > >> * src/c++23/std.cc.in (layout_right_padded): Add. > >> * testsuite/23_containers/mdspan/layouts/ctors.cc: Update > >> test for right padded layout. > >> * testsuite/23_containers/mdspan/layouts/empty.cc: Ditto. > >> * testsuite/23_containers/mdspan/layouts/mapping.cc: Ditto. > >> * testsuite/23_containers/mdspan/layouts/padded.cc: Ditto. > >> * testsuite/23_containers/mdspan/layouts/padded_neg.cc: Ditto. > >> * testsuite/23_containers/mdspan/layouts/padded_traits.h: > Ditto. > >> > >> Signed-off-by: Luc Grosheintz <[email protected]> > >> --- > >> > > Suggestions here: > > * Add _PaddingStride::_M_equal for opearator== and use _M_stride to > > implement it > > * Fix spaces, and cast index_type as in layout_left > > * Suggestion for LWG issue for both > > * Explanation that we can use existing member names for internal classes, > > and suggestion > > to use it for _PaddingStorage. > > > > With that ready for v4 > > > > > >> libstdc++-v3/include/std/mdspan | 263 ++++++++++++++++++ > >> libstdc++-v3/src/c++23/std.cc.in | 5 +- > >> .../23_containers/mdspan/layouts/ctors.cc | 3 +- > >> .../23_containers/mdspan/layouts/empty.cc | 1 + > >> .../23_containers/mdspan/layouts/mapping.cc | 6 +- > >> .../23_containers/mdspan/layouts/padded.cc | 4 + > >> .../mdspan/layouts/padded_neg.cc | 28 ++ > >> .../mdspan/layouts/padded_traits.h | 135 ++++++++- > >> 8 files changed, 437 insertions(+), 8 deletions(-) > >> > >> diff --git a/libstdc++-v3/include/std/mdspan > >> b/libstdc++-v3/include/std/mdspan > >> index 18782bf0a06..f61931ab8e6 100644 > >> --- a/libstdc++-v3/include/std/mdspan > >> +++ b/libstdc++-v3/include/std/mdspan > >> @@ -954,6 +954,32 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > >> : mapping(__other.extents(), __mdspan::__internal_ctor{}) > >> { __glibcxx_assert(*this == __other); } > >> > >> +#if __glibcxx_padded_layouts > >> + template<class _RightPaddedMapping> > >> + requires > __mdspan::__is_right_padded_mapping<_RightPaddedMapping> > >> + && is_constructible_v<extents_type, > >> + typename > >> _RightPaddedMapping::extents_type> > >> > > Tabs instead of spaces, here and the rest of the file. > > > >> + constexpr > >> + explicit(!is_convertible_v<typename > >> _RightPaddedMapping::extents_type, > >> + extents_type>) > >> > > We should report bug against standard, because this constructor should be > > explicit, > > for Ranks > 1 and extent(0) or _S_padded_stride are dynamic, as we have > > precondition > > then. I think it should also be constrained, on what is now mandated, > about > > satic_stride > > matching. > > I see your point. (Let's discuss separately.) > > We should match stride_mismatch, the same way as static extent mismatch > is > > handled here. > > I'm sorry, I don't understand the immediately preceeding sentence. > I think that: padded layout width padded_stride X should be convertible to padded layout with padded_stride Y, only if Y == X same as for extents<int, X> and extents<int, Y> This is also towards what we should report as the issue, not something to be addressed in this PR. > > One guess is I should apply the trick: > > if constexpr (__rank > 1> > if constexpr ( /* static conds */) > static_assert(...); > else > __glibcxx_assert(...); > > (but that's already the case, likely due to a different review > comment). > > > > > Same for layot_left. > > > > > >> + mapping(const _RightPaddedMapping& __other) noexcept > >> + : mapping(__other.extents(), __mdspan::__internal_ctor{}) > >> + { > >> + constexpr size_t __rank = extents_type::rank(); > >> + constexpr size_t __ostride_sta = > __mdspan::__get_static_stride< > >> + _RightPaddedMapping>(); > >> + > >> + if constexpr (__rank > 1 > >> + && extents_type::static_extent(__rank - 1) != > dynamic_extent > >> + && __ostride_sta != dynamic_extent) > >> + static_assert(extents_type::static_extent(__rank - 1) == > >> __ostride_sta); > >> + > >> + if constexpr (__rank > 1) > >> + __glibcxx_assert(__other.stride(__rank - 2) > >> + == __other.extents().extent(__rank - 1)); > >> + } > >> +#endif > >> + > >> constexpr mapping& > >> operator=(const mapping&) noexcept = default; > >> > >> @@ -1350,6 +1376,37 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > >> return __res; > >> } > >> > >> + template<typename _Extents, typename _Stride, typename... _Indices> > >> + constexpr typename _Extents::index_type > >> + __linear_index_rightpad(const _Extents& __exts, _Stride __stride, > >> + _Indices... __indices) > >> + { > >> + // i[n-1] + stride*(i[n-2] + extents.extent(n-2])*...) > >> + using _IndexType = typename _Extents::index_type; > >> + _IndexType __res = 0; > >> + if constexpr (sizeof...(__indices) > 0) > >> + { > >> + _IndexType __mult = 1; > >> + array<_IndexType, sizeof...(__indices)> > >> __ind_arr{__indices...}; > >> + > >> + auto __update_rest = [&, __pos = > __exts.rank()-1](_IndexType) > >> mutable > >> + { > >> + --__pos; > >> + __res += __ind_arr[__pos] * __mult; > >> + __mult *= __exts.extent(__pos); > >> + }; > >> + > >> + auto __update = [&](_IndexType, auto... __rest) > >> + { > >> + __res += __ind_arr[__exts.rank() - 1]; > >> + __mult = __stride.extent(0); > >> + (__update_rest(__rest), ...); > >> + }; > >> + __update(__indices...); > >> + } > >> + return __res; > >> + } > >> + > >> template<size_t _Rank> > >> struct _LeftPaddedLayoutTraits > >> { > >> @@ -1384,6 +1441,39 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > >> } > >> }; > >> > >> + template<size_t _Rank> > >> + struct _RightPaddedLayoutTraits > >> + { > >> + using _LayoutSame = layout_right; > >> + using _LayoutOther = layout_left; > >> + > >> + constexpr static size_t _S_ext_idx = _Rank - 1; > >> + constexpr static size_t _S_stride_idx = _Rank - 2; > >> + constexpr static size_t _S_unpad_begin = 0; > >> + constexpr static size_t _S_unpad_end = _Rank - 1; > >> + > >> + template<typename _Mapping> > >> + constexpr static bool _S_is_same_padded_mapping = > >> + __is_right_padded_mapping<_Mapping>; > >> + > >> + template<typename _Mapping> > >> + constexpr static bool _S_is_other_padded_mapping = > >> + __is_left_padded_mapping<_Mapping>; > >> + > >> + template<typename _IndexType, size_t _StaticStride, > >> size_t..._Extents> > >> + constexpr static auto _S_make_padded_extent( > >> + extents<_IndexType, _StaticStride> __stride, > >> + const extents<_IndexType, _Extents...>& __exts) > >> + { > >> + auto __impl = [&]<size_t... _Is>(integer_sequence<size_t, > >> _Is...>) > >> + { > >> + return extents<_IndexType, (_Extents...[_Is])..., > >> _StaticStride>{ > >> + __exts.extent(_Is)..., __stride.extent(0)}; > >> + }; > >> + return __impl(make_index_sequence<sizeof...(_Extents) - > 1>()); > >> + } > >> + }; > >> + > >> template<size_t _PaddingValue, typename _Extents, typename > >> _LayoutTraits> > >> class _PaddedStorage > >> { > >> @@ -1834,6 +1924,179 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION > >> __other.stride(1))); > >> } > >> }; > >> + > >> + template<size_t _PaddingValue> > >> + template<typename _Extents> > >> + class layout_right_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_right_padded<_PaddingValue>; > >> + > >> + private: > >> + static constexpr size_t _S_rank = extents_type::rank(); > >> + using _RightPaddedStorage = > __mdspan::_PaddedStorage<_PaddingValue, > >> + _Extents, __mdspan::_RightPaddedLayoutTraits<_S_rank>>; > >> + [[no_unique_address]] _RightPaddedStorage _M_storage; > >> + > >> + static constexpr size_t _S_static_stride = > >> + _RightPaddedStorage::_S_static_stride; > >> + > >> + consteval friend size_t > >> + __mdspan::__get_static_stride<mapping>(); > >> + > >> + constexpr index_type > >> + _M_extent(size_t __r) const noexcept > >> + { return _M_storage._M_extents.extent(__r); } > >> + > >> + constexpr index_type > >> + _M_dynamic_padded_stride() const noexcept > >> + { return _M_storage._M_stride.extent(0); } > >> + > >> + public: > >> + constexpr > >> + mapping() noexcept > >> + { } > >> + > >> + constexpr > >> + mapping(const mapping&) noexcept = default; > >> + > >> + constexpr > >> + mapping(const extents_type& __exts) > >> + : _M_storage(__exts) > >> + { } > >> + > >> + template<__mdspan::__valid_index_type<index_type> _OIndexType> > >> + constexpr mapping(const extents_type& __exts, _OIndexType > __opad) > >> + : _M_storage(__exts, std::move(__opad)) > >> > > Cast to index_type here. > > > >> + { } > >> + > >> + template<typename _OExtents> > >> + requires is_constructible_v<extents_type, _OExtents> > >> + constexpr explicit(!is_convertible_v<_OExtents, extents_type>) > >> + mapping(const layout_right::mapping<_OExtents>& __other) > >> + : _M_storage(__other) > >> + { } > >> + > >> + template<typename _OExtents> > >> + requires is_constructible_v<_OExtents, extents_type> > >> + constexpr explicit(_OExtents::rank() > 0) > >> + mapping(const typename layout_stride::mapping<_OExtents>& > >> __other) > >> + : _M_storage(__other) > >> + { __glibcxx_assert(*this == __other); } > >> + > >> + template<typename _RightPaddedMapping> > >> + requires > __mdspan::__is_right_padded_mapping<_RightPaddedMapping> > >> + && is_constructible_v<extents_type, > >> + typename > >> _RightPaddedMapping::extents_type> > >> + constexpr explicit(_S_rank > 1 && (padding_value != > >> dynamic_extent > >> + || _RightPaddedMapping::padding_value == > dynamic_extent)) > >> + mapping(const _RightPaddedMapping& __other) > >> + : _M_storage(__other) > >> + { } > >> + > >> + template<typename _LeftPaddedMapping> > >> + requires > (__mdspan::__is_left_padded_mapping<_LeftPaddedMapping> > >> + || __mdspan::__mapping_of<layout_left, > _LeftPaddedMapping>) > >> + && (_S_rank <= 1) > >> + && 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 > >> + : _M_storage(__other) > >> + { } > >> + > >> + constexpr mapping& operator=(const mapping&) noexcept = default; > >> + > >> + constexpr const extents_type& > >> + extents() const noexcept { return _M_storage._M_extents; } > >> + > >> + constexpr array<index_type, _S_rank> > >> + strides() const noexcept > >> + { > >> + array<index_type, _S_rank> __ret; > >> + if constexpr (_S_rank > 0) > >> + __ret[_S_rank - 1] = 1; > >> + if constexpr (_S_rank > 1) > >> + __ret[_S_rank - 2] = _M_dynamic_padded_stride(); > >> + if constexpr (_S_rank > 2) > >> + for(size_t __i = _S_rank - 2; __i > 0; --__i) > >> + __ret[__i - 1] = __ret[__i] * _M_extent(__i); > >> + return __ret; > >> + } > >> + > >> + constexpr index_type > >> + required_span_size() const noexcept > >> + { return _M_storage._M_required_span_size(); } > >> > > You can name the matching function in PaddedStorage as > > "required_span_size()" > > without the "_M_prefix". We use _M_prefix as the name is reserved for > > implementation, > > and people are not allowed to do: > > #define _M_required_span_size > > #include <mdspan> > > Or in other words, if they do, is on them. > > > > Similary if the name is used somewhere, like required_span_size, we can > use > > it direclty. > > I think I would prefer using exactly the same name for function that we > > just forward to, > > but I will leave it up to you. > > I feel that would be inconsistent. We use _IndexType a lot, even if > it's a type alias, also _S_rank isn't needed as often since "rank" is > safe and there's several other cases too, I think there should be > some similar cases in the helper for std::extents. > > > > > > >> + > >> + // _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_rightpad( > >> + extents(), _M_storage._M_stride, > >> + static_cast<index_type>(std::move(__indices))...); > >> + } > >> + > >> + static constexpr bool > >> + is_always_exhaustive() noexcept > >> + { return _RightPaddedStorage::_M_is_always_exhaustive(); } > >> + > >> + constexpr bool > >> + is_exhaustive() noexcept > >> + { return _M_storage._M_is_exhaustive(); } > >> + > >> + static constexpr bool > >> + is_always_unique() noexcept { return true; } > >> + > >> + static constexpr bool > >> + is_always_strided() noexcept { return true; } > >> + > >> + static constexpr bool > >> + is_unique() noexcept { return true; } > >> + > >> + static constexpr bool > >> + is_strided() noexcept { return true; } > >> + > >> + constexpr index_type > >> + stride(rank_type __r) const noexcept > >> + { > >> + __glibcxx_assert(__r < _S_rank); > >> + if constexpr (_S_rank <= 1) > >> + return 1; > >> + else > >> + { > >> + if (__r == _S_rank - 1) > >> + return 1; > >> + else if (__r == _S_rank - 2) > >> + return _M_dynamic_padded_stride(); > >> + else > >> + return static_cast<index_type>( > >> + static_cast<size_t>(_M_dynamic_padded_stride()) * > >> + static_cast<size_t>(__mdspan::__fwd_prod( > >> + extents(), __r + 1, _S_rank - 1))); > >> + } > >> + } > >> + > >> + template<typename _RightPaddedMapping> > >> + > requires(__mdspan::__is_right_padded_mapping<_RightPaddedMapping> > >> + && _RightPaddedMapping::extents_type::rank() == > _S_rank) > >> + friend constexpr bool > >> + operator==(const mapping& __self, const _RightPaddedMapping& > >> __other) > >> + noexcept > >> + { > >> + return __self.extents() == __other.extents() > >> + && (_S_rank < 2 || cmp_equal(__self.stride(_S_rank - 2), > >> + __other.stride(_S_rank - > 2))); > >> > > + } > >> > > This could be just __self.extents() == __other.extents() > > && __self._M_stride == other._M_stride; > > I would add _M_equal method to _PaddingStride to handle this, and call it > > from here. > > > > The slight catch is that it requires additional friendlyness > because > > _M_storage._M_equal(__other._M_storage); > > due to _M_storage being private. Instead I did: > > _M_extents == _M_extents > && (_S_rank < 2 || _M_stride.extent(0) == __other.stride(...)); > > the _S_rank < 2 check ensures that we end up with > > true || ... > > which I think we can trust the optimizer to eliminate for us. > And for _S_rank <2 we _M_stride is of type: extents<index_type, 0> Thus _M_stide comparison is just empty. I wanted to benefit here from the fact, that if the strides are know statically, we will have that encoded in types: extents<index_type, 2> == extents<index_type, 3> And all the optimizations you verified happens for comparison of extents. > > > > > > > >> + }; > >> #endif > >> > >> template<typename _ElementType> > >> diff --git a/libstdc++-v3/src/c++23/std.cc.in b/libstdc++-v3/src/c++23/ > >> std.cc.in > >> index f10bab59e2c..c1b4e4c88b7 100644 > >> --- a/libstdc++-v3/src/c++23/std.cc.in > >> +++ b/libstdc++-v3/src/c++23/std.cc.in > >> @@ -1871,9 +1871,10 @@ export namespace std > >> using std::mdspan; > >> #if __glibcxx_padded_layouts > >> using std::layout_left_padded; > >> + using std::layout_right_padded; > >> #endif > >> - // FIXME layout_right_padded, strided_slice, > submdspan_mapping_result, > >> - // full_extent_t, full_extent, submdspan_extents, mdsubspan > >> + // FIXME 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 891471467e1..73b161f5979 100644 > >> --- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc > >> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc > >> @@ -10,7 +10,7 @@ constexpr size_t dyn = std::dynamic_extent; > >> #if __glibcxx_padded_layouts > >> template<typename Layout> > >> constexpr bool > >> - is_padded_layout = is_left_padded<Layout>; > >> + is_padded_layout = is_left_padded<Layout> || is_right_padded<Layout>; > >> #else > >> template<typename> > >> constexpr bool > >> @@ -479,6 +479,7 @@ main() > >> test_all<std::layout_right>(); > >> #ifdef __glibcxx_padded_layouts > >> test_padded_all<std::layout_left_padded>(); > >> + test_padded_all<std::layout_right_padded>(); > >> #endif > >> > >> from_left_or_right::test_all<std::layout_left, std::layout_right>(); > >> diff --git > a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc > >> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc > >> index 236aca8bc2b..c5519afe34f 100644 > >> --- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc > >> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc > >> @@ -142,6 +142,7 @@ main() > >> static_assert(test_all<std::layout_stride>()); > >> #ifdef __glibcxx_padded_layouts > >> static_assert(test_padded_all<std::layout_left_padded>()); > >> + static_assert(test_padded_all<std::layout_right_padded>()); > >> #endif > >> return 0; > >> } > >> diff --git > >> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc > >> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc > >> index 5bf6bf65d3a..57560a6aae7 100644 > >> --- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc > >> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc > >> @@ -374,7 +374,7 @@ template<> > >> > >> #if __glibcxx_padded_layouts > >> template<typename Layout> > >> - requires is_left_padded<Layout> > >> + requires is_left_padded<Layout> || is_right_padded<Layout> > >> struct TestStride2D<Layout> > >> { > >> static constexpr void > >> @@ -458,7 +458,7 @@ template<> > >> > >> #if __glibcxx_padded_layouts > >> template<typename Layout> > >> - requires is_left_padded<Layout> > >> + requires is_left_padded<Layout> || is_right_padded<Layout> > >> struct TestStride3D<Layout> > >> { > >> static constexpr void > >> @@ -702,6 +702,7 @@ main() > >> test_all<std::layout_stride>(); > >> #ifdef __glibcxx_padded_layouts > >> test_padded_all<std::layout_left_padded>(); > >> + test_padded_all<std::layout_right_padded>(); > >> #endif > >> > >> test_has_op_eq<std::layout_right, std::layout_left, false>(); > >> @@ -709,6 +710,7 @@ main() > >> test_has_op_eq<std::layout_left, std::layout_stride, true>(); > >> #ifdef __glibcxx_padded_layouts > >> test_padded_has_op_eq<std::layout_left_padded>(); > >> + test_padded_has_op_eq<std::layout_right_padded>(); > >> #endif > >> > >> test_has_op_eq_peculiar(); > >> diff --git > a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc > >> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc > >> index ea9a8ef3f4b..a607d1ddd02 100644 > >> --- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc > >> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc > >> @@ -670,6 +670,10 @@ main() > >> test_all<std::layout_left_padded>(); > >> static_assert(test_all<std::layout_left_padded>()); > >> > >> + test_all<std::layout_right_padded>(); > >> + static_assert(test_all<std::layout_right_padded>()); > >> + > >> test_from_pad_all<std::layout_left_padded>(); > >> + test_from_pad_all<std::layout_right_padded>(); > >> return 0; > >> } > >> diff --git > >> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_neg.cc > >> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_neg.cc > >> index 29f12aa4de2..d0ac31c2810 100644 > >> --- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_neg.cc > >> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_neg.cc > >> @@ -15,6 +15,7 @@ template<template<size_t> typename Layout> > >> return true; > >> } > >> > static_assert(test_from_extens_representable_sta<std::layout_left_padded>()); > >> // { dg-error "from here" } > >> > +static_assert(test_from_extens_representable_sta<std::layout_right_padded>()); > >> // { dg-error "from here" } > >> > >> template<template<size_t> typename Layout> > >> constexpr bool > >> @@ -28,6 +29,7 @@ template<template<size_t> typename Layout> > >> return true; > >> } > >> > > static_assert(test_from_extents_representable_padded_size<std::layout_left_padded>()); > >> // { dg-error "expansion of" } > >> > +static_assert(test_from_extents_representable_padded_size<std::layout_right_padded>()); > >> // { dg-error "expansion of" } > >> > >> template<template<size_t> typename Layout> > >> constexpr bool > >> @@ -40,6 +42,7 @@ template<template<size_t> typename Layout> > >> return true; > >> } > >> > > static_assert(test_from_extents_representable_stride<std::layout_left_padded>()); > >> // { dg-error "expansion of" } > >> > +static_assert(test_from_extents_representable_stride<std::layout_right_padded>()); > >> // { dg-error "expansion of" } > >> > >> template<template<size_t> typename Layout> > >> constexpr bool > >> @@ -51,6 +54,7 @@ template<template<size_t> typename Layout> > >> return true; > >> } > >> > static_assert(test_from_pad_representable_stride<std::layout_left_padded>()); > >> // { dg-error "expansion of" } > >> > +static_assert(test_from_pad_representable_stride<std::layout_right_padded>()); > >> // { dg-error "expansion of" } > >> > >> template<template<size_t> typename Layout> > >> constexpr bool > >> @@ -62,6 +66,7 @@ template<template<size_t> typename Layout> > >> return true; > >> } > >> > > static_assert(test_from_pad_representable_padded_size<std::layout_left_padded>()); > >> // { dg-error "expansion of" } > >> > +static_assert(test_from_pad_representable_padded_size<std::layout_right_padded>()); > >> // { dg-error "expansion of" } > >> > >> template<template<size_t> typename Layout> > >> constexpr bool > >> @@ -76,6 +81,7 @@ template<template<size_t> typename Layout> > >> return true; > >> } > >> static_assert(test_from_left<std::layout_left_padded>()); // { > dg-error > >> "required from here" } > >> +static_assert(test_from_left<std::layout_right_padded>()); // { > dg-error > >> "required from here" } > >> > >> template<template<size_t> typename Layout> > >> constexpr bool > >> @@ -90,6 +96,7 @@ template<template<size_t> typename Layout> > >> return true; > >> } > >> > static_assert(test_from_left_bad_runtime_stride<std::layout_left_padded>()); > >> // { dg-error "expansion of" } > >> > +static_assert(test_from_left_bad_runtime_stride<std::layout_right_padded>()); > >> // { dg-error "expansion of" } > >> > >> template<template<size_t> typename Layout> > >> constexpr bool > >> @@ -103,6 +110,7 @@ template<template<size_t> typename Layout> > >> return true; > >> } > >> > > static_assert(test_from_left_representable_extents<std::layout_left_padded>()); > >> // { dg-error "expansion of" } > >> > +static_assert(test_from_left_representable_extents<std::layout_right_padded>()); > >> // { dg-error "expansion of" } > >> > >> template<template<size_t> typename Layout, size_t PaddingValue> > >> constexpr bool > >> @@ -116,6 +124,8 @@ template<template<size_t> typename Layout, size_t > >> PaddingValue> > >> } > >> static_assert(test_pad_overflow<std::layout_left_padded, 1>()); // { > >> dg-error "expansion of" } > >> static_assert(test_pad_overflow<std::layout_left_padded, dyn>()); // { > >> dg-error "expansion of" } > >> +static_assert(test_pad_overflow<std::layout_right_padded, 1>()); // { > >> dg-error "expansion of" } > >> +static_assert(test_pad_overflow<std::layout_right_padded, dyn>()); // { > >> dg-error "expansion of" } > >> > >> template<template<size_t> typename Layout, size_t PaddingValue> > >> constexpr bool > >> @@ -128,6 +138,8 @@ template<template<size_t> typename Layout, size_t > >> PaddingValue> > >> } > >> static_assert(test_from_pad_negative<std::layout_left_padded, 1>()); > // > >> { dg-error "expansion of" } > >> static_assert(test_from_pad_negative<std::layout_left_padded, > dyn>()); // > >> { dg-error "expansion of" } > >> +static_assert(test_from_pad_negative<std::layout_right_padded, 1>()); > >> // { dg-error "expansion of" } > >> +static_assert(test_from_pad_negative<std::layout_right_padded, dyn>()); > >> // { dg-error "expansion of" } > >> > >> template<template<size_t> typename Layout, size_t Pad> > >> constexpr bool > >> @@ -142,6 +154,8 @@ template<template<size_t> typename Layout, size_t > Pad> > >> } > >> static_assert(test_static_pad_same<std::layout_left_padded, 1>()); // > { > >> dg-error "expansion of" } > >> static_assert(test_static_pad_same<std::layout_left_padded, 3>()); // > { > >> dg-error "expansion of" } > >> +static_assert(test_static_pad_same<std::layout_right_padded, 1>()); // > { > >> dg-error "expansion of" } > >> +static_assert(test_static_pad_same<std::layout_right_padded, 3>()); // > { > >> dg-error "expansion of" } > >> > >> template<template<size_t> typename Layout> > >> constexpr bool > >> @@ -156,6 +170,7 @@ template<template<size_t> typename Layout> > >> return true; > >> } > >> > static_assert(test_from_stride_wrong_stride0<std::layout_left_padded>()); > >> // { dg-error "expansion of" } > >> > +static_assert(test_from_stride_wrong_stride0<std::layout_right_padded>()); > >> // { dg-error "expansion of" } > >> > >> template<template<size_t> typename Layout> > >> constexpr bool > >> @@ -170,6 +185,7 @@ template<template<size_t> typename Layout> > >> return true; > >> } > >> > static_assert(test_from_stride_wrong_stride1<std::layout_left_padded>()); > >> // { dg-error "expansion of" } > >> > +static_assert(test_from_stride_wrong_stride1<std::layout_right_padded>()); > >> // { dg-error "expansion of" } > >> > >> template<template<size_t> typename Layout> > >> constexpr bool > >> @@ -184,6 +200,7 @@ template<template<size_t> typename Layout> > >> return true; > >> } > >> > static_assert(test_from_stride_wrong_stride2<std::layout_left_padded>()); > >> > +static_assert(test_from_stride_wrong_stride2<std::layout_right_padded>()); > >> > >> template<template<size_t> typename Layout> > >> constexpr bool > >> @@ -200,6 +217,7 @@ template<template<size_t> typename Layout> > >> return true; > >> } > >> static_assert(test_from_stride_oversized<std::layout_left_padded>()); > // > >> { dg-error "expansion of" } > >> +static_assert(test_from_stride_oversized<std::layout_right_padded>()); > // > >> { dg-error "expansion of" } > >> > >> template<template<size_t> typename Layout> > >> constexpr bool > >> @@ -213,6 +231,7 @@ template<template<size_t> typename Layout> > >> return true; > >> } > >> static_assert(test_from_samepad_dyn<std::layout_left_padded>()); // { > >> dg-error "expansion of" } > >> +static_assert(test_from_samepad_dyn<std::layout_right_padded>()); // { > >> dg-error "expansion of" } > >> > >> template<template<size_t> typename Layout> > >> constexpr bool > >> @@ -226,6 +245,7 @@ template<template<size_t> typename Layout> > >> return true; > >> } > >> static_assert(test_from_samepad_sta<std::layout_left_padded>()); // { > >> dg-error "expansion of" } > >> +static_assert(test_from_samepad_sta<std::layout_right_padded>()); // { > >> dg-error "expansion of" } > >> > >> template<template<size_t> typename Layout> > >> constexpr bool > >> @@ -240,6 +260,7 @@ template<template<size_t> typename Layout> > >> return true; > >> } > >> > static_assert(test_from_samepad_oversized<std::layout_left_padded>()); // > >> { dg-error "expansion of" } > >> +static_assert(test_from_samepad_oversized<std::layout_right_padded>()); > >> // { dg-error "expansion of" } > >> > >> template<template<size_t> typename Layout, size_t RunId> > >> constexpr bool > >> @@ -278,6 +299,10 @@ > >> static_assert(test_to_same_not_exhaustive<std::layout_left_padded, > 0>()); > >> // { d > >> static_assert(test_to_same_not_exhaustive<std::layout_left_padded, > 1>()); > >> // { dg-error "expansion of" } > >> static_assert(test_to_same_not_exhaustive<std::layout_left_padded, > 2>()); > >> // { dg-error "expansion of" } > >> static_assert(test_to_same_not_exhaustive<std::layout_left_padded, > 3>()); > >> // { dg-error "expansion of" } > >> +static_assert(test_to_same_not_exhaustive<std::layout_right_padded, > >> 0>()); // { dg-error "expansion of" } > >> +static_assert(test_to_same_not_exhaustive<std::layout_right_padded, > >> 1>()); // { dg-error "expansion of" } > >> +static_assert(test_to_same_not_exhaustive<std::layout_right_padded, > >> 2>()); // { dg-error "expansion of" } > >> +static_assert(test_to_same_not_exhaustive<std::layout_right_padded, > >> 3>()); // { dg-error "expansion of" } > >> > >> template<template<size_t> typename Layout> > >> constexpr bool > >> @@ -290,6 +315,7 @@ template<template<size_t> typename Layout> > >> return true; > >> } > >> > static_assert(test_statically_bad_padding_value1<std::layout_left_padded>()); > >> // { dg-error "required from" } > >> > +static_assert(test_statically_bad_padding_value1<std::layout_right_padded>()); > >> // { dg-error "required from" } > >> > >> template<template<size_t> typename Layout> > >> constexpr bool > >> @@ -301,6 +327,7 @@ template<template<size_t> typename Layout> > >> return true; > >> } > >> > static_assert(test_statically_bad_padding_value2<std::layout_left_padded>()); > >> // { dg-error "required from" } > >> > +static_assert(test_statically_bad_padding_value2<std::layout_right_padded>()); > >> // { dg-error "required from" } > >> > >> template<template<size_t> typename Layout> > >> constexpr bool > >> @@ -312,6 +339,7 @@ template<template<size_t> typename Layout> > >> return true; > >> } > >> static_assert(test_statically_oversized<std::layout_left_padded>()); > // { > >> dg-error "from here" } > >> +static_assert(test_statically_oversized<std::layout_right_padded>()); > // > >> { dg-error "from here" } > >> > >> // { dg-prune-output "padding_value must be representable as > index_type" } > >> // { dg-prune-output "non-constant condition for static assertion" } > >> diff --git > >> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_traits.h > >> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_traits.h > >> index 1f0169d7c02..c267454f467 100644 > >> --- > a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_traits.h > >> +++ > b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_traits.h > >> @@ -2,9 +2,86 @@ > >> #define TEST_MDSPAN_PADDED_TRAITS_H > >> > >> #if __glibcxx_padded_layouts > >> +template<typename Rev, typename Fwd> > >> + struct reverse_impl; > >> + > >> +template<size_t... Ir, size_t I0, size_t... I> > >> + struct reverse_impl<std::integer_sequence<size_t, Ir...>, > >> + std::integer_sequence<size_t, I0, I...>> > >> + { > >> + using type = typename reverse_impl<std::index_sequence<I0, Ir...>, > >> + std::index_sequence<I...>>::type; > >> + }; > >> + > >> +template<size_t... Ir> > >> + struct reverse_impl<std::integer_sequence<size_t, Ir...>, > >> + std::integer_sequence<size_t>> > >> + { > >> + using type = std::index_sequence<Ir...>; > >> + }; > >> + > >> +template<typename I> > >> + struct reverse; > >> + > >> +template<size_t... Ir> > >> + struct reverse<std::index_sequence<Ir...>> > >> + { > >> + using type = typename reverse_impl<std::integer_sequence<size_t>, > >> + std::integer_sequence<size_t, > >> Ir...>>::type; > >> + }; > >> + > >> +template<typename, typename> > >> + struct sequence_to_extents; > >> + > >> +template<typename IndexType, size_t... I> > >> + struct sequence_to_extents<IndexType, std::index_sequence<I...>> > >> + { > >> + using type = std::extents<IndexType, I...>; > >> + }; > >> + > >> +template<typename IndexType, size_t... I> > >> + struct reverse<std::extents<IndexType, I...>> > >> + { > >> + using type = typename sequence_to_extents< > >> + IndexType, > >> + typename reverse<std::index_sequence<I...>>::type > >> + >::type; > >> + }; > >> + > >> +template<typename Extents> > >> + constexpr auto > >> + dynamic_extents_array(const Extents& exts) > >> + { > >> + std::array<typename Extents::index_type, Extents::rank()> ret; > >> + for(size_t i = 0; i < Extents::rank(); ++i) > >> + ret[i] = exts.extent(i); > >> + return ret; > >> + } > >> + > >> +template<typename T, size_t N> > >> + constexpr std::array<T, N> > >> + reversed(const std::array<T, N>& fwd) > >> + { > >> + std::array<T, N> rev; > >> + for(size_t i = 0; i < N; ++i) > >> + rev[(N-1) - i] = fwd[i]; > >> + return rev; > >> + } > >> + > >> +template<typename IndexType, size_t... I> > >> + constexpr auto > >> + reversed(const std::extents<IndexType, I...>& exts) > >> + { > >> + using rev_type = typename reverse<std::extents<IndexType, > >> I...>>::type; > >> + return rev_type(reversed(dynamic_extents_array(exts))); > >> + } > >> + > >> +static_assert(std::array{3, 2, 1} == reversed(std::array{1, 2, 3})); > >> + > >> enum class PaddingSide > >> { > >> - Left > >> + Left, > >> + Right > >> }; > >> > >> template<typename Layout> > >> @@ -13,17 +90,33 @@ template<typename Layout> > >> template<size_t PaddingValue> > >> constexpr static bool > >> is_left_padded<std::layout_left_padded<PaddingValue>> = true; > >> > >> +template<typename Layout> > >> + constexpr static bool is_right_padded = false; > >> + > >> +template<size_t PaddingValue> > >> + constexpr static bool > >> is_right_padded<std::layout_right_padded<PaddingValue>> = true; > >> + > >> struct DeducePaddingSide > >> { > >> template<template<size_t> typename Layout> > >> constexpr static PaddingSide > >> from_template() > >> - { return PaddingSide::Left; } > >> + { > >> + if constexpr (std::same_as<Layout<0>, > std::layout_left_padded<0>>) > >> + return PaddingSide::Left; > >> + else > >> + return PaddingSide::Right; > >> + } > >> > >> template<typename Layout> > >> constexpr static PaddingSide > >> from_typename() > >> - { return PaddingSide::Left; } > >> + { > >> + if constexpr (is_left_padded<Layout>) > >> + return PaddingSide::Left; > >> + else > >> + return PaddingSide::Right; > >> + } > >> }; > >> > >> template<PaddingSide Side> > >> @@ -59,5 +152,41 @@ template<> > >> { return exts.extent(0); } > >> }; > >> > >> +template<> > >> + struct LayoutTraits<PaddingSide::Right> > >> + { > >> + using layout_same = std::layout_right; > >> + using layout_other = std::layout_left; > >> + > >> + template<typename Extents> > >> + using extents_type = typename reverse<Extents>::type; > >> + > >> + template<typename Extents> > >> + constexpr static extents_type<Extents> > >> + make_extents(const Extents& exts) > >> + { return reversed(exts); } > >> + > >> + template<typename T, size_t N> > >> + constexpr static std::array<T, N> > >> + make_expected(const std::array<T, N>& expected) > >> + { return reversed(expected); } > >> + > >> + template<typename Mapping> > >> + constexpr static auto > >> + padded_stride(const Mapping& m) > >> + { > >> + auto rank = Mapping::extents_type::rank(); > >> + return m.stride(rank - 2); > >> + } > >> + > >> + template<typename Extents> > >> + constexpr static auto > >> + padded_extent(const Extents& exts) > >> + { > >> + auto rank = Extents::rank(); > >> + return exts.extent(rank - 1); > >> + } > >> + }; > >> + > >> #endif > >> #endif > >> -- > >> 2.50.1 > >> > >> > > > >
