On Wed, Jun 4, 2025 at 12:36 PM Luc Grosheintz <luc.groshei...@gmail.com> wrote:
> > > On 6/3/25 15:10, Tomasz Kaminski wrote: > > On Tue, Jun 3, 2025 at 2:50 PM Luc Grosheintz <luc.groshei...@gmail.com> > > wrote: > > > >> > >> > >> On 6/3/25 14:31, Tomasz Kaminski wrote: > >>> On Mon, Jun 2, 2025 at 9:07 AM Luc Grosheintz < > luc.groshei...@gmail.com> > >>> wrote: > >>> > >>>> > >>>> > >>>> On 5/30/25 18:42, Luc Grosheintz wrote: > >>>>> Implements a suite of tests for the currently implemented parts of > >>>>> layout_left. The individual tests are templated over the layout type, > >> to > >>>>> allow reuse as more layouts are added. > >>>>> > >>>>> libstdc++-v3/ChangeLog: > >>>>> > >>>>> * > testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc: > >> New > >>>> test. > >>>>> * testsuite/23_containers/mdspan/layouts/ctors.cc: New test. > >>>>> * testsuite/23_containers/mdspan/layouts/empty.cc: New test. > >>>>> * testsuite/23_containers/mdspan/layouts/mapping.cc: New > test. > >>>>> > >>>> > >>>> CI complains that empty.cc contains a bug on 32-bit systems. I'll > >> explain > >>>> below. > >>>> > >>>>> Signed-off-by: Luc Grosheintz <luc.groshei...@gmail.com> > >>>>> --- > >>>>> .../mdspan/layouts/class_mandate_neg.cc | 36 ++ > >>>>> .../23_containers/mdspan/layouts/ctors.cc | 280 +++++++++++ > >>>>> .../23_containers/mdspan/layouts/empty.cc | 67 +++ > >>>>> .../23_containers/mdspan/layouts/mapping.cc | 437 > >> ++++++++++++++++++ > >>>>> 4 files changed, 820 insertions(+) > >>>>> create mode 100644 > >>>> > libstdc++-v3/testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc > >>>>> create mode 100644 > >>>> libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc > >>>>> create mode 100644 > >>>> libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc > >>>>> create mode 100644 > >>>> libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc > >>>>> > >>>>> diff --git > >>>> > >> > a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc > >>>> > >> > b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc > >>>>> new file mode 100644 > >>>>> index 00000000000..62526140200 > >>>>> --- /dev/null > >>>>> +++ > >>>> > >> > b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc > >>>>> @@ -0,0 +1,36 @@ > >>>>> +// { dg-do compile { target c++23 } } > >>>>> +#include<mdspan> > >>>>> + > >>>>> +#include <cstdint> > >>>>> + > >>>>> +constexpr size_t dyn = std::dynamic_extent; > >>>>> +static constexpr size_t n = std::numeric_limits<uint8_t>::max() / 2; > >>>>> + > >>>>> +template<typename Layout> > >>>>> + struct A > >>>>> + { > >>>>> + typename Layout::mapping<std::extents<uint8_t, n, 2>> m0; > >>>>> + typename Layout::mapping<std::extents<uint8_t, n, 2, dyn>> m1; > >>>>> + typename Layout::mapping<std::extents<uint8_t, n, 2, 0>> m2; > >>>>> + > >>>>> + using extents_type = std::extents<uint8_t, n, 4>; > >>>>> + typename Layout::mapping<extents_type> m3; // { dg-error > "required > >>>> from" } > >>>>> + }; > >>>>> + > >>>>> +template<size_t Count, typename Layout, typename OLayout> > >>>>> + struct B // { dg-error "expansion of" } > >>>>> + { > >>>>> + using Extents = std::extents<uint8_t, dyn, dyn, Count>; > >>>>> + using OExtents = std::extents<uint16_t, n, 4, Count>; > >>>>> + > >>>>> + using Mapping = typename Layout::mapping<Extents>; > >>>>> + using OMapping = typename OLayout::mapping<OExtents>; > >>>>> + > >>>>> + Mapping m{OMapping{}}; > >>>>> + }; > >>>>> + > >>>>> +A<std::layout_left> a_left; // { dg-error > >>>> "required from" } > >>>>> + > >>>>> +B<1, std::layout_left, std::layout_left> bll; // { dg-error > >>>> "required here" } > >>>>> + > >>>>> +// { dg-prune-output "must be representable as index_type" } > >>>>> diff --git > >>>> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc > >>>> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc > >>>>> new file mode 100644 > >>>>> index 00000000000..8e1fd2d9524 > >>>>> --- /dev/null > >>>>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc > >>>>> @@ -0,0 +1,280 @@ > >>>>> +// { dg-do run { target c++23 } } > >>>>> +#include <mdspan> > >>>>> + > >>>>> +#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); > >>>>> + VERIFY(m.extents() == oexts); > >>>>> + } > >>>>> + > >>>>> +template<typename Mapping, typename OMapping> > >>>>> + requires (requires { typename OMapping::layout_type; }) > >>>>> + constexpr void > >>>>> + verify(OMapping other) > >>>>> + { > >>>>> + constexpr auto rank = Mapping::extents_type::rank(); > >>>>> + auto m = Mapping(other); > >>>>> + VERIFY(m.extents() == other.extents()); > >>>>> + if constexpr (rank > 0) > >>>>> + for(size_t i = 0; i < rank; ++i) > >>>>> + VERIFY(std::cmp_equal(m.stride(i), other.stride(i))); > >>>>> + } > >>>>> + > >>>>> + > >>>>> +template<typename To, typename From> > >>>>> + constexpr void > >>>>> + verify_convertible(From from) > >>>>> + { > >>>>> + static_assert(std::is_convertible_v<From, To>); > >>>>> + verify<To>(from); > >>>>> + } > >>>>> + > >>>>> +template<typename To, typename From> > >>>>> + constexpr void > >>>>> + verify_nothrow_convertible(From from) > >>>>> + { > >>>>> + static_assert(std::is_nothrow_constructible_v<To, From>); > >>>>> + verify_convertible<To>(from); > >>>>> + } > >>>>> + > >>>>> +template<typename To, typename From> > >>>>> + constexpr void > >>>>> + verify_constructible(From from) > >>>>> + { > >>>>> + static_assert(!std::is_convertible_v<From, To>); > >>>>> + static_assert(std::is_constructible_v<To, From>); > >>>>> + verify<To>(from); > >>>>> + } > >>>>> + > >>>>> +template<typename To, typename From> > >>>>> + constexpr void > >>>>> + verify_nothrow_constructible(From from) > >>>>> + { > >>>>> + static_assert(std::is_nothrow_constructible_v<To, From>); > >>>>> + verify_constructible<To>(from); > >>>>> + } > >>>>> + > >>>>> +template<typename Mapping, typename OExtents> > >>>>> + constexpr void > >>>>> + assert_not_constructible() > >>>>> + { > >>>>> + static_assert(!std::is_constructible_v<Mapping, OExtents>); > >>>>> + } > >>>>> + > >>>>> +// ctor: mapping() > >>>>> +namespace default_ctor > >>>>> +{ > >>>>> + template<typename Layout, typename Extents> > >>>>> + constexpr void > >>>>> + test_default_ctor() > >>>>> + { > >>>>> + using Mapping = typename Layout::mapping<Extents>; > >>>>> + > >>>>> + Mapping m; > >>>>> + for(size_t i = 0; i < Extents::rank(); ++i) > >>>>> + { > >>>>> + if (Extents::static_extent(i) == std::dynamic_extent) > >>>>> + VERIFY(m.extents().extent(i) == 0); > >>>>> + else > >>>>> + VERIFY(m.extents().static_extent(i) == > >>>> Extents::static_extent(i)); > >>>>> + } > >>>>> + } > >>>>> + > >>>>> + template<typename Layout> > >>>>> + constexpr bool > >>>>> + test_default_ctor_all() > >>>>> + { > >>>>> + test_default_ctor<Layout, std::extents<int, dyn>>(); > >>>>> + test_default_ctor<Layout, std::extents<int, 1, 2>>(); > >>>>> + test_default_ctor<Layout, std::extents<int, dyn, 2>>(); > >>>>> + test_default_ctor<Layout, std::extents<int, dyn, dyn>>(); > >>>>> + test_default_ctor<Layout, std::extents<int, dyn, 2, dyn>>(); > >>>>> + test_default_ctor<Layout, std::extents<int, dyn, dyn, dyn>>(); > >>>>> + return true; > >>>>> + } > >>>>> + > >>>>> + template<typename Layout> > >>>>> + constexpr void > >>>>> + test_all() > >>>>> + { > >>>>> + test_default_ctor_all<Layout>(); > >>>>> + static_assert(test_default_ctor_all<Layout>()); > >>>>> + } > >>>>> +} > >>>>> + > >>>>> +// ctor: mapping(const extents&) > >>>>> +namespace from_extents > >>>>> +{ > >>>>> + template<typename Layout, typename Extents, typename OExtents> > >>>>> + constexpr void > >>>>> + verify_nothrow_convertible(OExtents oexts) > >>>>> + { > >>>>> + using Mapping = typename Layout::mapping<Extents>; > >>>>> + ::verify_nothrow_convertible<Mapping>(oexts); > >>>>> + } > >>>>> + > >>>>> + template<typename Layout, typename Extents, typename OExtents> > >>>>> + constexpr void > >>>>> + verify_nothrow_constructible(OExtents oexts) > >>>>> + { > >>>>> + using Mapping = typename Layout::mapping<Extents>; > >>>>> + ::verify_nothrow_constructible<Mapping>(oexts); > >>>>> + } > >>>>> + > >>>>> + template<typename Layout, typename Extents, typename OExtents> > >>>>> + constexpr void > >>>>> + assert_not_constructible() > >>>>> + { > >>>>> + using Mapping = typename Layout::mapping<Extents>; > >>>>> + ::assert_not_constructible<Mapping, OExtents>(); > >>>>> + } > >>>>> + > >>>>> + template<typename Layout> > >>>>> + constexpr bool > >>>>> + test_ctor() > >>>>> + { > >>>>> + verify_nothrow_convertible<Layout, std::extents<int>>( > >>>>> + std::extents<int>{}); > >>>>> + > >>>>> + verify_nothrow_convertible<Layout, std::extents<int, 2>>( > >>>>> + std::extents<int, 2>{}); > >>>>> + > >>>>> + verify_nothrow_convertible<Layout, std::extents<int, dyn, 3>>( > >>>>> + std::extents<int, dyn, 3>{2}); > >>>>> + > >>>>> + verify_nothrow_constructible<Layout, std::extents<unsigned > >> int>>( > >>>>> + std::extents<int>{}); > >>>>> + > >>>>> + verify_nothrow_constructible<Layout, std::extents<int, dyn>>( > >>>>> + std::extents<int, 2>{}); > >>>>> + > >>>>> + verify_nothrow_constructible<Layout, std::extents<int, dyn, > 3>>( > >>>>> + std::extents<int, 2, 3>{}); > >>>>> + > >>>>> + assert_not_constructible<Layout, std::extents<int>, > >>>>> + std::extents<unsigned int>>(); > >>>>> + assert_not_constructible<Layout, std::extents<int, 2>, > >>>>> + std::extents<int, dyn>>(); > >>>>> + assert_not_constructible<Layout, std::extents<int, 2, 3>, > >>>>> + std::extents<int, dyn, 3>>(); > >>>>> + return true; > >>>>> + } > >>>>> + > >>>>> + template<typename Layout, typename Extents> > >>>>> + constexpr void > >>>>> + assert_deducible(Extents exts) > >>>>> + { > >>>>> + typename Layout::mapping m(exts); > >>>>> + static_assert(std::same_as<decltype(m), > >>>>> + typename Layout::mapping<Extents>>); > >>>>> + } > >>>>> + > >>>>> + template<typename Layout> > >>>>> + constexpr void > >>>>> + test_deducible() > >>>>> + { > >>>>> + assert_deducible<Layout>(std::extents<int>()); > >>>>> + assert_deducible<Layout>(std::extents<int, 1>()); > >>>>> + assert_deducible<Layout>(std::extents<int, 1, 2, dyn>(3)); > >>>>> + } > >>>>> + > >>>>> + template<typename Layout> > >>>>> + constexpr void > >>>>> + test_all() > >>>>> + { > >>>>> + test_ctor<Layout>(); > >>>>> + static_assert(test_ctor<Layout>()); > >>>>> + test_deducible<Layout>(); > >>>>> + } > >>>>> +} > >>>>> + > >>>>> +// ctor: mapping(mapping<OExtents>) > >>>>> +namespace from_same_layout > >>>>> +{ > >>>>> + template<typename Layout, typename Extents, typename OExtents> > >>>>> + constexpr void > >>>>> + verify_nothrow_convertible(OExtents exts) > >>>>> + { > >>>>> + using Mapping = typename Layout::mapping<Extents>; > >>>>> + using OMapping = typename Layout::mapping<OExtents>; > >>>>> + > >>>>> + ::verify_nothrow_convertible<Mapping>(OMapping(exts)); > >>>>> + } > >>>>> + > >>>>> + template<typename Layout, typename Extents, typename OExtents> > >>>>> + constexpr void > >>>>> + verify_nothrow_constructible(OExtents exts) > >>>>> + { > >>>>> + using Mapping = typename Layout::mapping<Extents>; > >>>>> + using OMapping = typename Layout::mapping<OExtents>; > >>>>> + > >>>>> + ::verify_nothrow_constructible<Mapping>(OMapping(exts)); > >>>>> + } > >>>>> + > >>>>> + template<typename Layout> > >>>>> + constexpr bool > >>>>> + test_ctor() > >>>>> + { > >>>>> + verify_nothrow_convertible<Layout, std::extents<unsigned > int>>( > >>>>> + std::extents<int>{}); > >>>>> + > >>>>> + verify_nothrow_constructible<Layout, std::extents<int>>( > >>>>> + std::extents<unsigned int>{}); > >>>>> + > >>>>> + assert_not_constructible< > >>>>> + typename Layout::mapping<std::extents<int>>, > >>>>> + typename Layout::mapping<std::extents<int, 1>>>(); > >>>>> + > >>>>> + assert_not_constructible< > >>>>> + typename Layout::mapping<std::extents<int, 1>>, > >>>>> + typename Layout::mapping<std::extents<int>>>(); > >>>>> + > >>>>> + verify_nothrow_constructible<Layout, std::extents<int, 1>>( > >>>>> + std::extents<int, dyn>{1}); > >>>>> + > >>>>> + verify_nothrow_convertible<Layout, std::extents<int, dyn>>( > >>>>> + std::extents<int, 1>{}); > >>>>> + > >>>>> + assert_not_constructible< > >>>>> + typename Layout::mapping<std::extents<int, 1, 2>>, > >>>>> + typename Layout::mapping<std::extents<int, 1>>>(); > >>>>> + > >>>>> + verify_nothrow_constructible<Layout, std::extents<int, 1, 2>>( > >>>>> + std::extents<int, dyn, 2>{1}); > >>>>> + > >>>>> + verify_nothrow_convertible<Layout, std::extents<int, dyn, 2>>( > >>>>> + std::extents<int, 1, 2>{}); > >>>>> + return true; > >>>>> + } > >>>>> + > >>>>> + template<typename Layout> > >>>>> + constexpr void > >>>>> + test_all() > >>>>> + { > >>>>> + test_ctor<Layout>(); > >>>>> + static_assert(test_ctor<Layout>()); > >>>>> + } > >>>>> +} > >>>>> + > >>>>> +template<typename Layout> > >>>>> + constexpr void > >>>>> + test_all() > >>>>> + { > >>>>> + default_ctor::test_all<Layout>(); > >>>>> + from_extents::test_all<Layout>(); > >>>>> + from_same_layout::test_all<Layout>(); > >>>>> + } > >>>>> + > >>>>> +int > >>>>> +main() > >>>>> +{ > >>>>> + test_all<std::layout_left>(); > >>>>> + return 0; > >>>>> +} > >>>>> diff --git > >>>> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc > >>>> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc > >>>>> new file mode 100644 > >>>>> index 00000000000..10843e2a2bb > >>>>> --- /dev/null > >>>>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc > >>>>> @@ -0,0 +1,67 @@ > >>>>> +// { dg-do run { target c++23 } } > >>>>> +#include <mdspan> > >>>>> + > >>>>> +#include <cstdint> > >>>>> +#include <testsuite_hooks.h> > >>>>> + > >>>>> +constexpr size_t dyn = std::dynamic_extent; > >>>>> + > >>>>> +template<typename Mapping> > >>>>> + constexpr void > >>>>> + invoke_stride(Mapping m) > >>>>> + { > >>>>> + // Only checking for UB, e.g. signed overflow. > >>>>> + for(size_t i = 0; i < Mapping::extents_type::rank(); ++i) > >>>>> + m.stride(i); > >>>>> + } > >>>>> + > >>>>> +template<typename Mapping> > >>>>> + constexpr void > >>>>> + verify_required_span_size(Mapping m) > >>>>> + { VERIFY(m.required_span_size() == 0); } > >>>>> + > >>>>> +template<typename Mapping> > >>>>> + constexpr void > >>>>> + verify_all(Mapping m) > >>>>> + { > >>>>> + verify_required_span_size(m); > >>>>> + invoke_stride(m); > >>>>> + } > >>>>> + > >>>>> +template<typename Layout, typename Int> > >>>>> +constexpr void > >>>>> +test_default_constructed() > >>>>> +{ > >>>>> + constexpr auto n = std::numeric_limits<Int>::max(); > >>>>> + verify_all(typename Layout::mapping<std::extents<Int, n, n, 0, n, > >>>> n>>{}); > >>>>> + verify_all(typename Layout::mapping<std::extents<Int, 0, n, n, > >> n>>{}); > >>>>> + verify_all(typename Layout::mapping<std::extents<Int, dyn, n, n, > >>>> n>>{}); > >>>>> + verify_all(typename Layout::mapping<std::extents<Int, n, n, n, > >> 0>>{}); > >>>>> + verify_all(typename Layout::mapping<std::extents<Int, n, n, n, > >>>> dyn>>{}); > >>>>> +} > >>>> > >>>> The problem here are cases where > >>>> > >>>> sizeof(Int) >= sizeof(size_t) > >>>> > >>>> and it comes in two variations: > >>>> > >>>> 1. 32-bit systems: failure due to narrowing conversion from `long > >> long > >>>> int` to size_t, which is the bug that the CI found. > >>>> > >>> > >>>> 2. any system: If `sizeof(Int) == sizeof(size_t) && > >> is_unsigned_v<Int>` > >>>> then `n == std::dynamic_extents` and the test is ineffective. > >>>> > >>>> Proposed solution: > >>>> > >>>> constexpr Int n1 = std::numeric_limits<Int>::max(); > >>>> constexpr size_t n2 = std::numeric_limits<size_t>::max() - 1; > >>>> constexpr size_t n = std::cmp_less(n1, n2) ? size_t(n1) : n2; > >>>> > >>> And I would replace: std::numeric_limits<size_t>::max() - 1 with > >>> dynamic_extent-1. > >>> This would make it more clear for the reader. You can also add a > >>> constructor. > >>>> > >>>> > >>>> > >> > ------------------------------------------------------------------------- > >>>> > >>>> The tests are also insufficient because they don't cover cases where > >>>> the overflow could happen while multiplying dynamic extents. > >>>> > >>> In which code path that multiplication will happen? I think it will > >> always > >>> trigger > >>> the preconditions in the constructor. > >>> > >> > >> Look at the extents, they're all either: > >> * fully static with a 0, > >> * or have one dyn which is 0. > >> > >> What we don't check is the case where we have 4 dynamic extents, > >> one is 0 and the rest are huge, e.g. > >> > >> layout_left::mapping(std::dextents<Int, 4>(n, n, n, 0)); > >> > >> This matters because we split __exts_prod as follow: > >> __exts_prod = __sta_prod * __dyn_prod > >> > >> (The notation isn't very precise, but is gets to the core.) > >> > >> We've carefully checked that there's no overflow in computing > >> __sta_prod but not __dyn_prod. > >> > > We checked that overflow does not happen for non-degenerate cases, > > where none of the extents is zero in `__is_representable_extents`. So > only > > cases > > that remain are one where at least one is zero. In this case we perform > > computation > > in the size_t, and cast index type to it. > > > > The claim isn't that there's a bug in <mdspan>. The issue is in the tests > alone. As for the missing tests, they wont uncover any bugs, for exactly > the reasons you mention. However, the test that we multiply the dynamic > extents safely (e.g. as size_t) is missing. > Ah, I see now. That's good point. > > > However, if you are referring to a situation when index_type is greater > > than size_type, > > and contains values that do not fit size_type, such mapping is unusable > > with mdspan, > > as accessors are required to accept size_t. So even if you produce > greater > > value from mapping, > > you cannot call accessor with it. This applies to accessors that are > always > > returning the same element. > > So, I would ignore these cases for now, and I will try to get > > clarification, if we are missing some requirements. > > My preference would be to Mandate that sizoef(index_type) <= > sizeof(size_t) > > in extents. > > I agree with ignoring the issue for now. Personally, I don't see how > size_t semantically should enter the picture until `default_accessor` > and `mdspan`. Hence, I'd also look at removing the size_t related > requirement in the Layout Mapping Requirements; and instead add a > precondition in mdspan that the layout must satifsy the Layout Mapping > Requirements *and* > m.required_span_size() <= std::numeric_limits<size_t>::max() > static_extents are of size_t type, which suggest that this type should be able to represent extent for extents of any index_type. Otherwise such extent could not be specified statically. > > > > >> > >> The way this could cause issues is via m.stride(i) (for layout_left > >> and layout_right) or maybe in `required_span_size()` (if one doesn't > >> prescan for 0). > > > > > >> Remember the cases in empty.cc don't trigger the precondition, because > >> the required span size is 0. > >> > >>>> > >>>>> + > >>>>> +template<typename Layout> > >>>>> +constexpr bool > >>>>> +test_all() > >>>>> +{ > >>>>> + test_default_constructed<Layout, signed char>(); > >>>>> + test_default_constructed<Layout, short int>(); > >>>>> + test_default_constructed<Layout, int>(); > >>>>> + test_default_constructed<Layout, long int>(); > >>>>> + test_default_constructed<Layout, long long int>(); > >>>>> + > >>>>> + test_default_constructed<Layout, unsigned char>(); > >>>>> + test_default_constructed<Layout, unsigned short int>(); > >>>>> + test_default_constructed<Layout, unsigned int>(); > >>>>> + test_default_constructed<Layout, unsigned long int>(); > >>>>> + test_default_constructed<Layout, unsigned long long int>(); > >>>>> + test_default_constructed<Layout, size_t>(); > >>>>> + return true; > >>>>> +} > >>>>> + > >>>>> +int > >>>>> +main() > >>>>> +{ > >>>>> + static_assert(test_all<std::layout_left>()); > >>>>> + return 0; > >>>>> +} > >>>>> diff --git > >>>> a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc > >>>> b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc > >>>>> new file mode 100644 > >>>>> index 00000000000..a5be1166617 > >>>>> --- /dev/null > >>>>> +++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc > >>>>> @@ -0,0 +1,437 @@ > >>>>> +// { dg-do run { target c++23 } } > >>>>> +#include <mdspan> > >>>>> + > >>>>> +#include <cstdint> > >>>>> +#include <testsuite_hooks.h> > >>>>> + > >>>>> +constexpr size_t dyn = std::dynamic_extent; > >>>>> + > >>>>> +template<typename Layout, typename Extents> > >>>>> + constexpr bool > >>>>> + test_mapping_properties() > >>>>> + { > >>>>> + using M = typename Layout::mapping<Extents>; > >>>>> + static_assert(std::__mdspan::__is_extents<typename > >>>> M::extents_type>); > >>>>> + static_assert(std::copyable<M>); > >>>>> + static_assert(std::is_nothrow_move_constructible_v<M>); > >>>>> + static_assert(std::is_nothrow_move_assignable_v<M>); > >>>>> + static_assert(std::is_nothrow_swappable_v<M>); > >>>>> + static_assert(std::is_same_v<typename M::extents_type, > Extents>); > >>>>> + static_assert(std::is_same_v<typename M::index_type, > >>>>> + typename > M::extents_type::index_type>); > >>>>> + static_assert(std::is_same_v<typename M::size_type, > >>>>> + typename M::extents_type::size_type>); > >>>>> + static_assert(std::is_same_v<typename M::rank_type, > >>>>> + typename M::extents_type::rank_type>); > >>>>> + static_assert(std::is_same_v<typename M::layout_type, Layout>); > >>>>> + > >>>>> + static_assert(std::is_trivially_copyable_v<M>); > >>>>> + static_assert(std::regular<M>); > >>>>> + > >>>>> + static_assert(M::is_always_unique() && M::is_unique()); > >>>>> + static_assert(M::is_always_strided() && M::is_strided()); > >>>>> + return true; > >>>>> + } > >>>>> + > >>>>> +template<typename Layout> > >>>>> + constexpr bool > >>>>> + test_mapping_properties_all() > >>>>> + { > >>>>> + test_mapping_properties<Layout, std::extents<int>>(); > >>>>> + test_mapping_properties<Layout, std::extents<int, 1>>(); > >>>>> + test_mapping_properties<Layout, std::extents<int, dyn>>(); > >>>>> + test_mapping_properties<Layout, std::extents<int, dyn, dyn>>(); > >>>>> + return true; > >>>>> + } > >>>>> + > >>>>> +// Check operator()(Indices...) > >>>>> +template<typename Mapping, size_t N> > >>>>> + constexpr typename Mapping::index_type > >>>>> + linear_index(const Mapping& mapping, > >>>>> + const std::array<typename Mapping::index_type, N>& > >> indices) > >>>>> + { > >>>>> + typename Mapping::index_type ret = 0; > >>>>> + for(size_t r = 0; r < indices.size(); ++r) > >>>>> + ret += indices[r] * mapping.stride(r); > >>>>> + return ret; > >>>>> + } > >>>>> + > >>>>> +template<typename Mapping, typename... Indices> > >>>>> + constexpr void > >>>>> + test_linear_index(const Mapping& m, Indices... i) > >>>>> + { > >>>>> + using index_type = typename Mapping::index_type; > >>>>> + index_type expected = linear_index(m, > >> std::array{index_type(i)...}); > >>>>> + VERIFY(m(i...) == expected); > >>>>> + VERIFY(m(uint8_t(i)...) == expected); > >>>>> + } > >>>>> + > >>>>> +template<typename Layout> > >>>>> + constexpr void > >>>>> + test_linear_index_0d() > >>>>> + { > >>>>> + constexpr typename Layout::mapping<std::extents<int>> m; > >>>>> + VERIFY(m() == 0); > >>>>> + } > >>>>> + > >>>>> +template<typename Layout> > >>>>> + constexpr void > >>>>> + test_linear_index_1d() > >>>>> + { > >>>>> + typename Layout::mapping<std::extents<int, 5>> m; > >>>>> + test_linear_index(m, 0); > >>>>> + test_linear_index(m, 1); > >>>>> + test_linear_index(m, 4); > >>>>> + } > >>>>> + > >>>>> +template<typename Layout> > >>>>> + constexpr void > >>>>> + test_linear_index_2d() > >>>>> + { > >>>>> + typename Layout::mapping<std::extents<int, 3, 256>> m; > >>>>> + test_linear_index(m, 0, 0); > >>>>> + test_linear_index(m, 1, 0); > >>>>> + test_linear_index(m, 0, 1); > >>>>> + test_linear_index(m, 1, 1); > >>>>> + test_linear_index(m, 2, 4); > >>>>> + } > >>>>> + > >>>>> +template<typename Layout> > >>>>> + struct MappingFactory > >>>>> + { > >>>>> + template<typename Extents> > >>>>> + static constexpr typename Layout::mapping<Extents> > >>>>> + create(Extents exts) > >>>>> + { return exts; } > >>>>> + }; > >>>>> + > >>>>> +template<typename Layout> > >>>>> + constexpr void > >>>>> + test_linear_index_3d() > >>>>> + { > >>>>> + auto m = MappingFactory<Layout>::create(std::extents(3, 5, 7)); > >>>>> + test_linear_index(m, 0, 0, 0); > >>>>> + test_linear_index(m, 1, 0, 0); > >>>>> + test_linear_index(m, 0, 1, 0); > >>>>> + test_linear_index(m, 0, 0, 1); > >>>>> + test_linear_index(m, 1, 1, 0); > >>>>> + test_linear_index(m, 2, 4, 6); > >>>>> + } > >>>>> + > >>>>> +struct IntLikeA > >>>>> +{ > >>>>> + operator int() > >>>>> + { return 0; } > >>>>> +}; > >>>>> + > >>>>> +struct IntLikeB > >>>>> +{ > >>>>> + operator int() noexcept > >>>>> + { return 0; } > >>>>> +}; > >>>>> + > >>>>> +struct NotIntLike > >>>>> +{ }; > >>>>> + > >>>>> +template<typename Layout> > >>>>> + constexpr void > >>>>> + test_has_linear_index_0d() > >>>>> + { > >>>>> + using Mapping = typename Layout::mapping<std::extents<int>>; > >>>>> + static_assert(std::invocable<Mapping>); > >>>>> + static_assert(!std::invocable<Mapping, int>); > >>>>> + static_assert(!std::invocable<Mapping, IntLikeA>); > >>>>> + static_assert(!std::invocable<Mapping, IntLikeB>); > >>>>> + static_assert(!std::invocable<Mapping, NotIntLike>); > >>>>> + } > >>>>> + > >>>>> +template<typename Layout> > >>>>> + constexpr void > >>>>> + test_has_linear_index_1d() > >>>>> + { > >>>>> + using Mapping = typename Layout::mapping<std::extents<int, 3>>; > >>>>> + static_assert(std::invocable<Mapping, int>); > >>>>> + static_assert(!std::invocable<Mapping>); > >>>>> + static_assert(!std::invocable<Mapping, IntLikeA>); > >>>>> + static_assert(std::invocable<Mapping, IntLikeB>); > >>>>> + static_assert(!std::invocable<Mapping, NotIntLike>); > >>>>> + static_assert(std::invocable<Mapping, double>); > >>>>> + } > >>>>> + > >>>>> +template<typename Layout> > >>>>> + constexpr void > >>>>> + test_has_linear_index_2d() > >>>>> + { > >>>>> + using Mapping = typename Layout::mapping<std::extents<int, 3, > 5>>; > >>>>> + static_assert(std::invocable<Mapping, int, int>); > >>>>> + static_assert(!std::invocable<Mapping, int>); > >>>>> + static_assert(!std::invocable<Mapping, IntLikeA, int>); > >>>>> + static_assert(std::invocable<Mapping, IntLikeB, int>); > >>>>> + static_assert(!std::invocable<Mapping, NotIntLike, int>); > >>>>> + static_assert(std::invocable<Mapping, double, double>); > >>>>> + } > >>>>> + > >>>>> +template<typename Layout> > >>>>> + constexpr bool > >>>>> + test_linear_index_all() > >>>>> + { > >>>>> + test_linear_index_0d<Layout>(); > >>>>> + test_linear_index_1d<Layout>(); > >>>>> + test_linear_index_2d<Layout>(); > >>>>> + test_linear_index_3d<Layout>(); > >>>>> + test_has_linear_index_0d<Layout>(); > >>>>> + test_has_linear_index_1d<Layout>(); > >>>>> + test_has_linear_index_2d<Layout>(); > >>>>> + return true; > >>>>> + } > >>>>> + > >>>>> +template<typename Mapping> > >>>>> + constexpr typename Mapping::index_type > >>>>> + linear_index_end(Mapping m) > >>>>> + { > >>>>> + using index_type = typename Mapping::index_type; > >>>>> + constexpr size_t rank = Mapping::extents_type::rank(); > >>>>> + > >>>>> + auto impl = [m]<index_type... Counts>( > >>>>> + std::integer_sequence<index_type, Counts...>) -> index_type > >>>>> + { > >>>>> + auto exts = m.extents(); > >>>>> + if(((exts.extent(Counts) == 0) || ...)) > >>>>> + return 0; > >>>>> + return m((exts.extent(Counts) - 1)...) + 1; > >>>>> + }; > >>>>> + > >>>>> + return impl(std::make_integer_sequence<index_type, rank>()); > >>>>> + } > >>>>> + > >>>>> +// Check required_span_size > >>>>> +template<typename Mapping> > >>>>> + constexpr void > >>>>> + test_required_span_size(Mapping m) > >>>>> + { VERIFY(m.required_span_size() == linear_index_end(m)); } > >>>>> + > >>>>> +template<typename Layout> > >>>>> + constexpr void > >>>>> + test_required_span_size_0d() > >>>>> + { > >>>>> + typename Layout::mapping<std::extents<int>> m; > >>>>> + test_required_span_size(m); > >>>>> + } > >>>>> + > >>>>> +template<typename Layout> > >>>>> + constexpr void > >>>>> + test_required_span_size_1d() > >>>>> + { > >>>>> + auto m = MappingFactory<Layout>::create(std::extents(3)); > >>>>> + test_required_span_size(m); > >>>>> + } > >>>>> + > >>>>> +template<typename Layout> > >>>>> + constexpr void > >>>>> + test_required_span_size_2d() > >>>>> + { > >>>>> + auto m = MappingFactory<Layout>::create(std::extents(3, 5)); > >>>>> + test_required_span_size(m); > >>>>> + } > >>>>> + > >>>>> +template<typename Layout> > >>>>> + constexpr void > >>>>> + test_required_span_size_3d() > >>>>> + { > >>>>> + auto m = MappingFactory<Layout>::create(std::extents(3, 5, 7)); > >>>>> + test_required_span_size(m); > >>>>> + } > >>>>> + > >>>>> +template<typename Layout> > >>>>> + constexpr void > >>>>> + test_required_span_size_zero_1d() > >>>>> + { > >>>>> + auto m = MappingFactory<Layout>::create(std::extents(3, 0)); > >>>>> + test_required_span_size(m); > >>>>> + } > >>>>> + > >>>>> +template<typename Layout> > >>>>> + constexpr void > >>>>> + test_required_span_size_zero_3d() > >>>>> + { > >>>>> + auto m = MappingFactory<Layout>::create(std::extents(3, 0, 7)); > >>>>> + test_required_span_size(m); > >>>>> + } > >>>>> + > >>>>> +template<typename Layout> > >>>>> + constexpr bool > >>>>> + test_required_span_size_all() > >>>>> + { > >>>>> + test_required_span_size_0d<Layout>(); > >>>>> + test_required_span_size_1d<Layout>(); > >>>>> + test_required_span_size_2d<Layout>(); > >>>>> + test_required_span_size_3d<Layout>(); > >>>>> + test_required_span_size_zero_1d<Layout>(); > >>>>> + test_required_span_size_zero_3d<Layout>(); > >>>>> + return true; > >>>>> + } > >>>>> + > >>>>> +// Check stride > >>>>> +template<typename Layout> > >>>>> + constexpr void > >>>>> + test_stride_1d() > >>>>> + { > >>>>> + std::layout_left::mapping<std::extents<int, 3>> m; > >>>>> + VERIFY(m.stride(0) == 1); > >>>>> + } > >>>>> + > >>>>> +template<typename Layout> > >>>>> + constexpr void > >>>>> + test_stride_2d(); > >>>>> + > >>>>> +template<> > >>>>> + constexpr void > >>>>> + test_stride_2d<std::layout_left>() > >>>>> + { > >>>>> + std::layout_left::mapping<std::extents<int, 3, 5>> m; > >>>>> + VERIFY(m.stride(0) == 1); > >>>>> + VERIFY(m.stride(1) == 3); > >>>>> + } > >>>>> + > >>>>> +template<typename Layout> > >>>>> + constexpr void > >>>>> + test_stride_3d(); > >>>>> + > >>>>> +template<> > >>>>> + constexpr void > >>>>> + test_stride_3d<std::layout_left>() > >>>>> + { > >>>>> + std::layout_left::mapping m(std::dextents<int, 3>(3, 5, 7)); > >>>>> + VERIFY(m.stride(0) == 1); > >>>>> + VERIFY(m.stride(1) == 3); > >>>>> + VERIFY(m.stride(2) == 3*5); > >>>>> + } > >>>>> + > >>>>> +template<typename Layout> > >>>>> + constexpr bool > >>>>> + test_stride_all() > >>>>> + { > >>>>> + test_stride_1d<Layout>(); > >>>>> + test_stride_2d<Layout>(); > >>>>> + test_stride_3d<Layout>(); > >>>>> + return true; > >>>>> + } > >>>>> + > >>>>> +template<typename Mapping> > >>>>> + concept has_stride = requires (Mapping m) > >>>>> + { > >>>>> + { m.stride(0) } -> std::same_as<typename Mapping::index_type>; > >>>>> + }; > >>>>> + > >>>>> +template<typename Layout> > >>>>> + constexpr void > >>>>> + test_has_stride_0d() > >>>>> + { > >>>>> + using Mapping = typename Layout::mapping<std::extents<int>>; > >>>>> + constexpr bool expected = false; > >>>>> + static_assert(has_stride<Mapping> == expected); > >>>>> + } > >>>>> + > >>>>> +template<typename Layout> > >>>>> + constexpr void > >>>>> + test_has_stride_1d() > >>>>> + { static_assert(has_stride<typename > >> Layout::mapping<std::extents<int, > >>>> 1>>>); } > >>>>> + > >>>>> +template<typename Layout> > >>>>> + constexpr void > >>>>> + test_has_stride_2d() > >>>>> + { > >>>>> + using Extents = std::extents<int, 1, 2>; > >>>>> + static_assert(has_stride<typename Layout::mapping<Extents>>); > >>>>> + } > >>>>> + > >>>>> +// Check operator== > >>>>> +template<typename Layout> > >>>>> + constexpr void > >>>>> + test_eq() > >>>>> + { > >>>>> + typename Layout::mapping<std::extents<int, 1, 2>> m1; > >>>>> + typename Layout::mapping<std::extents<int, 2, 2>> m2; > >>>>> + typename Layout::mapping<std::dextents<int, 2>> m3(m1); > >>>>> + > >>>>> + VERIFY(m1 == m1); > >>>>> + VERIFY(m1 != m2); > >>>>> + VERIFY(m1 == m3); > >>>>> + VERIFY(m2 != m3); > >>>>> + } > >>>>> + > >>>>> +template<typename Layout> > >>>>> + constexpr void > >>>>> + test_eq_zero() > >>>>> + { > >>>>> + typename Layout::mapping<std::extents<int, 0, 2>> m1; > >>>>> + typename Layout::mapping<std::extents<int, 0, 2>> m2; > >>>>> + typename Layout::mapping<std::extents<int, 2, 0>> m3; > >>>>> + > >>>>> + VERIFY(m1 == m2); > >>>>> + VERIFY(m1 != m3); > >>>>> + } > >>>>> + > >>>>> +template<typename M1, typename M2> > >>>>> + concept has_op_eq = requires (M1 m1, M2 m2) > >>>>> + { > >>>>> + { m1 == m2 } -> std::same_as<bool>; > >>>>> + { m2 == m1 } -> std::same_as<bool>; > >>>>> + { m1 != m2 } -> std::same_as<bool>; > >>>>> + { m2 != m1 } -> std::same_as<bool>; > >>>>> + }; > >>>>> + > >>>>> +template<typename Layout> > >>>>> + constexpr bool > >>>>> + test_has_op_eq() > >>>>> + { > >>>>> + static_assert(!has_op_eq< > >>>>> + typename Layout::mapping<std::extents<int, 1, 2>>, > >>>>> + typename Layout::mapping<std::extents<int, 1>>>); > >>>>> + > >>>>> + static_assert(has_op_eq< > >>>>> + typename Layout::mapping<std::extents<int, 1>>, > >>>>> + typename Layout::mapping<std::extents<int, 1>>>); > >>>>> + > >>>>> + static_assert(has_op_eq< > >>>>> + typename Layout::mapping<std::extents<int, 1>>, > >>>>> + typename Layout::mapping<std::extents<int, 2>>>); > >>>>> + return true; > >>>>> + } > >>>>> + > >>>>> +template<typename Layout> > >>>>> + constexpr bool > >>>>> + test_mapping_all() > >>>>> + { > >>>>> + test_linear_index_all<Layout>(); > >>>>> + test_required_span_size_all<Layout>(); > >>>>> + test_stride_all<Layout>(); > >>>>> + > >>>>> + test_eq<Layout>(); > >>>>> + test_eq_zero<Layout>(); > >>>>> + return true; > >>>>> + } > >>>>> + > >>>>> +template<typename Layout> > >>>>> + constexpr void > >>>>> + test_all() > >>>>> + { > >>>>> + > static_assert(std::is_trivially_default_constructible_v<Layout>); > >>>>> + static_assert(std::is_trivially_copyable_v<Layout>); > >>>>> + static_assert(test_mapping_properties_all<Layout>()); > >>>>> + > >>>>> + test_mapping_all<Layout>(); > >>>>> + static_assert(test_mapping_all<Layout>()); > >>>>> + > >>>>> + test_has_stride_0d<Layout>(); > >>>>> + test_has_stride_1d<Layout>(); > >>>>> + test_has_stride_2d<Layout>(); > >>>>> + test_has_op_eq<Layout>(); > >>>>> + } > >>>>> + > >>>>> +int > >>>>> +main() > >>>>> +{ > >>>>> + test_all<std::layout_left>(); > >>>>> + return 0; > >>>>> +} > >>>> > >>>> > >>> > >> > >> > > > >