Skip to content

[libc++] mdspan - implement layout_stride #69650

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 20, 2023

Conversation

crtrott
Copy link
Contributor

@crtrott crtrott commented Oct 19, 2023

This implements layout_stride for C++23 and with that completes the implementation of the C++23 mdspan header. The feature test macro is added, and the status pages updated.

Co-authored-by: Damien L-G [email protected]

Differential Revision: https://reviews.llvm.org/D157171

@crtrott crtrott requested a review from a team as a code owner October 19, 2023 22:26
@llvmbot llvmbot added the libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi. label Oct 19, 2023
@llvmbot
Copy link
Member

llvmbot commented Oct 19, 2023

@llvm/pr-subscribers-libcxx

Author: Christian Trott (crtrott)

Changes

This implements layout_stride for C++23 and with that completes the implementation of the C++23 mdspan header. The feature test macro is added, and the status pages updated.

Co-authored-by: Damien L-G <[email protected]>

Differential Revision: https://reviews.llvm.org/D157171


Patch is 156.98 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/69650.diff

67 Files Affected:

  • (modified) libcxx/docs/FeatureTestMacroTable.rst (+1-1)
  • (modified) libcxx/docs/Status/Cxx23.rst (-1)
  • (modified) libcxx/docs/Status/Cxx23Papers.csv (+4-4)
  • (modified) libcxx/include/CMakeLists.txt (+1)
  • (modified) libcxx/include/__fwd/mdspan.h (+2-5)
  • (modified) libcxx/include/__mdspan/layout_left.h (+21-6)
  • (modified) libcxx/include/__mdspan/layout_right.h (+21-6)
  • (added) libcxx/include/__mdspan/layout_stride.h (+366)
  • (modified) libcxx/include/mdspan (+59)
  • (modified) libcxx/include/module.modulemap.in (+1)
  • (modified) libcxx/include/version (+1-1)
  • (modified) libcxx/modules/std/mdspan.inc (+1-1)
  • (added) libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.ctor.layout_stride.pass.cpp (+83)
  • (added) libcxx/test/libcxx/containers/views/mdspan/layout_right/assert.ctor.layout_stride.pass.cpp (+83)
  • (added) libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.conversion.pass.cpp (+112)
  • (added) libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_array.non_unique.pass.cpp (+67)
  • (added) libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_array.pass.cpp (+73)
  • (added) libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_span.non_unique.pass.cpp (+70)
  • (added) libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_span.pass.cpp (+80)
  • (added) libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.index_operator.pass.cpp (+88)
  • (added) libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.stride.pass.cpp (+36)
  • (modified) libcxx/test/libcxx/containers/views/mdspan/mdspan/assert.conversion.pass.cpp (+1-1)
  • (modified) libcxx/test/libcxx/containers/views/mdspan/mdspan/assert.size.pass.cpp (+1-1)
  • (modified) libcxx/test/libcxx/transitive_includes/cxx03.csv (+1)
  • (modified) libcxx/test/libcxx/transitive_includes/cxx11.csv (+1)
  • (modified) libcxx/test/libcxx/transitive_includes/cxx14.csv (+1)
  • (modified) libcxx/test/libcxx/transitive_includes/cxx17.csv (+1)
  • (modified) libcxx/test/libcxx/transitive_includes/cxx20.csv (+1)
  • (modified) libcxx/test/libcxx/transitive_includes/cxx23.csv (+1)
  • (modified) libcxx/test/libcxx/transitive_includes/cxx26.csv (+1)
  • (renamed) libcxx/test/std/containers/views/mdspan/CustomTestLayouts.h (+30-17)
  • (added) libcxx/test/std/containers/views/mdspan/layout_left/ctor.layout_stride.pass.cpp (+114)
  • (added) libcxx/test/std/containers/views/mdspan/layout_right/ctor.layout_stride.pass.cpp (+114)
  • (added) libcxx/test/std/containers/views/mdspan/layout_stride/comparison.pass.cpp (+198)
  • (added) libcxx/test/std/containers/views/mdspan/layout_stride/ctor.default.pass.cpp (+73)
  • (added) libcxx/test/std/containers/views/mdspan/layout_stride/ctor.extents_array.pass.cpp (+138)
  • (added) libcxx/test/std/containers/views/mdspan/layout_stride/ctor.extents_span.pass.cpp (+141)
  • (added) libcxx/test/std/containers/views/mdspan/layout_stride/ctor.strided_mapping.pass.cpp (+187)
  • (added) libcxx/test/std/containers/views/mdspan/layout_stride/deduction.pass.cpp (+55)
  • (added) libcxx/test/std/containers/views/mdspan/layout_stride/extents.verify.cpp (+33)
  • (added) libcxx/test/std/containers/views/mdspan/layout_stride/index_operator.pass.cpp (+121)
  • (added) libcxx/test/std/containers/views/mdspan/layout_stride/is_exhaustive_corner_case.pass.cpp (+54)
  • (added) libcxx/test/std/containers/views/mdspan/layout_stride/properties.pass.cpp (+116)
  • (added) libcxx/test/std/containers/views/mdspan/layout_stride/required_span_size.pass.cpp (+56)
  • (added) libcxx/test/std/containers/views/mdspan/layout_stride/static_requirements.pass.cpp (+131)
  • (added) libcxx/test/std/containers/views/mdspan/layout_stride/stride.pass.cpp (+57)
  • (modified) libcxx/test/std/containers/views/mdspan/mdspan/assign.pass.cpp (+1-1)
  • (modified) libcxx/test/std/containers/views/mdspan/mdspan/conversion.pass.cpp (+1-1)
  • (modified) libcxx/test/std/containers/views/mdspan/mdspan/conversion.verify.cpp (+1-1)
  • (modified) libcxx/test/std/containers/views/mdspan/mdspan/ctor.copy.pass.cpp (+1-1)
  • (modified) libcxx/test/std/containers/views/mdspan/mdspan/ctor.default.pass.cpp (+1-1)
  • (modified) libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_array.pass.cpp (+1-1)
  • (modified) libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_extents.pass.cpp (+1-1)
  • (modified) libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_integers.pass.cpp (+1-1)
  • (modified) libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_map.pass.cpp (+1-1)
  • (modified) libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_map_acc.pass.cpp (+1-1)
  • (modified) libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_span.pass.cpp (+1-1)
  • (modified) libcxx/test/std/containers/views/mdspan/mdspan/ctor.move.pass.cpp (+1-1)
  • (modified) libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp (+1-1)
  • (modified) libcxx/test/std/containers/views/mdspan/mdspan/index_operator.pass.cpp (+1-1)
  • (modified) libcxx/test/std/containers/views/mdspan/mdspan/move.pass.cpp (+1-1)
  • (modified) libcxx/test/std/containers/views/mdspan/mdspan/properties.pass.cpp (+1-1)
  • (modified) libcxx/test/std/containers/views/mdspan/mdspan/swap.pass.cpp (+1-1)
  • (modified) libcxx/test/std/containers/views/mdspan/mdspan/types.pass.cpp (+1-1)
  • (modified) libcxx/test/std/language.support/support.limits/support.limits.general/mdspan.version.compile.pass.cpp (+10-22)
  • (modified) libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp (+10-22)
  • (modified) libcxx/utils/generate_feature_test_macro_components.py (-1)
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 2b21ec3d2fe23ac..10afd64e0a39469 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -332,7 +332,7 @@ Status
     --------------------------------------------------- -----------------
     ``__cpp_lib_is_scoped_enum``                        ``202011L``
     --------------------------------------------------- -----------------
