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.

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 --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;
+}







Reply via email to