Implements the tests for layout_stride and for the features of the other
two layouts that depend on layout_stride.

libstdc++/ChangeLog:

        * testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc: Add
        tests for layout_stride.
        * testsuite/23_containers/mdspan/layouts/ctors.cc: Add test for
        layout_stride and the interaction with other layouts.
        * testsuite/23_containers/mdspan/layouts/mapping.cc: Ditto.
        * testsuite/23_containers/mdspan/layouts/stride.cc: New test.

Signed-off-by: Luc Grosheintz <luc.groshei...@gmail.com>
---
 .../mdspan/layouts/class_mandate_neg.cc       |   1 +
 .../23_containers/mdspan/layouts/ctors.cc     |  79 ++++
 .../23_containers/mdspan/layouts/mapping.cc   |  42 +-
 .../23_containers/mdspan/layouts/stride.cc    | 359 ++++++++++++++++++
 4 files changed, 480 insertions(+), 1 deletion(-)
 create mode 100644 
libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride.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
index fdebda8bd06..f9fa6212d4d 100644
--- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/class_mandate_neg.cc
@@ -18,5 +18,6 @@ template<typename Layout>
 
 A<std::layout_left> a_left;     // { dg-error "required from" }
 A<std::layout_right> a_right;   // { dg-error "required from" }
+A<std::layout_stride> a_stride; // { dg-error "required from" }
 
 // { dg-prune-output "not 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
index 5c54b440083..fc479ae596e 100644
--- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/ctors.cc
@@ -202,12 +202,91 @@ namespace from_left_or_right
     }
 }
 
+// ctor: mapping(layout_stride::mapping<OExtents>)
+namespace from_stride
+{
+  template<typename Layout>
+    constexpr void
+    test_constructible()
+    {
+      static_assert(!std::is_constructible_v<
+         typename Layout::mapping<std::extents<int, 1>>,
+         std::layout_stride::mapping<std::extents<int>>>);
+
+      static_assert(!std::is_constructible_v<
+         typename Layout::mapping<std::extents<int, 2>>,
+         std::layout_stride::mapping<std::extents<int, 1>>>);
+
+      static_assert(std::is_constructible_v<
+         typename Layout::mapping<std::extents<int, 1, 2>>,
+         std::layout_stride::mapping<std::extents<int, 1, 2>>>);
+    }
+
+  template<typename Layout>
+    constexpr void
+    test_convertible()
+    {
+      static_assert(!std::is_convertible_v<
+         std::layout_stride::mapping<std::extents<int, 1>>,
+         typename Layout::mapping<std::extents<int, 1>>>);
+
+      static_assert(std::is_convertible_v<
+         std::layout_stride::mapping<std::extents<int>>,
+         typename Layout::mapping<std::extents<int>>>);
+    }
+
+  template<typename Mapping>
+    constexpr std::array<typename Mapping::index_type,
+                        Mapping::extents_type::rank()>
+    strides(Mapping m)
+    {
+      std::array<typename Mapping::index_type, Mapping::extents_type::rank()> 
s;
+      if constexpr (Mapping::extents_type::rank() > 0)
+       for(size_t i = 0; i < Mapping::extents_type::rank(); ++i)
+         s[i] = m.stride(i);
+      return s;
+    }
+
+  template<typename Layout, typename Extents>
+    constexpr bool
+    test_ctor_from_stride(Extents exts)
+    {
+      typename Layout::mapping<Extents> m(exts);
+      std::layout_stride::mapping<Extents> m1(exts, strides(m));
+      typename Layout::mapping<Extents> m2(m1);
+      VERIFY(m1.extents() == m2.extents());
+      return true;
+    }
+
+  template<typename Layout>
+    constexpr bool
+    test_ctor_from_stride_all()
+    {
+      test_ctor_from_stride<Layout>(std::extents<int>{});
+      test_ctor_from_stride<Layout>(std::extents<int, 1>{});
+      test_ctor_from_stride<Layout>(std::extents<int, dyn, dyn, dyn>{3, 5, 7});
+      return true;
+    }
+
+  template<typename Layout>
+    constexpr void
+    test_all()
+    {
+      test_constructible<Layout>();
+      test_convertible<Layout>();
+
+      test_ctor_from_stride_all<Layout>();
+      static_assert(test_ctor_from_stride_all<Layout>());
+    }
+}
+
 template<typename Layout>
 constexpr void
 test_all()
 {
   from_extents::test_all<Layout>();
   from_same_layout::test_all<Layout>();
+  from_stride::test_all<Layout>();
 }
 
 int
diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc 
b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc
index f59dd75d01f..affa1be56d0 100644
--- a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/mapping.cc
@@ -11,6 +11,7 @@ template<typename Layout, typename Extent>
   {
     using M = typename Layout::mapping<Extent>;
     static_assert(std::__mdspan::__is_extents<typename M::extents_type>);
+    static_assert(std::__mdspan::__mapping_like<M>);
     static_assert(std::copyable<M>);
     static_assert(std::is_nothrow_move_constructible_v<M>);
     static_assert(std::is_nothrow_move_assignable_v<M>);
@@ -28,6 +29,8 @@ template<typename Layout, typename Extent>
 
     static_assert(M::is_always_unique() && M::is_unique());
     static_assert(M::is_always_strided() && M::is_strided());
+    if constexpr (!std::is_same_v<Layout, std::layout_stride>)
+      static_assert(M::is_always_exhaustive() && M::is_exhaustive());
     return true;
   }
 
@@ -94,6 +97,20 @@ template<typename Layout>
     { return Mapping(exts); }
   };
 
+template<>
+  struct Mapping3dFactory<std::layout_stride>
+  {
+    using Mapping = std::layout_stride::mapping<std::dextents<size_t, 3>>;
+
+    static constexpr Mapping
+    create(std::dextents<size_t, 3> exts)
+    {
+      size_t n = exts.extent(0);
+      size_t m = exts.extent(1);
+      return Mapping(exts, std::array<size_t, 3>{2*m, 1, 11*m*n});
+    }
+  };
+
 template<typename Layout>
   constexpr void
   test_linear_index_3d()
@@ -248,6 +265,16 @@ template<>
     VERIFY(m.stride(1) == 1);
   }
 
+template<>
+  constexpr void
+  test_stride_2d<std::layout_stride>()
+  {
+    std::array<int, 2> strides{13, 2};
+    std::layout_stride::mapping m(std::extents<int, 3, 5>{}, strides);
+    VERIFY(m.stride(0) == strides[0]);
+    VERIFY(m.stride(1) == strides[1]);
+  }
+
 template<typename Layout>
   constexpr void
   test_stride_3d();
@@ -272,6 +299,18 @@ template<>
     VERIFY(m.stride(2) == 1);
   }
 
+template<>
+  constexpr void
+  test_stride_3d<std::layout_stride>()
+  {
+    std::dextents<int, 3> exts(3, 5, 7);
+    std::array<int, 3> strides{11, 2, 41};
+    std::layout_stride::mapping<std::dextents<int, 3>> m(exts, strides);
+    VERIFY(m.stride(0) == strides[0]);
+    VERIFY(m.stride(1) == strides[1]);
+    VERIFY(m.stride(2) == strides[2]);
+  }
+
 template<typename Layout>
   constexpr bool
   test_stride_all()
@@ -292,7 +331,7 @@ template<typename Layout>
   test_has_stride_0d()
   {
     using Mapping = typename Layout::mapping<std::extents<int>>;
-    constexpr bool expected = false;
+    constexpr bool expected = std::is_same_v<Layout, std::layout_stride>;
     static_assert(has_stride<Mapping> == expected);
   }
 
@@ -387,5 +426,6 @@ main()
 {
   test_all<std::layout_left>();
   test_all<std::layout_right>();
+  test_all<std::layout_stride>();
   return 0;
 }
