https://gcc.gnu.org/g:c915bfc0f7d2e27b7ebc5bae1e30cdb1ae078255
commit r16-5838-gc915bfc0f7d2e27b7ebc5bae1e30cdb1ae078255 Author: Luc Grosheintz <[email protected]> Date: Mon Dec 1 09:41:38 2025 +0100 libstdc++: Implement submdspan_extents. [PR110352] Implement submdspan_extents as described in P3663 and adds it to the std module. PR libstdc++/110352 libstdc++-v3/ChangeLog: * include/std/mdspan (submdspan_extents): New function. * src/c++23/std.cc.in: Add submdspan_extents. * testsuite/23_containers/mdspan/int_like.h: Add StructuralInt. * testsuite/23_containers/mdspan/submdspan/submdspan_extents.cc: New test. * testsuite/23_containers/mdspan/submdspan/submdspan_extents_neg.cc: New test. Reviewed-by: Tomasz KamiĆski <[email protected]> Signed-off-by: Luc Grosheintz <[email protected]> Diff: --- libstdc++-v3/include/std/mdspan | 103 +++++++++++++ libstdc++-v3/src/c++23/std.cc.in | 5 +- .../testsuite/23_containers/mdspan/int_like.h | 9 ++ .../mdspan/submdspan/submdspan_extents.cc | 169 +++++++++++++++++++++ .../mdspan/submdspan/submdspan_extents_neg.cc | 48 ++++++ 5 files changed, 333 insertions(+), 1 deletion(-) diff --git a/libstdc++-v3/include/std/mdspan b/libstdc++-v3/include/std/mdspan index 87c2f442a48b..bfec288aded1 100644 --- a/libstdc++-v3/include/std/mdspan +++ b/libstdc++-v3/include/std/mdspan @@ -865,6 +865,31 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return extents<_IndexType, _Extents::static_extent(_Index)>{ __exts.extent(_Index)}; } + + template<typename _IndexType, typename... _Slices> + consteval auto + __subrank() + { + return (static_cast<size_t>(!convertible_to<_Slices, _IndexType>) + + ... + 0); + } + + template<typename _IndexType, typename... _Slices> + consteval auto + __inv_map_rank() + { + constexpr auto __rank = sizeof...(_Slices); + constexpr auto __sub_rank = __subrank<_IndexType, _Slices...>(); + auto __map = std::array<size_t, __sub_rank>{}; + auto __is_int_like = std::array<bool, __rank>{ + convertible_to<_Slices, _IndexType>...}; + + size_t __i = 0; + for (size_t __k = 0; __k < __rank; ++__k) + if (!__is_int_like[__k]) + __map[__i++] = __k; + return __map; + } #endif // __glibcxx_submdspan } @@ -2664,8 +2689,86 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION }; __impl(make_index_sequence<__rank>()); } + + template<typename _IndexType, size_t _Extent, typename _Slice> + consteval size_t + __static_slice_extent() + { + if constexpr (same_as<_Slice, full_extent_t>) + return _Extent; + else if constexpr (same_as<_Slice, constant_wrapper<_IndexType(0)>>) + return 0; + else if constexpr (__is_constant_wrapper<typename _Slice::extent_type> + && __is_constant_wrapper<typename _Slice::stride_type>) + return 1 + ((typename _Slice::extent_type{}) - 1) + / (typename _Slice::stride_type{}); + else + return dynamic_extent; + } + + template<size_t _K, typename _Extents, typename _Slice> + constexpr typename _Extents::index_type + __dynamic_slice_extent(const _Extents& __exts, _Slice __slice) + { + if constexpr (__is_strided_slice<_Slice>) + return __slice.extent == 0 ? 0 : 1 + (__slice.extent - 1) / __slice.stride; + else + return __exts.extent(_K); + } + + template<typename _IndexType, size_t... _Extents, typename... _Slices> + requires (sizeof...(_Slices) == sizeof...(_Extents)) + constexpr auto + __subextents(const extents<_IndexType, _Extents...>& __exts, + _Slices... __slices) + { + constexpr auto __inv_map = __mdspan::__inv_map_rank<_IndexType, _Slices...>(); + auto __impl = [&]<size_t... _Indices>(index_sequence<_Indices...>) + { + using _SubExtents = extents<_IndexType, + (__mdspan::__static_slice_extent<_IndexType, + _Extents...[__inv_map[_Indices]], + _Slices...[__inv_map[_Indices]]>())...>; + if constexpr (_SubExtents::rank_dynamic() == 0) + return _SubExtents{}; + else + { + using _StaticSubExtents = __mdspan::_StaticExtents< + __mdspan::__static_extents<_SubExtents>()>; + auto __create = [&]<size_t... _Is>(index_sequence<_Is...>) + { + constexpr auto __slice_idx = [__inv_map](size_t __i) consteval + { + return __inv_map[_StaticSubExtents::_S_dynamic_index_inv(__i)]; + }; + + return _SubExtents{ + (__mdspan::__dynamic_slice_extent<__slice_idx(_Is)>( + __exts, __slices...[__slice_idx(_Is)]))...}; + }; + constexpr auto __dyn_subrank = _SubExtents::rank_dynamic(); + return __create(make_index_sequence<__dyn_subrank>()); + } + }; + + return __impl(make_index_sequence<__inv_map.size()>()); + } } + template<typename _IndexType, size_t... _Extents, typename... _RawSlices> + requires (sizeof...(_RawSlices) == sizeof...(_Extents)) + constexpr auto + submdspan_extents(const extents<_IndexType, _Extents...>& __exts, + _RawSlices... __raw_slices) + { + auto __impl = [&__exts](auto... __slices) + { + __mdspan::__check_valid_slices(__exts, __slices...); + return __mdspan::__subextents(__exts, __slices...); + }; + return __impl(__mdspan::__slice_cast<_IndexType>(__raw_slices)...); + } + template<typename _IndexType, size_t... _Extents, typename... _RawSlices> requires (sizeof...(_Extents) == sizeof...(_RawSlices)) constexpr auto diff --git a/libstdc++-v3/src/c++23/std.cc.in b/libstdc++-v3/src/c++23/std.cc.in index 63b3d183bf58..c2a9293b05a6 100644 --- a/libstdc++-v3/src/c++23/std.cc.in +++ b/libstdc++-v3/src/c++23/std.cc.in @@ -1877,13 +1877,16 @@ export namespace std #if __glibcxx_padded_layouts using std::layout_left_padded; using std::layout_right_padded; +#endif +#if __glibcxx_submdspan using std::strided_slice; using std::full_extent_t; using std::full_extent; using std::submdspan_mapping_result; using std::submdspan_canonicalize_slices; + using std::submdspan_extents; #endif - // FIXME submdspan_extents, mdsubspan + // FIXME mdsubspan } #endif diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/int_like.h b/libstdc++-v3/testsuite/23_containers/mdspan/int_like.h index 2f79b9dd3b55..b839be73ae94 100644 --- a/libstdc++-v3/testsuite/23_containers/mdspan/int_like.h +++ b/libstdc++-v3/testsuite/23_containers/mdspan/int_like.h @@ -69,4 +69,13 @@ using RValueInt = CustomIndexType<CustomIndexKind::RValue>; struct NotIntLike { }; +struct StructuralInt +{ + constexpr + operator int() const noexcept + { return value; } + + int value; +}; + #endif // TEST_MDSPAN_INT_LIKE_H diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_extents.cc b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_extents.cc new file mode 100644 index 000000000000..841910a77c8c --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_extents.cc @@ -0,0 +1,169 @@ +// { dg-do run { target c++26 } } +#include <mdspan> + +#include "../int_like.h" +#include <testsuite_hooks.h> + +constexpr size_t dyn = std::dynamic_extent; +constexpr auto all = std::full_extent; + +constexpr bool +test_from_full_extent() +{ + auto exts = std::extents<int, 3, dyn, 7>{}; + auto sub_exts = submdspan_extents(exts, 1, all, all); + VERIFY(sub_exts.rank() == 2); + VERIFY(sub_exts.static_extent(0) == dyn); + VERIFY(sub_exts.extent(0) == exts.extent(1)); + VERIFY(std::cmp_equal(sub_exts.static_extent(1), exts.extent(2))); + return true; +} + +template<template<typename, typename> typename Pair, template<int> typename Cw> + constexpr bool + test_from_tuple() + { + auto exts = std::extents<int, 3, 5, 7>{}; + auto s0 = Cw<1>{}; + auto s1 = Pair{Cw<1>{}, Cw<2>{}}; + auto s2 = Pair{Cw<1>{}, 4}; + auto sub_exts = submdspan_extents(exts, s0, s1, s2); + VERIFY(sub_exts.rank() == 2); + VERIFY(sub_exts.static_extent(0) == size_t(get<1>(s1) - get<0>(s1))); + VERIFY(sub_exts.static_extent(1) == dyn); + VERIFY(std::cmp_equal(sub_exts.extent(1), get<1>(s2) - get<0>(s2))); + return true; + } + +template<template<int> typename Cw> + constexpr bool + test_from_tuple_all() + { + test_from_tuple<std::tuple, Cw>(); + test_from_tuple<std::pair, Cw>(); + return true; + } + + +template<typename Int> + void + test_from_int_like_as_scalar() + { + auto exts = std::extents<int, 3, 5>{}; + auto sub_exts = submdspan_extents(exts, Int(1), std::tuple{1, 3}); + VERIFY(sub_exts.rank() == 1); + VERIFY(sub_exts.static_extent(0) == dyn); + VERIFY(sub_exts.extent(0) == 2); + } + +template<template<int> typename Cw> + constexpr bool + test_from_const_int() + { + auto exts = std::extents<int, 3, 5>{}; + auto sub_exts = submdspan_extents(exts, Cw<1>{}, std::tuple{1, 3}); + VERIFY(sub_exts.rank() == 1); + VERIFY(sub_exts.static_extent(0) == dyn); + VERIFY(sub_exts.extent(0) == 2); + return true; + } + +template<typename Int> + constexpr bool + test_from_int_like_in_tuple() + { + auto exts = std::extents<int, 3, 5>{}; + auto sub_exts = submdspan_extents(exts, Int(1), std::tuple{Int(1), Int(3)}); + VERIFY(sub_exts.rank() == 1); + VERIFY(sub_exts.static_extent(0) == dyn); + VERIFY(sub_exts.extent(0) == 2); + return true; + } + +template<template<int> typename Cw> + constexpr bool + test_from_strided_slice() + { + auto exts = std::extents<int, 5, 7, 11>{}; + { + auto s0 = 1; + auto s1 = std::strided_slice{0, 0, 0}; + auto s2 = std::strided_slice{1, Cw<0>{}, 0}; + auto sub_exts = submdspan_extents(exts, s0, s1, s2); + VERIFY(sub_exts.rank() == 2); + VERIFY(sub_exts.static_extent(0) == dyn); + VERIFY(sub_exts.extent(0) == 0); + VERIFY(sub_exts.static_extent(1) == 0); + } + + { + auto s0 = 1; + auto s1 = std::strided_slice{0, 2, Cw<1>{}}; + auto s2 = std::strided_slice{1, Cw<2>{}, 1}; + auto sub_exts = submdspan_extents(exts, s0, s1, s2); + VERIFY(sub_exts.rank() == 2); + VERIFY(sub_exts.static_extent(0) == dyn); + VERIFY(sub_exts.extent(0) == 2); + VERIFY(sub_exts.static_extent(1) == dyn); + VERIFY(sub_exts.extent(1) == 2); + } + + { + // selected = 1 x [1, 3] x [1, 4, 7, 10] + auto s0 = 1; + auto s1 = std::strided_slice{1, Cw<4>{}, 2}; + auto s2 = std::strided_slice{1, Cw<10>{}, Cw<3>{}}; + auto sub_exts = submdspan_extents(exts, s0, s1, s2); + VERIFY(sub_exts.rank() == 2); + VERIFY(sub_exts.static_extent(0) == dyn); + VERIFY(sub_exts.extent(0) == 2); + VERIFY(sub_exts.static_extent(1) == 4); + } + + { + // selected = [0, 2] x [1, 3] x [0, 3, 6] + auto s0 = std::strided_slice(0, 3, 2); + auto s1 = std::strided_slice(1, 4, 2); + auto s2 = std::strided_slice(0, 7, 3); + auto sub_exts = submdspan_extents(exts, s0, s1, s2); + VERIFY(sub_exts.rank() == 3); + VERIFY(sub_exts.extent(0) == 2); + VERIFY(sub_exts.extent(1) == 2); + VERIFY(sub_exts.extent(2) == 3); + } + return true; + } + +template<int Value> + using CW = std::constant_wrapper<Value, int>; + +template<int Value> + using IC = std::integral_constant<int, Value>; + +constexpr bool +test_all() +{ + test_from_full_extent(); + test_from_tuple_all<CW>(); + test_from_tuple_all<IC>(); + test_from_const_int<CW>(); + test_from_const_int<IC>(); + test_from_strided_slice<CW>(); + test_from_strided_slice<IC>(); + test_from_int_like_in_tuple<StructuralInt>(); + return true; +} + +int +main() +{ + test_all(); + static_assert(test_all()); + + test_from_int_like_as_scalar<CustomIndexType<CustomIndexKind::Const, true>>(); + test_from_int_like_as_scalar<CustomIndexType<CustomIndexKind::Throwing, true>>(); + test_from_int_like_as_scalar<CustomIndexType<CustomIndexKind::Mutating, true>>(); + test_from_int_like_as_scalar<CustomIndexType<CustomIndexKind::RValue, true>>(); + test_from_int_like_as_scalar<StructuralInt>(); + return 0; +} diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_extents_neg.cc b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_extents_neg.cc new file mode 100644 index 000000000000..cf27c0c7e4d6 --- /dev/null +++ b/libstdc++-v3/testsuite/23_containers/mdspan/submdspan/submdspan_extents_neg.cc @@ -0,0 +1,48 @@ +// { dg-do compile { target c++26 } } +#include <mdspan> + +#include <cstdint> + +struct NotASlice +{ }; + +constexpr bool +test_unrelated_stride_type() +{ + auto exts = std::extents(3, 5, 7); + auto sub_exts = submdspan_extents(exts, 1, NotASlice{}, 2); // { dg-error "required from" } + return true; +} +static_assert(test_unrelated_stride_type()); + +constexpr bool +test_invalid_stride_zero() +{ + auto exts = std::extents(3, 5, 7); + auto s = std::strided_slice{0, 1, 0}; + auto sub_exts = submdspan_extents(exts, 1, s, 2); // { dg-error "expansion of" } + return true; +} +static_assert(test_invalid_stride_zero()); + +template<typename Slice> +constexpr bool +test_out_of_bounds(const Slice& slice) +{ + auto exts = std::extents<uint16_t, 3, 5, 7>{}; + auto sub_exts = submdspan_extents(exts, 1, slice, 2); // { dg-error "expansion of" } + return true; +} +static_assert(test_out_of_bounds(std::strided_slice{0, 6, 1})); // { dg-error "expansion of" } +static_assert(test_out_of_bounds(std::strided_slice{0, 7, 2})); // { dg-error "expansion of" } +static_assert(test_out_of_bounds(std::strided_slice{1, 6, 1})); // { dg-error "expansion of" } +static_assert(test_out_of_bounds(std::strided_slice{1, 6, 2})); // { dg-error "expansion of" } +static_assert(test_out_of_bounds(std::tuple{1, 6})); // { dg-error "expansion of" } +static_assert(test_out_of_bounds(std::tuple{std::cw<1>, std::cw<6>})); // { dg-error "expansion of" } +static_assert(test_out_of_bounds(std::strided_slice{-1, 2, 1})); // { dg-error "expansion of" } +static_assert(test_out_of_bounds(std::tuple{-1, 2})); // { dg-error "expansion of" } + +// { dg-prune-output "cannot decompose class type 'NotASlice'" } +// { dg-prune-output "static assertion failed" } +// { dg-prune-output "__glibcxx_assert_fail" } +// { dg-prune-output "non-constant condition" }
