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

Reply via email to