diff --git a/libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride.cc 
b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride.cc
new file mode 100644
index 00000000000..158c9bea954
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/mdspan/layouts/stride.cc
@@ -0,0 +1,359 @@
+// { dg-do run { target c++23 } }
+#include <mdspan>
+
+#include <testsuite_hooks.h>
+
+constexpr size_t dyn = std::dynamic_extent;
+
+template<typename MappingStride>
+  constexpr void
+  test_ctor_default_stride()
+  {
+    using Extents = typename MappingStride::extents_type;
+    MappingStride actual;
+    typename std::layout_right::mapping<Extents> expected;
+
+    constexpr auto rank = MappingStride::extents_type::rank();
+    if constexpr (rank > 0)
+      for(size_t i = 0; i < rank; ++i)
+       VERIFY(actual.stride(i) == expected.stride(i));
+  }
+
+constexpr bool
+test_ctor_default_stride_all()
+{
+  using M1 = std::layout_stride::mapping<std::extents<int, 3, 5, 7>>;
+  test_ctor_default_stride<M1>();
+
+  using M2 = std::layout_stride::mapping<std::extents<int>>;
+  test_ctor_default_stride<M2>();
+  return true;
+}
+
+struct IntLikeA
+{
+  operator int()
+  { return 0; }
+};
+
+struct IntLikeB
+{
+  operator int() noexcept
+  { return 0; }
+};
+
+struct NotIntLike
+{ };
+
+template<typename E, typename E_arg, typename T, size_t N, bool Expected>
+constexpr void
+test_stride_constructible()
+{
+  static_assert(std::is_constructible_v<
+      std::layout_stride::mapping<E>, E_arg, std::span<T, N>> == Expected);
+  static_assert(std::is_constructible_v<
+      std::layout_stride::mapping<E>, E_arg, std::array<T, N>> == Expected);
+}
+
+constexpr void
+test_stride_constructible_all()
+{
+  using E0 = std::extents<int>;
+  using E1 = std::extents<int, 2>;
+  using E2 = std::extents<int, dyn>;
+
+  test_stride_constructible<E0, E0, int, 0, true>();
+  test_stride_constructible<E0, E0, IntLikeA, 0, false>();
+  test_stride_constructible<E0, E0, IntLikeB, 0, true>();
+  test_stride_constructible<E0, E0, NotIntLike, 0, false>();
+  test_stride_constructible<E1, E1, int, 1, true>();
+  test_stride_constructible<E2, E1, int, 1, true>();
+  test_stride_constructible<E1, E1, int, 2, false>();
+  test_stride_constructible<E1, E0, int, 1, false>();
+}
+
+template<typename Extents, typename Shape>
+  constexpr void
+  test_ctor_shape_strides(Extents exts, Shape strides)
+  {
+    using M = std::layout_stride::mapping<Extents>;
+    M m(exts, strides);
+
+    if constexpr (Extents::rank() > 0)
+      for(size_t i = 0; i < exts.rank(); ++i)
+       {
+         VERIFY(m.stride(i) == strides[i]);
+         VERIFY(m.extents().extent(i) == exts.extent(i));
+       }
+  }
+
+constexpr bool
+test_ctor_shape_stride_all()
+{
+  test_ctor_shape_strides(std::extents<int>{}, std::array<int, 0>{});
+  test_ctor_shape_strides(std::extents<int, 2, 4, 6>{},
+                         std::array<int, 3>{20, 5, 45});
+  return true;
+}
+
+template<typename Extents, std::array<bool, 2> Strided,
+        std::array<bool, 2> Unique, std::array<bool, 2> Exhautive,
+        typename Extents::index_type Offset = 0>
+  struct MappingLike
+  {
+    using extents_type = Extents;
+    using index_type = typename Extents::index_type;
+
+    static constexpr bool
+    is_always_strided() requires (Strided[0])
+    { return Strided[1]; }
+
+    static constexpr bool
+    is_always_unique() requires (Unique[0])
+    { return Unique[1]; }
+
+    static constexpr bool
+    is_always_exhaustive() requires (Exhautive[0])
+    { return Exhautive[1]; }
+
+    constexpr Extents
+    extents() const { return _extents; }
+
+    constexpr index_type
+    stride(size_t i) const { return _strides[i]; }
+
+    template<typename... Indices>
+      constexpr index_type
+      operator()(Indices... indices) const
+      {
+       std::array<index_type, Extents::rank()> ind_arr{indices...};
+       index_type ret = Offset;
+       for(size_t i = 0; i < Extents::rank(); ++i)
+         ret += ind_arr[i]*_strides[i];
+       return ret;
+      }
+
+    Extents _extents;
+    std::array<index_type, Extents::rank()> _strides;
+  };
+
+
+struct ExtentLike
+{
+  using index_type = int;
+
+  static constexpr size_t
+  rank() { return 1; }
+};
+
+
+constexpr void
+test_mapping_like_constructible()
+{
+  using E1 = std::extents<int, 2>;
+  using E2 = std::extents<int, 3>;
+  using E3 = ExtentLike;
+  using M = std::layout_stride::mapping<E1>;
+
+  constexpr auto TT = std::array{true, true};
+  constexpr auto FT = std::array{false, true};
+  constexpr auto TF = std::array{true, false};
+
+  static_assert(std::is_constructible_v<M, MappingLike<E1, TT, TT, TT>>);
+  static_assert(!std::is_constructible_v<M, MappingLike<E3, TT, TT, TT>>);
+  static_assert(!std::is_constructible_v<M, MappingLike<E1, FT, TT, TT>>);
+  static_assert(!std::is_constructible_v<M, MappingLike<E1, TF, TT, TT>>);
+  static_assert(!std::is_constructible_v<M, MappingLike<E1, TT, FT, TT>>);
+  static_assert(!std::is_constructible_v<M, MappingLike<E1, TT, TF, TT>>);
+  static_assert(!std::is_constructible_v<M, MappingLike<E1, TT, TT, FT>>);
+  static_assert(!std::is_constructible_v<M, MappingLike<E2, TT, TT, TT>>);
+}
+
+constexpr void
+test_mapping_like_convertible()
+{
+  using E1 = std::extents<int, 2>;
+  using E2 = std::extents<short, 2>;
+  using E3 = std::extents<int, dyn>;
+  using M = std::layout_stride::mapping<E1>;
+  constexpr auto TT = std::array{true, true};
+
+  static_assert(!std::is_convertible_v<MappingLike<E1, TT, TT, TT>, M>);
+  static_assert(std::is_convertible_v<std::layout_stride::mapping<E2>, M>);
+  static_assert(!std::is_convertible_v<std::layout_stride::mapping<E3>, M>);
+  static_assert(std::is_convertible_v<std::layout_left::mapping<E2>, M>);
+  static_assert(!std::is_convertible_v<std::layout_left::mapping<E3>, M>);
+  static_assert(std::is_convertible_v<std::layout_right::mapping<E2>, M>);
+  static_assert(!std::is_convertible_v<std::layout_right::mapping<E3>, M>);
+}
+
+template<class OtherMapping>
+constexpr void
+test_ctor_stride_like(OtherMapping other)
+{
+  using E = typename OtherMapping::extents_type;
+
+  auto strided = std::layout_stride::mapping<E>(other);
+  VERIFY(strided == other);
+}
+
+constexpr void
+test_ctor_stride_like_all()
+{
+  using E = std::extents<int, 3, 5, 7>;
+  auto strides = std::array<int, 3>{5, 1, 15};
+
+  test_ctor_stride_like(std::layout_left::mapping<E>{});
+  test_ctor_stride_like(std::layout_right::mapping<E>{});
+  test_ctor_stride_like(std::layout_stride::mapping<E>(E{}, strides));
+}
+
+constexpr bool
+test_ctor_strides_all()
+{
+  test_ctor_default_stride_all();
+  test_ctor_shape_stride_all();
+  test_ctor_stride_like_all();
+  return true;
+}
+
+// Check is_exhaustive.
+template<typename Extents, typename Strides>
+  constexpr void
+  test_is_exhaustive(Extents extents, Strides strides, bool expected)
+  {
+    std::layout_stride::mapping<Extents> m(extents, strides);
+
+    VERIFY(m.is_exhaustive() == expected);
+  }
+
+constexpr void
+test_is_exhaustive_zero()
+{
+  std::extents<int, 3, 0, 7> extents;
+
+  // This is exhaustive, and the current implementation recognizes it as such,
+  // but the standard requires returning `false`.
+  test_is_exhaustive(extents, std::array{1, 1, 1}, true);
+
+  // Unabiguously, exhaustive.
+  test_is_exhaustive(extents, std::array{0, 1, 0}, true);
+  test_is_exhaustive(extents, std::array{1, 3, 0}, true);
+  test_is_exhaustive(extents, std::array{1, 21, 3}, true);
+  test_is_exhaustive(extents, std::array{7, 21, 1}, true);
+}
+
+constexpr void
+test_is_exhaustive_3d()
+{
+  std::extents<int, 3, dyn, 7> extents(5);
+
+  test_is_exhaustive(extents, std::array{1, 3, 3*5}, true);
+  test_is_exhaustive(extents, std::array{5*7, 1, 5}, true);
+  test_is_exhaustive(extents, std::array{7, 3*7, 1}, true);
+
+  // Shuffled variations of the above
+  test_is_exhaustive(extents, std::array{3, 1, 3*5}, false);
+  test_is_exhaustive(extents, std::array{5*7, 5, 1}, false);
+  test_is_exhaustive(extents, std::array{1, 7, 3*7}, false);
+
+  test_is_exhaustive(extents, std::array{2*7, 2*3*7, 2}, false);
+}
+
+constexpr void
+test_is_exhaustive_ones()
+{
+  std::extents<int, 1, 1, 3, 1> extents;
+  test_is_exhaustive(extents, std::array{1, 1, 1, 1}, true);
+  test_is_exhaustive(extents, std::array{1, 1, 1, 3}, true);
+  test_is_exhaustive(extents, std::array{3, 3, 1, 3}, true);
+  test_is_exhaustive(extents, std::array{3, 1, 1, 3}, true);
+}
+
+constexpr bool
+test_is_exhaustive_all()
+{
+  test_is_exhaustive_zero();
+  test_is_exhaustive_ones();
+  test_is_exhaustive_3d();
+  return true;
+}
+
+template<int Offset>
+using OffsetMapping = MappingLike<std::extents<int, 1, 2>, {true, true},
+                                 {true, true}, {true, false}, Offset>;
+
+constexpr bool
+test_eq()
+{
+  using Extents = std::extents<int, 1, 2>;
+  std::layout_stride::mapping<Extents> m1;
+  std::layout_left::mapping<Extents> m2;
+  std::layout_right::mapping<std::dextents<int, 2>> m3(m1.extents());
+  std::layout_stride::mapping<Extents> m4(Extents{}, std::array{1, 1});
+  std::layout_stride::mapping<Extents> m5(Extents{}, std::array{2, 1});
+  OffsetMapping<0> m6{Extents{}, std::array{2, 1}};
+  OffsetMapping<1> m7{Extents{}, std::array{2, 1}};
+
+  VERIFY(m1 != m2);
+  VERIFY(m2 != m1);
+  VERIFY(m1 == m3);
+  VERIFY(m1 != m4);
+  VERIFY(m1 == m5);
+  VERIFY(m4 == m2);
+  VERIFY(m4 != m3);
+  VERIFY(m4 != m6);
+  VERIFY(m1 == m6);
+  VERIFY(m1 != m7);
+  return true;
+}
+
+constexpr bool
+test_eq_zero()
+{
+  using Mapping = std::layout_stride::mapping<std::extents<int, 0, 2>>;
+  Mapping m1(std::extents<int, 0, 2>{}, std::array<int, 2>{1, 5});
+  Mapping m2(std::extents<int, 0, 2>{}, std::array<int, 2>{1, 5});
+  Mapping m3(std::extents<int, 0, 2>{}, std::array<int, 2>{5, 1});
+
+  VERIFY(m1 == m2);
+  VERIFY(m1 != m3);
+  return true;
+}
+
+template<typename M1, typename M2>
+  concept has_op_eq = requires (M1 m1, M2 m2)
+  {
+    { m1 == m2 } -> std::same_as<bool>;
+    { m1 != m2 } -> std::same_as<bool>;
+  };
+
+constexpr void
+test_has_op_eq()
+{
+  using E1 = std::extents<int, 1, 2>;
+  constexpr auto FT = std::array{false, true};
+
+  static_assert(!has_op_eq<
+      std::layout_stride::mapping<E1>, MappingLike<E1, FT, FT, FT>>);
+}
+
+int
+main()
+{
+  test_ctor_strides_all();
+  static_assert(test_ctor_strides_all());
+  test_mapping_like_convertible();
+  test_mapping_like_constructible();
+  test_stride_constructible_all();
+
+  test_is_exhaustive_all();
+  static_assert(test_is_exhaustive_all());
+  test_eq();
+  static_assert(test_eq());
+  test_eq_zero();
+  static_assert(test_eq_zero());
+  test_has_op_eq();
+
+  return 0;
+}
-- 
2.49.0

Reply via email to