[libcxx-commits] [libcxx] [libc++] mdspan - implement layout_stride (PR #69650)

via libcxx-commits libcxx-commits at lists.llvm.org
Thu Oct 19 15:27:18 PDT 2023


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-libcxx

Author: Christian Trott (crtrott)

<details>
<summary>Changes</summary>

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 <dalg24@<!-- -->gmail.com>

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
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]

``````````

</details>


https://github.com/llvm/llvm-project/pull/69650


More information about the libcxx-commits mailing list