-    ``__cpp_lib_mdspan``                                *unimplemented*
+    ``__cpp_lib_mdspan``                                ``202207L``
     --------------------------------------------------- -----------------
     ``__cpp_lib_move_only_function``                    *unimplemented*
     --------------------------------------------------- -----------------
diff --git a/libcxx/docs/Status/Cxx23.rst b/libcxx/docs/Status/Cxx23.rst
index 5b4d9a6fe943d45..839640a7c7e881b 100644
--- a/libcxx/docs/Status/Cxx23.rst
+++ b/libcxx/docs/Status/Cxx23.rst
@@ -40,7 +40,6 @@ Paper Status
 
 .. note::
 
-   .. [#note-P0009R18] P0009R18: ``extents``, ``dextents``, ``layout_left``, ``layout_right``, and ``default_accessor`` are implemented.
    .. [#note-P0533R9] P0533R9: ``isfinite``, ``isinf``, ``isnan`` and ``isnormal`` are implemented.
    .. [#note-P1413R3] P1413R3: ``std::aligned_storage_t`` and ``std::aligned_union_t`` are marked deprecated, but
       clang doesn't issue a diagnostic for deprecated using template declarations.
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 35319fb7576d3c6..0513a4a6e9fe9b5 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -51,7 +51,7 @@
 "`P2442R1 <https://wg21.link/P2442R1>`__","LWG","Windowing range adaptors: ``views::chunk`` and ``views::slide``","February 2022","","","|ranges|"
 "`P2443R1 <https://wg21.link/P2443R1>`__","LWG","``views::chunk_by``","February 2022","|Complete|","18.0","|ranges|"
 "","","","","","",""
-"`P0009R18 <https://wg21.link/P0009R18>`__","LWG","mdspan: A Non-Owning Multidimensional Array Reference","July 2022","|In progress| [#note-P0009R18]_",""
+"`P0009R18 <https://wg21.link/P0009R18>`__","LWG","mdspan: A Non-Owning Multidimensional Array Reference","July 2022","|Complete|","18.0"
 "`P0429R9 <https://wg21.link/P0429R9>`__","LWG","A Standard ``flat_map``","July 2022","",""
 "`P1169R4 <https://wg21.link/P1169R4>`__","LWG","``static operator()``","July 2022","|Complete|","16.0"
 "`P1222R4 <https://wg21.link/P1222R4>`__","LWG","A Standard ``flat_set``","July 2022","",""
@@ -89,9 +89,9 @@
 "`P2549R1 <https://wg21.link/P2549R1>`__","LWG","``std::unexpected`` should have ``error()`` as member accessor","July 2022","|Complete|","16.0"
 "`P2585R0 <https://wg21.link/P2585R0>`__","LWG","Improving default container formatting","July 2022","|Complete|","17.0"
 "`P2590R2 <https://wg21.link/P2590R2>`__","LWG","Explicit lifetime management","July 2022","",""
-"`P2599R2 <https://wg21.link/P2599R2>`__","LWG","``mdspan::size_type`` should be ``index_type``","July 2022","",""
-"`P2604R0 <https://wg21.link/P2604R0>`__","LWG","mdspan: rename pointer and contiguous","July 2022","",""
-"`P2613R1 <https://wg21.link/P2613R1>`__","LWG","Add the missing ``empty`` to ``mdspan``","July 2022","",""
+"`P2599R2 <https://wg21.link/P2599R2>`__","LWG","``mdspan::size_type`` should be ``index_type``","July 2022","|Complete|","18.0"
+"`P2604R0 <https://wg21.link/P2604R0>`__","LWG","mdspan: rename pointer and contiguous","July 2022","|Complete|","18.0"
+"`P2613R1 <https://wg21.link/P2613R1>`__","LWG","Add the missing ``empty`` to ``mdspan``","July 2022","|Complete|","18.0"
 "","","","","","",""
 "`P1202R5 <https://wg21.link/P1202R5>`__","LWG", "Asymmetric Fences", "November 2022","","","|concurrency TS|"
 "`P1264R2 <https://wg21.link/P1264R2>`__","LWG", "Revising the wording of ``stream`` input operations", "November 2022","|Complete|","9.0",""
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 9b03430a87d8338..85470c5b337f39f 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -508,6 +508,7 @@ set(files
   __mdspan/extents.h
   __mdspan/layout_left.h
   __mdspan/layout_right.h
+  __mdspan/layout_stride.h
   __mdspan/mdspan.h
   __memory/addressof.h
   __memory/align.h
diff --git a/libcxx/include/__fwd/mdspan.h b/libcxx/include/__fwd/mdspan.h
index a3628c2d60dcc8f..8889567a047f6ec 100644
--- a/libcxx/include/__fwd/mdspan.h
+++ b/libcxx/include/__fwd/mdspan.h
@@ -42,14 +42,11 @@ struct layout_right {
   class mapping;
 };
 
-/*
-// Will be implemented with follow on revision
 // Layout policy with a unique mapping where strides are arbitrary
 struct layout_stride {
-  template<class Extents>
-    class mapping;
+  template <class _Extents>
+  class mapping;
 };
-*/
 
 #endif // _LIBCPP_STD_VER >= 23
 
diff --git a/libcxx/include/__mdspan/layout_left.h b/libcxx/include/__mdspan/layout_left.h
index 5faae597f6f81fd..fd644fa0c53226b 100644
--- a/libcxx/include/__mdspan/layout_left.h
+++ b/libcxx/include/__mdspan/layout_left.h
@@ -110,12 +110,27 @@ class layout_left::mapping {
         "layout_left::mapping converting ctor: other.required_span_size() must be representable as index_type.");
   }
 
-// FIXME: add when we add other layouts
-#  if 0
-    template<class _OtherExtents>
-      constexpr explicit(extents_type::rank() > 0)
-        mapping(const layout_stride::mapping_<OtherExtents>&) noexcept;
-#  endif
+  template <class _OtherExtents>
+    requires(is_constructible_v<extents_type, _OtherExtents>)
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(extents_type::rank() > 0)
+      mapping(const layout_stride::mapping<_OtherExtents>& __other) noexcept
+      : __extents_(__other.extents()) {
+    if constexpr (extents_type::rank() > 0) {
+      _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+          ([&]() {
+            using _CommonType = common_type_t<typename extents_type::index_type, typename _OtherExtents::index_type>;
+            for (rank_type __r = 0; __r < extents_type::rank(); __r++)
+              if (static_cast<_CommonType>(stride(__r)) != static_cast<_CommonType>(__other.stride(__r)))
+                return false;
+            return true;
+          }()),
+          "layout_left::mapping from layout_stride ctor: strides are not compatible with layout_left.");
+      _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+          __mdspan_detail::__is_representable_as<index_type>(__other.required_span_size()),
+          "layout_left::mapping from layout_stride ctor: other.required_span_size() must be representable as "
+          "index_type.");
+    }
+  }
 
   _LIBCPP_HIDE_FROM_ABI constexpr mapping& operator=(const mapping&) noexcept = default;
 
diff --git a/libcxx/include/__mdspan/layout_right.h b/libcxx/include/__mdspan/layout_right.h
index 4f95789a2fafcc9..8e64d07dd52309c 100644
--- a/libcxx/include/__mdspan/layout_right.h
+++ b/libcxx/include/__mdspan/layout_right.h
@@ -109,12 +109,27 @@ class layout_right::mapping {
         "layout_right::mapping converting ctor: other.required_span_size() must be representable as index_type.");
   }
 
