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/mapping.cc: New test.

Signed-off-by: Luc Grosheintz <luc.groshei...@gmail.com>
---
 .../mdspan/layouts/class_mandate_neg.cc       |  22 +
 .../23_containers/mdspan/layouts/ctors.cc     | 238 ++++++++++
 .../23_containers/mdspan/layouts/mapping.cc   | 438 ++++++++++++++++++
 3 files changed, 698 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/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..b276fbd333e
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc
@@ -0,0 +1,22 @@
+// { 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" }
+  };
+
+A<std::layout_left> a_left;                     // { dg-error "required from" }
+
+// { 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..c96f314818a
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
@@ -0,0 +1,238 @@
+// { dg-do run { target c++23 } }
+#include <mdspan>
+
+#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(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()
+  {
+    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/mapping.cc 
b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc
new file mode 100644
index 00000000000..60630dc37ca
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc
@@ -0,0 +1,438 @@
+// { 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...>)
+      {
+       auto exts = m.extents();
+       if(((exts.extent(Counts) == 0) || ...))
+         return index_type(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;
+}
-- 
2.49.0

Reply via email to