On Thu, 20 Nov 2025 at 13:56, Tomasz Kaminski <[email protected]> wrote:
>
>
>
> On Tue, Nov 18, 2025 at 3:25 PM Luc Grosheintz <[email protected]>
> wrote:
>>
>> The changes needed for submdspan are:
>>
>> * In submdspan related code the user-defined integer-like
>> types need to be copy- and move-constructable.
>>
>> * The traits for writing tests that work with both left- and right,
>> possibly padded, layouts will also be useful for submdspan.
>> Therefore, this code is moved up and generalized.
>>
>> * Move __offset further up in <mdspan> and fix some formatting
>> mistakes.
>>
>> * include/std/mdspan: Improve formatting and placement.
>> * testsuite/23_containers/mdspan/int_like.h: Optionally,
>> add move- and copy-ctors.
>> * testsuite/23_containers/mdspan/layouts/padded_traits.h: Move to...
>> * testsuite/23_containers/mdspan/layout_traits.h: ...here.
>> * testsuite/23_containers/mdspan/layouts/ctors.cc: Fix include.
>> * 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.
>>
>> Signed-off-by: Luc Grosheintz <[email protected]>
>> ---
>
> LGTM, one very minor comment, but the change is not required.
>>
>> libstdc++-v3/include/std/mdspan | 52 ++++++++-------
>> .../testsuite/23_containers/mdspan/int_like.h | 25 ++++---
>> .../padded_traits.h => layout_traits.h} | 65 +++++++++++++++----
>> .../23_containers/mdspan/layouts/ctors.cc | 2 +-
>> .../23_containers/mdspan/layouts/mapping.cc | 2 +-
>> .../23_containers/mdspan/layouts/padded.cc | 2 +-
>> .../mdspan/layouts/padded_neg.cc | 2 +-
>> 7 files changed, 103 insertions(+), 47 deletions(-)
>> rename libstdc++-v3/testsuite/23_containers/mdspan/{layouts/padded_traits.h
>> => layout_traits.h} (70%)
>>
>> diff --git a/libstdc++-v3/include/std/mdspan
>> b/libstdc++-v3/include/std/mdspan
>> index bd7a2a201a7..0a49f24fd3f 100644
>> --- a/libstdc++-v3/include/std/mdspan
>> +++ b/libstdc++-v3/include/std/mdspan
>> @@ -338,21 +338,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>>
>> #if __glibcxx_submdspan
>> struct full_extent_t
>> {
>> explicit full_extent_t() = default;
>> };
>>
>> inline constexpr full_extent_t full_extent{};
>>
>> template<typename _OffsetType, typename _ExtentType, typename _StrideType>
>> - struct strided_slice {
>> + struct strided_slice
>> + {
>> static_assert(__is_standard_integer<_OffsetType>::value
>> || __detail::__integral_constant_like<_OffsetType>);
>> static_assert(__is_standard_integer<_ExtentType>::value
>> || __detail::__integral_constant_like<_ExtentType>);
>> static_assert(__is_standard_integer<_StrideType>::value
>> || __detail::__integral_constant_like<_StrideType>);
>>
>> using offset_type = _OffsetType;
>> using extent_type = _ExtentType;
>> using stride_type = _StrideType;
>> @@ -361,21 +362,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> [[no_unique_address]] extent_type extent{};
>> [[no_unique_address]] stride_type stride{};
>> };
>>
>> template<typename _Mapping>
>> struct submdspan_mapping_result
>> {
>> [[no_unique_address]] _Mapping mapping = _Mapping();
>> size_t offset{};
>> };
>> -#endif
>> +#endif // __glibcxx_submdspan
>>
>> template<typename _IndexType, size_t... _Extents>
>> class extents
>> {
>> static_assert(__is_standard_integer<_IndexType>::value,
>> "IndexType must be a signed or unsigned integer type");
>> static_assert(
>> (__mdspan::__valid_static_extent<_Extents, _IndexType> && ...),
>> "Extents must either be dynamic or representable as IndexType");
>>
>> @@ -546,21 +547,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> __ret *= size_t(__factor);
>> return static_cast<typename _Extents::index_type>(__ret);
>> }
>>
>> // 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>(__begin, __end);
>> + span<const size_t> __sta_exts
>> + = __static_extents<_Extents>(__begin, __end);
>> size_t __ret = 1;
>> for(auto __ext : __sta_exts)
>> if (__ext != dynamic_extent)
>> __ret *= __ext;
>> return __ret;
>> }();
>> return __extents_prod(__exts, __sta_prod, __begin, __end);
>> }
>>
>> template<typename _Extents>
>> @@ -762,21 +764,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> }
>> }
>>
>> template<typename _Extents, typename _IndexType>
>> concept __representable_size = _Extents::rank_dynamic() != 0
>> || __contains_zero(__static_extents<_Extents>())
>> || (__static_quotient<_Extents, _IndexType>() != 0);
>>
>> template<typename _Layout, typename _Mapping>
>> concept __mapping_of =
>> - is_same_v<typename _Layout::template mapping<typename
>> _Mapping::extents_type>,
>> + is_same_v<typename _Layout::template mapping<
>> + typename _Mapping::extents_type>,
>> _Mapping>;
>>
>> template<template<size_t> typename _Layout, typename _Mapping>
>> concept __padded_mapping_of = __mapping_of<
>> _Layout<_Mapping::padding_value>, _Mapping>;
>>
>> #ifdef __glibcxx_padded_layouts
>> template<typename _Mapping>
>> constexpr bool __is_left_padded_mapping = __padded_mapping_of<
>> layout_left_padded, _Mapping>;
>> @@ -797,20 +800,39 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> || __mapping_of<layout_stride,
>> _Mapping>
>> #ifdef __glibcxx_padded_layouts
>> || __is_left_padded_mapping<_Mapping>
>> || __is_right_padded_mapping<_Mapping>
>> #endif
>> ;
>>
>> // A tag type to create internal ctors.
>> class __internal_ctor
>> { };
>> +
>> + template<typename _Mapping>
>> + constexpr typename _Mapping::index_type
>> + __offset(const _Mapping& __m) noexcept
>> + {
>> + using _IndexType = typename _Mapping::index_type;
>> + constexpr auto __rank = _Mapping::extents_type::rank();
>> +
>> + if constexpr (__standardized_mapping<_Mapping>)
>> + return 0;
>> + else if (__empty(__m.extents()))
>> + return 0;
>> + else
>> + {
>> + auto __impl = [&__m]<size_t...
>> _Counts>(index_sequence<_Counts...>)
>> + { return __m(((void) _Counts, _IndexType(0))...); };
>> + return __impl(make_index_sequence<__rank>());
>> + }
>> + }
>> }
>>
>> template<typename _Extents>
>> class layout_left::mapping
>> {
>> public:
>> 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;
>> @@ -1030,21 +1052,22 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> extents_type>)
>> 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)
>> {
>> - if constexpr (extents_type::static_extent(__rank - 1) !=
>> dynamic_extent
>> + if constexpr (
>> + extents_type::static_extent(__rank - 1) != dynamic_extent
>
> The 80 collumn limit is our soft limit, and 100 is the max, I think for here
> I preffer going slightly
> over it than having condition in next line.
I agree. Feel free to make that change when pushing if you want to.
OK for trunk.
>>
>> && __ostride_sta != dynamic_extent)
>> static_assert(extents_type::static_extent(__rank - 1)
>> == __ostride_sta);
>> else
>> __glibcxx_assert(__other.stride(__rank - 2)
>> == __other.extents().extent(__rank - 1));
>> }
>> }
>> #endif
>>
>> @@ -1129,39 +1152,20 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> {
>> requires __is_extents<typename _Mp::extents_type>;
>> { _Mp::is_always_strided() } -> same_as<bool>;
>> { _Mp::is_always_exhaustive() } -> same_as<bool>;
>> { _Mp::is_always_unique() } -> same_as<bool>;
>> bool_constant<_Mp::is_always_strided()>::value;
>> bool_constant<_Mp::is_always_exhaustive()>::value;
>> bool_constant<_Mp::is_always_unique()>::value;
>> };
>>
>> - template<typename _Mapping>
>> - constexpr typename _Mapping::index_type
>> - __offset(const _Mapping& __m) noexcept
>> - {
>> - using _IndexType = typename _Mapping::index_type;
>> - constexpr auto __rank = _Mapping::extents_type::rank();
>> -
>> - if constexpr (__standardized_mapping<_Mapping>)
>> - return 0;
>> - else if (__empty(__m.extents()))
>> - return 0;
>> - else
>> - {
>> - auto __impl = [&__m]<size_t...
>> _Counts>(index_sequence<_Counts...>)
>> - { return __m(((void) _Counts, _IndexType(0))...); };
>> - return __impl(make_index_sequence<__rank>());
>> - }
>> - }
>> -
>> template<typename _Mapping, typename... _Indices>
>> constexpr typename _Mapping::index_type
>> __linear_index_strides(const _Mapping& __m, _Indices... __indices)
>> noexcept
>> {
>> using _IndexType = typename _Mapping::index_type;
>> _IndexType __res = 0;
>> if constexpr (sizeof...(__indices) > 0)
>> {
>> auto __update = [&, __pos = 0u](_IndexType __idx) mutable
>> diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/int_like.h
>> b/libstdc++-v3/testsuite/23_containers/mdspan/int_like.h
>> index e9172c13455..2f79b9dd3b5 100644
>> --- a/libstdc++-v3/testsuite/23_containers/mdspan/int_like.h
>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/int_like.h
>> @@ -2,38 +2,47 @@
>> #define TEST_MDSPAN_INT_LIKE_H
>>
>> enum class CustomIndexKind
>> {
>> Const,
>> Throwing,
>> Mutating,
>> RValue,
>> };
>>
>> -template<CustomIndexKind Kind>
>> +template<CustomIndexKind Kind, bool Copyable = false>
>> class CustomIndexType
>> {
>> public:
>> explicit
>> CustomIndexType(int i)
>> : value(i)
>> { }
>>
>> - CustomIndexType() = delete;
>> - CustomIndexType(const CustomIndexType&) = delete;
>> - CustomIndexType(CustomIndexType&&) = delete;
>> + CustomIndexType() requires(Copyable) = default;
>> + CustomIndexType() requires(!Copyable) = delete;
>>
>> - const CustomIndexType&
>> - operator=(const CustomIndexType&) = delete;
>> + CustomIndexType(const CustomIndexType&) requires(Copyable) = default;
>> + CustomIndexType(const CustomIndexType&) requires(!Copyable) = delete;
>>
>> - const CustomIndexType&
>> - operator=(CustomIndexType&&) = delete;
>> + CustomIndexType(CustomIndexType&&) requires(Copyable) = default;
>> + CustomIndexType(CustomIndexType&&) requires(!Copyable) = delete;
>> +
>> + CustomIndexType&
>> + operator=(const CustomIndexType&) requires(Copyable) = default;
>> + CustomIndexType&
>> + operator=(const CustomIndexType&) requires(!Copyable) = delete;
>> +
>> + CustomIndexType&
>> + operator=(CustomIndexType&&) requires(Copyable) = default;
>> + CustomIndexType&
>> + operator=(CustomIndexType&&) requires(!Copyable) = delete;
>>
>> constexpr
>> operator int() const noexcept
>> requires (Kind == CustomIndexKind::Const)
>> { return value; }
>>
>> constexpr
>> operator int() const
>> requires (Kind == CustomIndexKind::Throwing)
>> { return value; }
>> diff --git
>> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_traits.h
>> b/libstdc++-v3/testsuite/23_containers/mdspan/layout_traits.h
>> similarity index 70%
>> rename from
>> libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_traits.h
>> rename to libstdc++-v3/testsuite/23_containers/mdspan/layout_traits.h
>> index 788ae82fcc4..619cab53b9e 100644
>> --- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_traits.h
>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layout_traits.h
>> @@ -1,15 +1,21 @@
>> -#ifndef TEST_MDSPAN_PADDED_TRAITS_H
>> -#define TEST_MDSPAN_PADDED_TRAITS_H
>> +#ifndef TEST_MDSPAN_LAYOUT_TRAITS_H
>> +#define TEST_MDSPAN_LAYOUT_TRAITS_H
>>
>> #include <algorithm>
>>
>> +enum class PaddingSide
>> +{
>> + Left,
>> + Right
>> +};
>> +
>> template<typename Layout>
>> constexpr static bool is_left_padded = false;
>>
>> #if __cplusplus > 202302L
>> template<size_t PaddingValue>
>> constexpr static bool
>> is_left_padded<std::layout_left_padded<PaddingValue>>
>> = true;
>> #endif
>>
>> template<typename Layout>
>> @@ -19,53 +25,61 @@ template<typename Layout>
>> template<size_t PaddingValue>
>> constexpr static bool
>> is_right_padded<std::layout_right_padded<PaddingValue>>
>> = true;
>> #endif
>>
>> template<typename Layout>
>> constexpr bool
>> is_padded_layout = is_left_padded<Layout> || is_right_padded<Layout>;
>>
>> #if __cplusplus > 202302L
>> +template<PaddingSide Side, typename Layout>
>> + constexpr bool
>> + is_same_padded;
>> +
>> +template<typename Layout>
>> + constexpr bool
>> + is_same_padded<PaddingSide::Left, Layout> = is_left_padded<Layout>;
>> +
>> +template<typename Layout>
>> + constexpr bool
>> + is_same_padded<PaddingSide::Right, Layout> = is_right_padded<Layout>;
>> +
>> 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;
>> }
>>
>> -enum class PaddingSide
>> -{
>> - Left,
>> - Right
>> -};
>> -
>> struct DeducePaddingSide
>> {
>> template<template<size_t> typename Layout>
>> constexpr static PaddingSide
>> from_template()
>> {
>> 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()
>> {
>> - if constexpr (is_left_padded<Layout>)
>> + if constexpr (std::same_as<Layout, std::layout_left>)
>> + return PaddingSide::Left;
>> + else if constexpr (is_left_padded<Layout>)
>> return PaddingSide::Left;
>> else
>> return PaddingSide::Right;
>> }
>> };
>>
>> template<PaddingSide Side>
>> struct LayoutTraits;
>>
>> template<>
>> @@ -77,22 +91,32 @@ template<>
>> template<typename Extents>
>> using extents_type = Extents;
>>
>> template<typename Extents>
>> constexpr static extents_type<Extents>
>> make_extents(const Extents& exts)
>> { return exts; }
>>
>> template<typename T, size_t N>
>> constexpr static std::array<T, N>
>> - make_array(const std::array<T, N>& expected)
>> - { return expected; }
>> + make_array(const std::array<T, N>& a)
>> + { return a; }
>> +
>> + template<typename... Indices>
>> + constexpr static auto
>> + make_indices(Indices... indices)
>> + { return std::array{indices...}; }
>> +
>> + template<typename... Ts>
>> + constexpr static std::tuple<Ts...>
>> + make_tuple(const std::tuple<Ts...>& tup)
>> + { return tup; }
>>
>> template<typename Mapping>
>> constexpr static auto
>> padded_stride(const Mapping& m)
>> { return m.stride(1); }
>>
>> template<typename Extents>
>> constexpr static auto
>> padded_extent(const Extents& exts)
>> { return exts.extent(0); }
>> @@ -121,20 +145,39 @@ template<>
>> using extents_type = decltype(make_extents(std::declval<Extents>()));
>>
>> template<typename T, size_t N>
>> constexpr static std::array<T, N>
>> make_array(std::array<T, N> a)
>> {
>> std::ranges::reverse(a);
>> return a;
>> }
>>
>> + template<typename... Indices>
>> + constexpr static auto
>> + make_indices(Indices... indices)
>> + { return make_array(std::array{indices...}); }
>> +
>> + template<typename... Ts>
>> + constexpr static auto
>> + make_tuple(const std::tuple<Ts...>& tup)
>> + {
>> + constexpr size_t rank = sizeof...(Ts);
>> + auto impl = [&]<size_t... I>(std::index_sequence<I...>)
>> + {
>> + auto idx = [rank](size_t i) consteval
>> + { return rank - 1 - i; };
>> + return std::tuple<Ts...[idx(I)]...>{get<idx(I)>(tup)...};
>> + };
>> + return impl(std::make_index_sequence<rank>());
>> + }
>> +
>> 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
>> diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
>> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
>> index 27065a0dfc6..80ae5d8d56a 100644
>> --- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
>> @@ -1,14 +1,14 @@
>> // { dg-do run { target c++23 } }
>> #include <mdspan>
>>
>> -#include "padded_traits.h"
>> +#include "../layout_traits.h"
>> #include <cstdint>
>> #include <testsuite_hooks.h>
>>
>> constexpr size_t dyn = std::dynamic_extent;
>>
>> template<typename Mapping, typename IndexType, size_t... Extents>
>> constexpr void
>> verify(std::extents<IndexType, Extents...> oexts)
>> {
>> auto m = Mapping(oexts);
>> diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc
>> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc
>> index d1486e4e11c..17cfac54113 100644
>> --- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc
>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc
>> @@ -1,15 +1,15 @@
>> // { dg-do run { target c++23 } }
>> #include <mdspan>
>>
>> #include "../int_like.h"
>> -#include "padded_traits.h"
>> +#include "../layout_traits.h"
>> #include <cstdint>
>> #include <testsuite_hooks.h>
>>
>> constexpr size_t dyn = std::dynamic_extent;
>>
>> template<typename Mapping>
>> concept has_static_is_exhaustive = requires
>> {
>> { Mapping::is_exhaustive() } -> std::same_as<bool>;
>> };
>> diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc
>> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc
>> index cf4821a3c74..19fdf93ce0d 100644
>> --- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc
>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded.cc
>> @@ -1,16 +1,16 @@
>> // { dg-do run { target c++26 } }
>> #include <mdspan>
>>
>> #include <cstdint>
>> #include "../int_like.h"
>> -#include "padded_traits.h"
>> +#include "../layout_traits.h"
>> #include <testsuite_hooks.h>
>>
>> constexpr size_t dyn = std::dynamic_extent;
>>
>> template<template<size_t> typename Layout>
>> constexpr bool
>> test_representable_padded_size()
>> {
>> using Traits = LayoutTraits<DeducePaddingSide::from_template<Layout>()>;
>> {
>> 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 a758f74287f..4073f683822 100644
>> --- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_neg.cc
>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/padded_neg.cc
>> @@ -1,14 +1,14 @@
>> // { dg-do compile { target c++26 } }
>> #include <mdspan>
>>
>> -#include "padded_traits.h"
>> +#include "../layout_traits.h"
>> #include <cstdint>
>>
>> constexpr size_t dyn = std::dynamic_extent;
>>
>> template<template<size_t> typename Layout>
>> constexpr bool
>> test_from_extens_representable_sta()
>> {
>> using E1 = std::extents<uint8_t, 8, 128>;
>> auto m = typename Layout<dyn>::mapping(E1{}); // { dg-error "required
>> from" }
>> --
>> 2.51.2
>>