-// FIXME: add when we add other layouts
-#  if 0
-    template<class _OtherExtents>
-      constexpr explicit(extents_type::rank() > 0)
-        mapping(const layout_stride::mapping_<OtherExtents>&) noexcept;
-#  endif
+  template <class _OtherExtents>
+    requires(is_constructible_v<extents_type, _OtherExtents>)
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(extents_type::rank() > 0)
+      mapping(const layout_stride::mapping<_OtherExtents>& __other) noexcept
+      : __extents_(__other.extents()) {
+    if constexpr (extents_type::rank() > 0) {
+      _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+          ([&]() {
+            using _CommonType = common_type_t<typename extents_type::index_type, typename _OtherExtents::index_type>;
+            for (rank_type __r = 0; __r < extents_type::rank(); __r++)
+              if (static_cast<_CommonType>(stride(__r)) != static_cast<_CommonType>(__other.stride(__r)))
+                return false;
+            return true;
+          }()),
+          "layout_right::mapping from layout_stride ctor: strides are not compatible with layout_right.");
+      _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+          __mdspan_detail::__is_representable_as<index_type>(__other.required_span_size()),
+          "layout_right::mapping from layout_stride ctor: other.required_span_size() must be representable as "
+          "index_type.");
+    }
+  }
 
   _LIBCPP_HIDE_FROM_ABI constexpr mapping& operator=(const mapping&) noexcept = default;
 
