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,toallow reuse as more layouts are added. libstdc++-v3/ChangeLog: * testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc:Newtest.* 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'llexplainbelow.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 100644libstdc++-v3/testsuite/23_containers/mdspan/layouts/class_mandate_neg.cccreate mode 100644libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cccreate mode 100644libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cccreate mode 100644libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.ccdiff --gita/libstdc++-v3/testsuite/23_containers/mdspan/layouts/class_mandate_neg.ccb/libstdc++-v3/testsuite/23_containers/mdspan/layouts/class_mandate_neg.ccnew 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 "requiredfrom" }+ }; + +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 --gita/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.ccnew 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<unsignedint>>(+ 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 --gita/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.cc b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/empty.ccnew 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 `longlongint` 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 willalwaystrigger 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.
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()
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 --gita/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.ccnew 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<typenameM::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<typenameLayout::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; +}