diff --git a/libcxx/include/__mdspan/layout_stride.h b/libcxx/include/__mdspan/layout_stride.h
new file mode 100644
index 000000000000000..77934bfa11d9de0
--- /dev/null
+++ b/libcxx/include/__mdspan/layout_stride.h
@@ -0,0 +1,366 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//                        Kokkos v. 4.0
+//       Copyright (2022) National Technology & Engineering
+//               Solutions of Sandia, LLC (NTESS).
+//
+// Under the terms of Contract DE-NA0003525 with NTESS,
+// the U.S. Government retains certain rights in this software.
+//
+//===---------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___MDSPAN_LAYOUT_STRIDE_H
+#define _LIBCPP___MDSPAN_LAYOUT_STRIDE_H
+
+#include <__assert>
+#include <__config>
+#include <__fwd/mdspan.h>
+#include <__mdspan/extents.h>
+#include <__type_traits/is_constructible.h>
+#include <__type_traits/is_convertible.h>
+#include <__type_traits/is_nothrow_constructible.h>
+#include <__utility/as_const.h>
+#include <__utility/integer_sequence.h>
+#include <__utility/swap.h>
+#include <array>
+#include <cinttypes>
+#include <cstddef>
+#include <limits>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+#  pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 23
+
+namespace __mdspan_detail {
+template <class _Layout, class _Mapping>
+constexpr bool __is_mapping_of =
+    is_same_v<typename _Layout::template mapping<typename _Mapping::extents_type>, _Mapping>;
+
+template <class _Mapping>
+concept __layout_mapping_alike = requires {
+  requires __is_mapping_of<typename _Mapping::layout_type, _Mapping>;
+  requires __is_extents_v<typename _Mapping::extents_type>;
+  { _Mapping::is_always_strided() } -> same_as<bool>;
+  { _Mapping::is_always_exhaustive() } -> same_as<bool>;
+  { _Mapping::is_always_unique() } -> same_as<bool>;
+  bool_constant<_Mapping::is_always_strided()>::value;
+  bool_constant<_Mapping::is_always_exhaustive()>::value;
+  bool_constant<_Mapping::is_always_unique()>::value;
+};
+} // namespace __mdspan_detail
+
+template <class _Extents>
+class layout_stride::mapping {
+public:
+  static_assert(__mdspan_detail::__is_extents<_Extents>::value,
+                "layout_stride::mapping template argument must be a specialization of extents.");
+
+  using extents_type = _Extents;
+  using index_type   = typename extents_type::index_type;
+  using size_type    = typename extents_type::size_type;
+  using rank_type    = typename extents_type::rank_type;
+  using layout_type  = layout_stride;
+
+private:
+  static constexpr rank_type __rank_ = extents_type::rank();
+
+  // Used for default construction check and mandates
+  _LIBCPP_HIDE_FROM_ABI static constexpr bool __required_span_size_is_representable(const extents_type& __ext) {
+    if constexpr (__rank_ == 0)
+      return true;
+
+    index_type __prod = __ext.extent(0);
+    for (rank_type __r = 1; __r < __rank_; __r++) {
+      bool __overflowed = __builtin_mul_overflow(__prod, __ext.extent(__r), &__prod);
+      if (__overflowed)
+        return false;
+    }
+    return true;
+  }
+
+  template <class _OtherIndexType>
+  _LIBCPP_HIDE_FROM_ABI static constexpr bool
+  __required_span_size_is_representable(const extents_type& __ext, span<_OtherIndexType, __rank_> __strides) {
+    if constexpr (__rank_ == 0)
+      return true;
+
+    index_type __size = 1;
+    for (rank_type __r = 0; __r < __rank_; __r++) {
+      // We can only check correct conversion of _OtherIndexType if it is an integral
+      if constexpr (is_integral_v<_OtherIndexType>) {
+        using _CommonType = common_type_t<index_type, _OtherIndexType>;
+        if (static_cast<_CommonType>(__strides[__r]) > static_cast<_CommonType>(numeric_limits<index_type>::max()))
+          return false;
+      }
+      if (__ext.extent(__r) == static_cast<index_type>(0))
+        return true;
+      index_type __prod     = (__ext.extent(__r) - 1);
+      bool __overflowed_mul = __builtin_mul_overflow(__prod, static_cast<index_type>(__strides[__r]), &__prod);
+      if (__overflowed_mul)
+        return false;
+      bool __overflowed_add = __builtin_add_overflow(__size, __prod, &__size);
+      if (__overflowed_add)
+        return false;
+    }
+    return true;
+  }
+
+  // compute offset of a strided layout mapping
+  template <class _StridedMapping>
+  _LIBCPP_HIDE_FROM_ABI static constexpr index_type __offset(const _StridedMapping& __mapping) {
+    if constexpr (_StridedMapping::extents_type::rank() == 0) {
+      return static_cast<index_type>(__mapping());
+    } else if (__mapping.required_span_size() == static_cast<typename _StridedMapping::index_type>(0)) {
+      return static_cast<index_type>(0);
+    } else {
+      return [&]<size_t... _Pos>(index_sequence<_Pos...>) {
+        return static_cast<index_type>(__mapping((_Pos ? 0 : 0)...));
+      }(make_index_sequence<__rank_>());
+    }
+  }
+
+  // compute the permutation for sorting the stride array
+  // we never actually sort the stride array
+  _LIBCPP_HIDE_FROM_ABI constexpr void __bubble_sort_by_strides(array<rank_type, __rank_>& __permute) const {
+    for (rank_type __i = __rank_ - 1; __i > 0; __i--) {
+      for (rank_type __r = 0; __r < __i; __r++) {
+        if (__strides_[__permute[__r]] > __strides_[__permute[__r + 1]]) {
+          swap(__permute[__r], __permute[__r + 1]);
+        } else {
+          // if two strides are the same then one of the associated extents must be 1 or 0
+          // both could be, but you can't have one larger than 1 come first
+          if ((__strides_[__permute[__r]] == __strides_[__permute[__r + 1]]) &&
+              (__extents_.extent(__permute[__r]) > static_cast<index_type>(1)))
+            swap(__permute[__r], __permute[__r + 1]);
+        }
+      }
+    }
+  }
+
+  static_assert((extents_type::rank_dynamic() > 0) || __required_span_size_is_representable(extents_type()),
+                "layout_stride::mapping product of static extents must be representable as index_type.");
+
+public:
+  // [mdspan.layout.stride.cons], constructors
+  _LIBCPP_HIDE_FROM_ABI constexpr mapping() noexcept : __extents_(extents_type()) {
+    // Note the nominal precondition is covered by above static assert since
+    // if rank_dynamic is != 0 required_span_size is zero for default construction
+    if constexpr (__rank_ > 0) {
+      index_type __stride = 1;
+      for (rank_type __r = __rank_ - 1; __r > static_cast<rank_type>(0); __r--) {
+        __strides_[__r] = __stride;
+        __stride *= __extents_.extent(__r);
+      }
+      __strides_[0] = __stride;
+    }
+  }
+
+  _LIBCPP_HIDE_FROM_ABI constexpr mapping(const mapping&) noexcept = default;
+
+  template <class _OtherIndexType>
+    requires(is_convertible_v<const _OtherIndexType&, index_type> &&
+             is_nothrow_constructible_v<index_type, const _OtherIndexType&>)
+  _LIBCPP_HIDE_FROM_ABI constexpr mapping(const extents_type& __ext, span<_OtherIndexType, __rank_> __strides) noexcept
+      : __extents_(__ext), __strides_([&]<size_t... _Pos>(index_sequence<_Pos...>) {
+          return __mdspan_detail::__possibly_empty_array<index_type, __rank_>{
+              static_cast<index_type>(std::as_const(__strides[_Pos]))...};
+        }(make_index_sequence<__rank_>())) {
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+        ([&]<size_t... _Pos>(index_sequence<_Pos...>) {
+          // For integrals we can do a pre-conversion check, for other types not
+          if constexpr (is_integral_v<_OtherIndexType>) {
+            return ((__strides[_Pos] > static_cast<_OtherIndexType>(0)) && ... && true);
+          } else {
+            return ((static_cast<index_type>(__strides[_Pos]) > static_cast<index_type>(0)) && ... && true);
+          }
+        }(make_index_sequence<__rank_>())),
+        "layout_stride::mapping ctor: all strides must be greater than 0");
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+        __required_span_size_is_representable(__ext, __strides),
+        "layout_stride::mapping ctor: required span size is not representable as index_type.");
+    if constexpr (__rank_ > 1) {
+      _LIBCPP_ASSERT_UNCATEGORIZED(
+          ([&]<size_t... _Pos>(index_sequence<_Pos...>) {
+            // basically sort the dimensions based on strides and extents, sorting is represented in permute array
+            array<rank_type, __rank_> __permute{_Pos...};
+            __bubble_sort_by_strides(__permute);
+
+            // check that this permutations represents a growing set
+            for (rank_type __i = 1; __i < __rank_; __i++)
+              if (static_cast<index_type>(__strides[__permute[__i]]) <
+                  static_cast<index_type>(__strides[__permute[__i - 1]]) * __extents_.extent(__permute[__i - 1]))
+                return false;
+            return true;
+          }(make_index_sequence<__rank_>())),
+          "layout_stride::mapping ctor: the provided extents and strides lead to a non-unique mapping");
+    }
+  }
+
+  template <class _OtherIndexType>
+    requires(is_convertible_v<const _OtherIndexType&, index_type> &&
+             is_nothrow_constructible_v<index_type, const _OtherIndexType&>)
+  _LIBCPP_HIDE_FROM_ABI constexpr mapping(const extents_type& __ext,
+                                          const array<_OtherIndexType, __rank_>& __strides) noexcept
+      : mapping(__ext, span(__strides)) {}
+
+  template <class _StridedLayoutMapping>
+    requires(__mdspan_detail::__layout_mapping_alike<_StridedLayoutMapping> &&
+             is_constructible_v<extents_type, typename _StridedLayoutMapping::extents_type> &&
+             _StridedLayoutMapping::is_always_unique() && _StridedLayoutMapping::is_always_strided())
+  _LIBCPP_HIDE_FROM_ABI constexpr explicit(
+      !(is_convertible_v<typename _StridedLayoutMapping::extents_type, extents_type> &&
+        (__mdspan_detail::__is_mapping_of<layout_left, _StridedLayoutMapping> ||
+         __mdspan_detail::__is_mapping_of<layout_right, _StridedLayoutMapping> ||
+         __mdspan_detail::__is_mapping_of<layout_stride, _StridedLayoutMapping>)))
+      mapping(const _StridedLayoutMapping& __other) noexcept
+      : __extents_(__other.extents()), __strides_([&]<size_t... _Pos>(index_sequence<_Pos...>) {
+          // stride() only compiles for rank > 0
+          if constexpr (__rank_ > 0) {
+            return __mdspan_detail::__possibly_empty_array<index_type, __rank_>{
+                static_cast<index_type>(__other.stride(_Pos))...};
+          } else {
+            return __mdspan_detail::__possibly_empty_array<index_type, 0>{};
+          }
+        }(make_index_sequence<__rank_>())) {
+    // stride() only compiles for rank > 0
+    if constexpr (__rank_ > 0) {
+      _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+          ([&]<size_t... _Pos>(index_sequence<_Pos...>) {
+            return ((static_cast<index_type>(__other.stride(_Pos)) > static_cast<index_type>(0)) && ... && true);
+          }(make_index_sequence<__rank_>())),
+          "layout_stride::mapping converting ctor: all strides must be greater than 0");
+    }
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(
+        __mdspan_detail::__is_representable_as<index_type>(__other.required_span_size()),
+        "layout_stride::mapping converting ctor: other.required_span_size() must be representable as index_type.");
+    _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(static_cast<index_type>(0) == __offset(__other),
+                               ...
[truncated]

@ldionne
Copy link
Member

ldionne commented Oct 19, 2023

@crtrott Remove libcxx/test/std/containers/views/mdspan/mdspan/CustomTestLayouts.h from the ignore_format.txt list, that should solve the CI issue.

Copy link
Member

@ldionne ldionne left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of the review happened on Phabricator at the linked review, but it's currently unresponsive.

LGTM once the CI is green.

This implements layout_stride for C++23 and with that completes the implementation of the C++23 mdspan header.
The feature test macro is added, and the status pages updated.

Co-authored-by: Damien L-G <[email protected]>

Differential Revision: https://reviews.llvm.org/D157171
@crtrott crtrott force-pushed the mdspan-layout-stride branch from f0b8458 to 87f7271 Compare October 19, 2023 23:38
@crtrott
Copy link
Contributor Author

crtrott commented Oct 19, 2023

Done.

@crtrott crtrott merged commit 639a098 into llvm:main Oct 20, 2023
@crtrott crtrott deleted the mdspan-layout-stride branch October 20, 2023 14:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
libc++ libc++ C++ Standard Library. Not GNU libstdc++. Not libc++abi.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants