[libcxx-commits] [libcxx] [libc++] mdspan - implement layout_stride (PR #69650)
Christian Trott via libcxx-commits
libcxx-commits at lists.llvm.org
Thu Oct 19 15:26:09 PDT 2023
https://github.com/crtrott created https://github.com/llvm/llvm-project/pull/69650
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 at gmail.com>
Differential Revision: https://reviews.llvm.org/D157171
>From f0b845826588f3051522d5a35154a5f5ab63583d Mon Sep 17 00:00:00 2001
From: Christian Trott <crtrott at sandia.gov>
Date: Thu, 19 Oct 2023 16:22:20 -0600
Subject: [PATCH] [libc++] mdspan - implement layout_stride
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 at gmail.com>
Differential Revision: https://reviews.llvm.org/D157171
---
libcxx/docs/FeatureTestMacroTable.rst | 2 +-
libcxx/docs/Status/Cxx23.rst | 1 -
libcxx/docs/Status/Cxx23Papers.csv | 8 +-
libcxx/include/CMakeLists.txt | 1 +
libcxx/include/__fwd/mdspan.h | 7 +-
libcxx/include/__mdspan/layout_left.h | 27 +-
libcxx/include/__mdspan/layout_right.h | 27 +-
libcxx/include/__mdspan/layout_stride.h | 366 ++++++++++++++++++
libcxx/include/mdspan | 59 +++
libcxx/include/module.modulemap.in | 1 +
libcxx/include/version | 2 +-
libcxx/modules/std/mdspan.inc | 2 +-
.../assert.ctor.layout_stride.pass.cpp | 83 ++++
.../assert.ctor.layout_stride.pass.cpp | 83 ++++
.../layout_stride/assert.conversion.pass.cpp | 112 ++++++
...ert.ctor.extents_array.non_unique.pass.cpp | 67 ++++
.../assert.ctor.extents_array.pass.cpp | 73 ++++
...sert.ctor.extents_span.non_unique.pass.cpp | 70 ++++
.../assert.ctor.extents_span.pass.cpp | 80 ++++
.../assert.index_operator.pass.cpp | 88 +++++
.../layout_stride/assert.stride.pass.cpp | 36 ++
.../mdspan/mdspan/assert.conversion.pass.cpp | 2 +-
.../views/mdspan/mdspan/assert.size.pass.cpp | 2 +-
.../test/libcxx/transitive_includes/cxx03.csv | 1 +
.../test/libcxx/transitive_includes/cxx11.csv | 1 +
.../test/libcxx/transitive_includes/cxx14.csv | 1 +
.../test/libcxx/transitive_includes/cxx17.csv | 1 +
.../test/libcxx/transitive_includes/cxx20.csv | 1 +
.../test/libcxx/transitive_includes/cxx23.csv | 1 +
.../test/libcxx/transitive_includes/cxx26.csv | 1 +
.../mdspan/{mdspan => }/CustomTestLayouts.h | 47 ++-
.../layout_left/ctor.layout_stride.pass.cpp | 114 ++++++
.../layout_right/ctor.layout_stride.pass.cpp | 114 ++++++
.../mdspan/layout_stride/comparison.pass.cpp | 198 ++++++++++
.../layout_stride/ctor.default.pass.cpp | 73 ++++
.../layout_stride/ctor.extents_array.pass.cpp | 138 +++++++
.../layout_stride/ctor.extents_span.pass.cpp | 141 +++++++
.../ctor.strided_mapping.pass.cpp | 187 +++++++++
.../mdspan/layout_stride/deduction.pass.cpp | 55 +++
.../mdspan/layout_stride/extents.verify.cpp | 33 ++
.../layout_stride/index_operator.pass.cpp | 121 ++++++
.../is_exhaustive_corner_case.pass.cpp | 54 +++
.../mdspan/layout_stride/properties.pass.cpp | 116 ++++++
.../layout_stride/required_span_size.pass.cpp | 56 +++
.../static_requirements.pass.cpp | 131 +++++++
.../mdspan/layout_stride/stride.pass.cpp | 57 +++
.../views/mdspan/mdspan/assign.pass.cpp | 2 +-
.../views/mdspan/mdspan/conversion.pass.cpp | 2 +-
.../views/mdspan/mdspan/conversion.verify.cpp | 2 +-
.../views/mdspan/mdspan/ctor.copy.pass.cpp | 2 +-
.../views/mdspan/mdspan/ctor.default.pass.cpp | 2 +-
.../mdspan/mdspan/ctor.dh_array.pass.cpp | 2 +-
.../mdspan/mdspan/ctor.dh_extents.pass.cpp | 2 +-
.../mdspan/mdspan/ctor.dh_integers.pass.cpp | 2 +-
.../views/mdspan/mdspan/ctor.dh_map.pass.cpp | 2 +-
.../mdspan/mdspan/ctor.dh_map_acc.pass.cpp | 2 +-
.../views/mdspan/mdspan/ctor.dh_span.pass.cpp | 2 +-
.../views/mdspan/mdspan/ctor.move.pass.cpp | 2 +-
.../views/mdspan/mdspan/deduction.pass.cpp | 2 +-
.../mdspan/mdspan/index_operator.pass.cpp | 2 +-
.../views/mdspan/mdspan/move.pass.cpp | 2 +-
.../views/mdspan/mdspan/properties.pass.cpp | 2 +-
.../views/mdspan/mdspan/swap.pass.cpp | 2 +-
.../views/mdspan/mdspan/types.pass.cpp | 2 +-
.../mdspan.version.compile.pass.cpp | 32 +-
.../version.version.compile.pass.cpp | 32 +-
.../generate_feature_test_macro_components.py | 1 -
67 files changed, 2835 insertions(+), 107 deletions(-)
create mode 100644 libcxx/include/__mdspan/layout_stride.h
create mode 100644 libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.ctor.layout_stride.pass.cpp
create mode 100644 libcxx/test/libcxx/containers/views/mdspan/layout_right/assert.ctor.layout_stride.pass.cpp
create mode 100644 libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.conversion.pass.cpp
create mode 100644 libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_array.non_unique.pass.cpp
create mode 100644 libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_array.pass.cpp
create mode 100644 libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_span.non_unique.pass.cpp
create mode 100644 libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_span.pass.cpp
create mode 100644 libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.index_operator.pass.cpp
create mode 100644 libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.stride.pass.cpp
rename libcxx/test/std/containers/views/mdspan/{mdspan => }/CustomTestLayouts.h (87%)
create mode 100644 libcxx/test/std/containers/views/mdspan/layout_left/ctor.layout_stride.pass.cpp
create mode 100644 libcxx/test/std/containers/views/mdspan/layout_right/ctor.layout_stride.pass.cpp
create mode 100644 libcxx/test/std/containers/views/mdspan/layout_stride/comparison.pass.cpp
create mode 100644 libcxx/test/std/containers/views/mdspan/layout_stride/ctor.default.pass.cpp
create mode 100644 libcxx/test/std/containers/views/mdspan/layout_stride/ctor.extents_array.pass.cpp
create mode 100644 libcxx/test/std/containers/views/mdspan/layout_stride/ctor.extents_span.pass.cpp
create mode 100644 libcxx/test/std/containers/views/mdspan/layout_stride/ctor.strided_mapping.pass.cpp
create mode 100644 libcxx/test/std/containers/views/mdspan/layout_stride/deduction.pass.cpp
create mode 100644 libcxx/test/std/containers/views/mdspan/layout_stride/extents.verify.cpp
create mode 100644 libcxx/test/std/containers/views/mdspan/layout_stride/index_operator.pass.cpp
create mode 100644 libcxx/test/std/containers/views/mdspan/layout_stride/is_exhaustive_corner_case.pass.cpp
create mode 100644 libcxx/test/std/containers/views/mdspan/layout_stride/properties.pass.cpp
create mode 100644 libcxx/test/std/containers/views/mdspan/layout_stride/required_span_size.pass.cpp
create mode 100644 libcxx/test/std/containers/views/mdspan/layout_stride/static_requirements.pass.cpp
create mode 100644 libcxx/test/std/containers/views/mdspan/layout_stride/stride.pass.cpp
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),
+ "layout_stride::mapping converting ctor: base offset of mapping must be zero.");
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr mapping& operator=(const mapping&) noexcept = default;
+
+ // [mdspan.layout.stride.obs], observers
+ _LIBCPP_HIDE_FROM_ABI constexpr const extents_type& extents() const noexcept { return __extents_; }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr array<index_type, __rank_> strides() const noexcept {
+ return [&]<size_t... _Pos>(index_sequence<_Pos...>) {
+ return array<index_type, __rank_>{__strides_[_Pos]...};
+ }(make_index_sequence<__rank_>());
+ }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr index_type required_span_size() const noexcept {
+ if constexpr (__rank_ == 0) {
+ return static_cast<index_type>(1);
+ } else {
+ return [&]<size_t... _Pos>(index_sequence<_Pos...>) {
+ if ((__extents_.extent(_Pos) * ... * 1) == 0)
+ return static_cast<index_type>(0);
+ else
+ return static_cast<index_type>(
+ static_cast<index_type>(1) +
+ (((__extents_.extent(_Pos) - static_cast<index_type>(1)) * __strides_[_Pos]) + ... +
+ static_cast<index_type>(0)));
+ }(make_index_sequence<__rank_>());
+ }
+ }
+
+ template <class... _Indices>
+ requires((sizeof...(_Indices) == __rank_) && (is_convertible_v<_Indices, index_type> && ...) &&
+ (is_nothrow_constructible_v<index_type, _Indices> && ...))
+ _LIBCPP_HIDE_FROM_ABI constexpr index_type operator()(_Indices... __idx) const noexcept {
+ // Mappings are generally meant to be used for accessing allocations and are meant to guarantee to never
+ // return a value exceeding required_span_size(), which is used to know how large an allocation one needs
+ // Thus, this is a canonical point in multi-dimensional data structures to make invalid element access checks
+ // However, mdspan does check this on its own, so for now we avoid double checking in hardened mode
+ _LIBCPP_ASSERT_UNCATEGORIZED(__mdspan_detail::__is_multidimensional_index_in(__extents_, __idx...),
+ "layout_stride::mapping: out of bounds indexing");
+ return [&]<size_t... _Pos>(index_sequence<_Pos...>) {
+ return ((static_cast<index_type>(__idx) * __strides_[_Pos]) + ... + index_type(0));
+ }(make_index_sequence<sizeof...(_Indices)>());
+ }
+
+ _LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_unique() noexcept { return true; }
+ _LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_exhaustive() noexcept { return false; }
+ _LIBCPP_HIDE_FROM_ABI static constexpr bool is_always_strided() noexcept { return true; }
+
+ _LIBCPP_HIDE_FROM_ABI static constexpr bool is_unique() noexcept { return true; }
+ // The answer of this function is fairly complex in the case where one or more
+ // extents are zero.
+ // Technically it is meaningless to query is_exhaustive() in that case, but unfortunately
+ // the way the standard defines this function, we can't give a simple true or false then.
+ _LIBCPP_HIDE_FROM_ABI constexpr bool is_exhaustive() const noexcept {
+ if constexpr (__rank_ == 0)
+ return true;
+ else {
+ index_type __span_size = required_span_size();
+ if (__span_size == static_cast<index_type>(0)) {
+ if constexpr (__rank_ == 1)
+ return __strides_[0] == 1;
+ else {
+ rank_type __r_largest = 0;
+ for (rank_type __r = 1; __r < __rank_; __r++)
+ if (__strides_[__r] > __strides_[__r_largest])
+ __r_largest = __r;
+ for (rank_type __r = 0; __r < __rank_; __r++)
+ if (__extents_.extent(__r) == 0 && __r != __r_largest)
+ return false;
+ return true;
+ }
+ } else {
+ return required_span_size() == [&]<size_t... _Pos>(index_sequence<_Pos...>) {
+ return (__extents_.extent(_Pos) * ... * static_cast<index_type>(1));
+ }(make_index_sequence<__rank_>());
+ }
+ }
+ }
+ _LIBCPP_HIDE_FROM_ABI static constexpr bool is_strided() noexcept { return true; }
+
+ // according to the standard layout_stride does not have a constraint on stride(r) for rank>0
+ // it still has the precondition though
+ _LIBCPP_HIDE_FROM_ABI constexpr index_type stride(rank_type __r) const noexcept {
+ _LIBCPP_ASSERT_VALID_ELEMENT_ACCESS(__r < __rank_, "layout_stride::mapping::stride(): invalid rank index");
+ return __strides_[__r];
+ }
+
+ template <class _OtherMapping>
+ requires(__mdspan_detail::__layout_mapping_alike<_OtherMapping> &&
+ (_OtherMapping::extents_type::rank() == __rank_) && _OtherMapping::is_always_strided())
+ _LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const mapping& __lhs, const _OtherMapping& __rhs) noexcept {
+ if (__offset(__rhs))
+ return false;
+ if constexpr (__rank_ == 0)
+ return true;
+ else {
+ return __lhs.extents() == __rhs.extents() && [&]<size_t... _Pos>(index_sequence<_Pos...>) {
+ // avoid warning when comparing signed and unsigner integers and pick the wider of two types
+ using _CommonType = common_type_t<index_type, typename _OtherMapping::index_type>;
+ return ((static_cast<_CommonType>(__lhs.stride(_Pos)) == static_cast<_CommonType>(__rhs.stride(_Pos))) && ... &&
+ true);
+ }(make_index_sequence<__rank_>());
+ }
+ }
+
+private:
+ _LIBCPP_NO_UNIQUE_ADDRESS extents_type __extents_{};
+ _LIBCPP_NO_UNIQUE_ADDRESS __mdspan_detail::__possibly_empty_array<index_type, __rank_> __strides_{};
+};
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___MDSPAN_LAYOUT_STRIDE_H
diff --git a/libcxx/include/mdspan b/libcxx/include/mdspan
index e38816657751b47..05f612593f4b5ba 100644
--- a/libcxx/include/mdspan
+++ b/libcxx/include/mdspan
@@ -190,6 +190,63 @@ namespace std {
};
}
+// layout_stride synopsis
+
+namespace std {
+ template<class Extents>
+ class layout_stride::mapping {
+ public:
+ 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(); // exposition only
+
+ public:
+ // [mdspan.layout.stride.cons], constructors
+ constexpr mapping() noexcept;
+ constexpr mapping(const mapping&) noexcept = default;
+ template<class OtherIndexType>
+ constexpr mapping(const extents_type&, span<OtherIndexType, rank_>) noexcept;
+ template<class OtherIndexType>
+ constexpr mapping(const extents_type&, const array<OtherIndexType, rank_>&) noexcept;
+
+ template<class StridedLayoutMapping>
+ constexpr explicit(see below) mapping(const StridedLayoutMapping&) noexcept;
+
+ constexpr mapping& operator=(const mapping&) noexcept = default;
+
+ // [mdspan.layout.stride.obs], observers
+ constexpr const extents_type& extents() const noexcept { return extents_; }
+ constexpr array<index_type, rank_> strides() const noexcept { return strides_; }
+
+ constexpr index_type required_span_size() const noexcept;
+
+ template<class... Indices>
+ constexpr index_type operator()(Indices...) const noexcept;
+
+ static constexpr bool is_always_unique() noexcept { return true; }
+ static constexpr bool is_always_exhaustive() noexcept { return false; }
+ static constexpr bool is_always_strided() noexcept { return true; }
+
+ static constexpr bool is_unique() noexcept { return true; }
+ constexpr bool is_exhaustive() const noexcept;
+ static constexpr bool is_strided() noexcept { return true; }
+
+ constexpr index_type stride(rank_type i) const noexcept { return strides_[i]; }
+
+ template<class OtherMapping>
+ friend constexpr bool operator==(const mapping&, const OtherMapping&) noexcept;
+
+ private:
+ extents_type extents_{}; // exposition only
+ array<index_type, rank_> strides_{}; // exposition only
+ };
+}
+
// default_accessor synopsis
namespace std {
@@ -348,7 +405,9 @@ namespace std {
#include <__mdspan/extents.h>
#include <__mdspan/layout_left.h>
#include <__mdspan/layout_right.h>
+#include <__mdspan/layout_stride.h>
#include <__mdspan/mdspan.h>
+#include <version>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 3e5a8a391b6e69c..41158212c9a86cf 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1510,6 +1510,7 @@ module std_private_mdspan_extents [system] {
}
module std_private_mdspan_layout_left [system] { header "__mdspan/layout_left.h" }
module std_private_mdspan_layout_right [system] { header "__mdspan/layout_right.h" }
+module std_private_mdspan_layout_stride [system] { header "__mdspan/layout_stride.h" }
module std_private_mdspan_mdspan [system] { header "__mdspan/mdspan.h" }
module std_private_mdspan_mdspan_fwd [system] { header "__fwd/mdspan.h" }
diff --git a/libcxx/include/version b/libcxx/include/version
index 4fdfec4f34e3f6e..c41ce745b59304b 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -427,7 +427,7 @@ __cpp_lib_within_lifetime 202306L <type_traits>
# define __cpp_lib_forward_like 202207L
# define __cpp_lib_invoke_r 202106L
# define __cpp_lib_is_scoped_enum 202011L
-// # define __cpp_lib_mdspan 202207L
+# define __cpp_lib_mdspan 202207L
// # define __cpp_lib_move_only_function 202110L
# undef __cpp_lib_optional
# define __cpp_lib_optional 202110L
diff --git a/libcxx/modules/std/mdspan.inc b/libcxx/modules/std/mdspan.inc
index 888c94ad59cfd9a..ba3f3926c3abdd1 100644
--- a/libcxx/modules/std/mdspan.inc
+++ b/libcxx/modules/std/mdspan.inc
@@ -18,7 +18,7 @@ export namespace std {
// [mdspan.layout], layout mapping
using std::layout_left;
using std::layout_right;
- // using std::layout_stride;
+ using std::layout_stride;
// [mdspan.accessor.default], class template default_accessor
using std::default_accessor;
diff --git a/libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.ctor.layout_stride.pass.cpp b/libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.ctor.layout_stride.pass.cpp
new file mode 100644
index 000000000000000..25ffa46a90ef27a
--- /dev/null
+++ b/libcxx/test/libcxx/containers/views/mdspan/layout_left/assert.ctor.layout_stride.pass.cpp
@@ -0,0 +1,83 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: libcpp-hardening-mode=unchecked
+// XFAIL: availability-verbose_abort-missing
+// ADDITIONAL_COMPILE_FLAGS: -Wno-ctad-maybe-unsupported
+
+// FIXME: https://github.com/llvm/llvm-project/issues/64719
+// There appear to be some issues around ctad which make it
+// currently impossible to get this code warning free.
+// Thus added the additional compile flag above
+
+// <mdspan>
+
+// template<class OtherExtents>
+// constexpr explicit(extents_type::rank() > 0)
+// mapping(const layout_stride::mapping<OtherExtents>& other);
+//
+// Constraints: is_constructible_v<extents_type, OtherExtents> is true.
+//
+// Preconditions:
+// - If extents_type::rank() > 0 is true, then for all r in the range [0, extents_type::rank()),
+// other.stride(r) equals other.extents().fwd-prod-of-extents(r), and
+// - other.required_span_size() is representable as a value of type index_type ([basic.fundamental]).
+//
+// Effects: Direct-non-list-initializes extents_ with other.extents().
+
+#include <mdspan>
+#include <cassert>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+ constexpr size_t D = std::dynamic_extent;
+
+ // working case
+ {
+ std::layout_stride::mapping arg(std::extents<int, D>(5), std::array<int, 1>{1});
+ [[maybe_unused]] std::layout_left::mapping<std::extents<size_t, 5>> m(arg); // should work
+ }
+ // mismatch of static extent
+ {
+ std::layout_stride::mapping arg(std::extents<int, D>(5), std::array<int, 1>{1});
+ TEST_LIBCPP_ASSERT_FAILURE(([=] { std::layout_left::mapping<std::extents<int, 3>> m(arg); }()),
+ "extents construction: mismatch of provided arguments with static extents.");
+ }
+ // non-representability of extents itself
+ {
+ std::layout_stride::mapping arg(std::extents<int, D>(500), std::array<int, 1>{1});
+ TEST_LIBCPP_ASSERT_FAILURE(([=] { std::layout_left::mapping<std::extents<char, D>> m(arg); }()),
+ "extents ctor: arguments must be representable as index_type and nonnegative");
+ }
+ // non-representability of required span size
+ {
+ std::layout_stride::mapping arg(std::extents<int, D, D>(100, 3), std::array<int, 2>{1, 100});
+ TEST_LIBCPP_ASSERT_FAILURE(
+ ([=] { std::layout_left::mapping<std::extents<char, D, D>> m(arg); }()),
+ "layout_left::mapping from layout_stride ctor: other.required_span_size() must be "
+ "representable as index_type.");
+ }
+ // strides are not layout_left compatible
+ {
+ std::layout_stride::mapping arg(std::extents<int, D>(5), std::array<int, 1>{2});
+ TEST_LIBCPP_ASSERT_FAILURE(
+ ([=] { std::layout_left::mapping<std::extents<size_t, 5>> m(arg); }()),
+ "layout_left::mapping from layout_stride ctor: strides are not compatible with layout_left.");
+ }
+ {
+ std::layout_stride::mapping arg(std::extents<int, D, D>(100, 3), std::array<int, 2>{2, 200});
+ TEST_LIBCPP_ASSERT_FAILURE(
+ ([=] { std::layout_left::mapping<std::extents<int, D, D>> m(arg); }()),
+ "layout_left::mapping from layout_stride ctor: strides are not compatible with layout_left.");
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx/containers/views/mdspan/layout_right/assert.ctor.layout_stride.pass.cpp b/libcxx/test/libcxx/containers/views/mdspan/layout_right/assert.ctor.layout_stride.pass.cpp
new file mode 100644
index 000000000000000..b8b78fceebbd5de
--- /dev/null
+++ b/libcxx/test/libcxx/containers/views/mdspan/layout_right/assert.ctor.layout_stride.pass.cpp
@@ -0,0 +1,83 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: libcpp-hardening-mode=unchecked
+// XFAIL: availability-verbose_abort-missing
+// ADDITIONAL_COMPILE_FLAGS: -Wno-ctad-maybe-unsupported
+
+// FIXME: https://github.com/llvm/llvm-project/issues/64719
+// There appear to be some issues around ctad which make it
+// currently impossible to get this code warning free.
+// Thus added the additional compile flag above
+
+// <mdspan>
+
+// template<class OtherExtents>
+// constexpr explicit(extents_type::rank() > 0)
+// mapping(const layout_stride::mapping<OtherExtents>& other);
+//
+// Constraints: is_constructible_v<extents_type, OtherExtents> is true.
+//
+// Preconditions:
+// - If extents_type::rank() > 0 is true, then for all r in the range [0, extents_type::rank()),
+// other.stride(r) equals other.extents().fwd-prod-of-extents(r), and
+// - other.required_span_size() is representable as a value of type index_type ([basic.fundamental]).
+//
+// Effects: Direct-non-list-initializes extents_ with other.extents().
+
+#include <mdspan>
+#include <cassert>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+ constexpr size_t D = std::dynamic_extent;
+
+ // working case
+ {
+ std::layout_stride::mapping arg(std::extents<int, D>(5), std::array<int, 1>{1});
+ [[maybe_unused]] std::layout_right::mapping<std::extents<size_t, 5>> m(arg); // should work
+ }
+ // mismatch of static extent
+ {
+ std::layout_stride::mapping arg(std::extents<int, D>(5), std::array<int, 1>{1});
+ TEST_LIBCPP_ASSERT_FAILURE(([=] { std::layout_right::mapping<std::extents<int, 3>> m(arg); }()),
+ "extents construction: mismatch of provided arguments with static extents.");
+ }
+ // non-representability of extents itself
+ {
+ std::layout_stride::mapping arg(std::extents<int, D>(500), std::array<int, 1>{1});
+ TEST_LIBCPP_ASSERT_FAILURE(([=] { std::layout_right::mapping<std::extents<char, D>> m(arg); }()),
+ "extents ctor: arguments must be representable as index_type and nonnegative");
+ }
+ // non-representability of required span size
+ {
+ std::layout_stride::mapping arg(std::extents<int, D, D>(100, 3), std::array<int, 2>{3, 1});
+ TEST_LIBCPP_ASSERT_FAILURE(
+ ([=] { std::layout_right::mapping<std::extents<char, D, D>> m(arg); }()),
+ "layout_right::mapping from layout_stride ctor: other.required_span_size() must be "
+ "representable as index_type.");
+ }
+ // strides are not layout_right compatible
+ {
+ std::layout_stride::mapping arg(std::extents<int, D>(5), std::array<int, 1>{2});
+ TEST_LIBCPP_ASSERT_FAILURE(
+ ([=] { std::layout_right::mapping<std::extents<size_t, 5>> m(arg); }()),
+ "layout_right::mapping from layout_stride ctor: strides are not compatible with layout_right.");
+ }
+ {
+ std::layout_stride::mapping arg(std::extents<int, D, D>(100, 3), std::array<int, 2>{6, 2});
+ TEST_LIBCPP_ASSERT_FAILURE(
+ ([=] { std::layout_right::mapping<std::extents<int, D, D>> m(arg); }()),
+ "layout_right::mapping from layout_stride ctor: strides are not compatible with layout_right.");
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.conversion.pass.cpp b/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.conversion.pass.cpp
new file mode 100644
index 000000000000000..e8bad8b0d512495
--- /dev/null
+++ b/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.conversion.pass.cpp
@@ -0,0 +1,112 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: libcpp-hardening-mode=unchecked
+// XFAIL: availability-verbose_abort-missing
+
+// <mdspan>
+
+// template<class StridedLayoutMapping>
+// constexpr explicit(see below)
+// mapping(const StridedLayoutMapping& other) noexcept;
+//
+// Constraints:
+// - layout-mapping-alike<StridedLayoutMapping> is satisfied.
+// - is_constructible_v<extents_type, typename StridedLayoutMapping::extents_type> is true.
+// - StridedLayoutMapping::is_always_unique() is true.
+// - StridedLayoutMapping::is_always_strided() is true.
+//
+// Preconditions:
+// - StridedLayoutMapping meets the layout mapping requirements ([mdspan.layout.policy.reqmts]),
+// - other.stride(r) > 0 is true for every rank index r of extents(),
+// - other.required_span_size() is representable as a value of type index_type ([basic.fundamental]), and
+// - OFFSET(other) == 0 is true.
+//
+// Effects: Direct-non-list-initializes extents_ with other.extents(), and for all d in the range [0, rank_),
+// direct-non-list-initializes strides_[d] with other.stride(d).
+//
+// Remarks: The expression inside explicit is equivalent to:
+// - !(is_convertible_v<typename StridedLayoutMapping::extents_type, extents_type> &&
+// (is-mapping-of<layout_left, LayoutStrideMapping> ||
+// is-mapping-of<layout_right, LayoutStrideMapping> ||
+// is-mapping-of<layout_stride, LayoutStrideMapping>))
+
+#include <mdspan>
+#include <cassert>
+
+#include "check_assertion.h"
+#include "../../../../../std/containers/views/mdspan/CustomTestLayouts.h"
+
+int main(int, char**) {
+ constexpr size_t D = std::dynamic_extent;
+
+ // working case
+ {
+ std::extents<int, D, D> arg_exts{100, 5};
+ std::layout_stride::mapping<std::extents<int, D, D>> arg(arg_exts, std::array<int, 2>{1, 100});
+ [[maybe_unused]] std::layout_stride::mapping<std::extents<size_t, D, 5>> m(arg); // should work
+ }
+ // mismatch of static extent
+ {
+ std::extents<int, D, D> arg_exts{100, 5};
+ std::layout_stride::mapping<std::extents<int, D, D>> arg(arg_exts, std::array<int, 2>{1, 100});
+ TEST_LIBCPP_ASSERT_FAILURE(([=] { std::layout_stride::mapping<std::extents<int, D, 3>> m(arg); }()),
+ "extents construction: mismatch of provided arguments with static extents.");
+ }
+ // non-representability of extents itself
+ {
+ TEST_LIBCPP_ASSERT_FAILURE(
+ ([=] {
+ std::layout_stride::mapping<std::extents<char, D>> m(
+ std::layout_stride::mapping<std::extents<int, D>>(std::extents<int, D>(500), std::array<int, 1>{1}));
+ }()),
+ "extents ctor: arguments must be representable as index_type and nonnegative");
+ }
+ // all strides must be larger than zero
+ {
+ always_convertible_layout::mapping<std::dextents<int, 2>> offset_map(std::dextents<int, 2>{10, 10}, 100, -1);
+ TEST_LIBCPP_ASSERT_FAILURE(
+ ([=] { std::layout_stride::template mapping<std::extents<char, D, D>> m(offset_map); }()),
+ "layout_stride::mapping converting ctor: all strides must be greater than 0");
+ }
+ // required_span_size not representable, while individual extents are
+ {
+ std::extents<int, D, D> arg_exts{100, 5};
+ std::layout_stride::mapping<std::extents<int, D, D>> arg(arg_exts, std::array<int, 2>{1, 100});
+ // check extents would be constructible
+ [[maybe_unused]] std::extents<char, D, 5> e(arg_exts);
+ // but the product is not, so we can't use it for layout_stride
+ TEST_LIBCPP_ASSERT_FAILURE(
+ ([=] { std::layout_stride::template mapping<std::extents<char, D, 5>> m(arg); }()),
+ "layout_stride::mapping converting ctor: other.required_span_size() must be representable as index_type.");
+ }
+ // required_span_size not representable, while individual extents are, edge case
+ {
+ // required span size = (3-1)*50 + (10-1) * 3 + 1 = 128
+ std::extents<int, D, D> arg_exts{3, 10};
+ std::layout_stride::mapping<std::extents<int, D, D>> arg(arg_exts, std::array<int, 2>{50, 3});
+ // sanity check:
+ assert(arg.required_span_size() == 128);
+ // check extents would be constructible
+ [[maybe_unused]] std::extents<signed char, D, 10> e(arg_exts);
+ // but the product is not, so we can't use it for layout_stride
+ TEST_LIBCPP_ASSERT_FAILURE(
+ ([=] { std::layout_stride::template mapping<std::extents<signed char, D, 10>> m(arg); }()),
+ "layout_stride::mapping converting ctor: other.required_span_size() must be representable as index_type.");
+ }
+ // base offset must be 0 (i.e. mapping(0,...,0)==0) for a strided layout with positiv strides
+ {
+ always_convertible_layout::mapping<std::dextents<int, 2>> offset_map(std::dextents<int, 2>{10, 10}, 3);
+ TEST_LIBCPP_ASSERT_FAILURE(
+ ([=] { std::layout_stride::template mapping<std::extents<char, D, D>> m(offset_map); }()),
+ "layout_stride::mapping converting ctor: base offset of mapping must be zero.");
+ }
+ return 0;
+}
diff --git a/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_array.non_unique.pass.cpp b/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_array.non_unique.pass.cpp
new file mode 100644
index 000000000000000..ba1c81d35d8df49
--- /dev/null
+++ b/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_array.non_unique.pass.cpp
@@ -0,0 +1,67 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: !libcpp-hardening-mode=debug
+// XFAIL: availability-verbose_abort-missing
+
+// <mdspan>
+
+// template<class OtherIndexType>
+// constexpr mapping(const extents_type& e, const array<OtherIndexType, rank_>& s) noexcept;
+//
+// Constraints:
+// - is_convertible_v<const OtherIndexType&, index_type> is true, and
+// - is_nothrow_constructible_v<index_type, const OtherIndexType&> is true.
+//
+// Preconditions:
+// - s[i] > 0 is true for all i in the range [0, rank_).
+// - REQUIRED-SPAN-SIZE(e, s) is representable as a value of type index_type ([basic.fundamental]).
+// - If rank_ is greater than 0, then there exists a permutation P of the integers in the range [0, rank_),
+// such that s[pi] >= s[pi_1] * e.extent(pi_1) is true for all i in the range [1, rank_), where
+// pi is the ith element of P.
+// [Note 1: For layout_stride, this condition is necessary and sufficient for is_unique() to be true. end note]
+//
+// Effects: Direct-non-list-initializes extents_ with e, and for all d in the range [0, rank_), direct-non-list-initializes strides_[d] with as_const(s[d]).
+
+#include <mdspan>
+#include <cassert>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+ constexpr size_t D = std::dynamic_extent;
+
+ // overlapping strides
+ {
+ TEST_LIBCPP_ASSERT_FAILURE(
+ ([=] {
+ std::layout_stride::template mapping<std::extents<unsigned, D, 5, 7>> m(
+ std::extents<unsigned, D, 5, 7>(20), std::array<unsigned, 3>{4, 1, 200});
+ }()),
+ "layout_stride::mapping ctor: the provided extents and strides lead to a non-unique mapping");
+ }
+ // equal strides
+ {
+ // should work because one of the equal strides is associated with an extent of 1
+ [[maybe_unused]] std::layout_stride::template mapping<std::extents<unsigned, D, 5, 1>> m1(
+ std::extents<unsigned, D, 5, 1>(2), std::array<unsigned, 3>{5, 1, 5});
+ [[maybe_unused]] std::layout_stride::template mapping<std::extents<unsigned, D, 5, 2>> m2(
+ std::extents<unsigned, D, 5, 2>(1), std::array<unsigned, 3>{5, 1, 5});
+
+ // will fail because neither of the equal strides is associated with an extent of 1
+ TEST_LIBCPP_ASSERT_FAILURE(
+ ([=] {
+ std::layout_stride::template mapping<std::extents<unsigned, D, 5, 2>> m3(
+ std::extents<unsigned, D, 5, 2>(2), std::array<unsigned, 3>{5, 1, 5});
+ }()),
+ "layout_stride::mapping ctor: the provided extents and strides lead to a non-unique mapping");
+ }
+ return 0;
+}
diff --git a/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_array.pass.cpp b/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_array.pass.cpp
new file mode 100644
index 000000000000000..6eea40c239f09d4
--- /dev/null
+++ b/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_array.pass.cpp
@@ -0,0 +1,73 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: libcpp-hardening-mode=unchecked
+// XFAIL: availability-verbose_abort-missing
+
+// <mdspan>
+
+// template<class OtherIndexType>
+// constexpr mapping(const extents_type& e, const array<OtherIndexType, rank_>& s) noexcept;
+//
+// Constraints:
+// - is_convertible_v<const OtherIndexType&, index_type> is true, and
+// - is_nothrow_constructible_v<index_type, const OtherIndexType&> is true.
+//
+// Preconditions:
+// - s[i] > 0 is true for all i in the range [0, rank_).
+// - REQUIRED-SPAN-SIZE(e, s) is representable as a value of type index_type ([basic.fundamental]).
+// - If rank_ is greater than 0, then there exists a permutation P of the integers in the range [0, rank_),
+// such that s[pi] >= s[pi_1] * e.extent(pi_1) is true for all i in the range [1, rank_), where
+// pi is the ith element of P.
+// [Note 1: For layout_stride, this condition is necessary and sufficient for is_unique() to be true. end note]
+//
+// Effects: Direct-non-list-initializes extents_ with e, and for all d in the range [0, rank_), direct-non-list-initializes strides_[d] with as_const(s[d]).
+
+#include <mdspan>
+#include <cassert>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+ constexpr size_t D = std::dynamic_extent;
+
+ // the extents are representable but the product with strides is not, so we can't use it for layout_stride
+ TEST_LIBCPP_ASSERT_FAILURE(
+ ([=] {
+ std::layout_stride::template mapping<std::extents<char, D, 5>> m(
+ std::extents<char, D, 5>(20), std::array<int, 2>{20, 1});
+ }()),
+ "layout_stride::mapping ctor: required span size is not representable as index_type.");
+
+ // check that if we first overflow in strides conversion we also fail
+ static_assert(static_cast<unsigned char>(257u) == 1);
+ TEST_LIBCPP_ASSERT_FAILURE(
+ ([=] {
+ std::layout_stride::template mapping<std::extents<unsigned char, D, 5>> m(
+ std::extents<unsigned char, D, 5>(20), std::array<unsigned, 2>{257, 1});
+ }()),
+ "layout_stride::mapping ctor: required span size is not representable as index_type.");
+
+ // negative strides are not allowed, check with unsigned index_type so we make sure we catch that
+ TEST_LIBCPP_ASSERT_FAILURE(
+ ([=] {
+ std::layout_stride::template mapping<std::extents<unsigned, D, 5>> m(
+ std::extents<unsigned, D, 5>(20), std::array<int, 2>{20, -1});
+ }()),
+ "layout_stride::mapping ctor: all strides must be greater than 0");
+ // zero strides are not allowed, check with unsigned index_type so we make sure we catch that
+ TEST_LIBCPP_ASSERT_FAILURE(
+ ([=] {
+ std::layout_stride::template mapping<std::extents<unsigned, D, 5>> m(
+ std::extents<unsigned, D, 5>(20), std::array<unsigned, 2>{20, 0});
+ }()),
+ "layout_stride::mapping ctor: all strides must be greater than 0");
+ return 0;
+}
diff --git a/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_span.non_unique.pass.cpp b/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_span.non_unique.pass.cpp
new file mode 100644
index 000000000000000..b20f65762407477
--- /dev/null
+++ b/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_span.non_unique.pass.cpp
@@ -0,0 +1,70 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: !libcpp-hardening-mode=debug
+// XFAIL: availability-verbose_abort-missing
+
+// <mdspan>
+
+// template<class OtherIndexType>
+// constexpr mapping(const extents_type& e, span<OtherIndexType, rank_> s) noexcept;
+//
+// Constraints:
+// - is_convertible_v<const OtherIndexType&, index_type> is true, and
+// - is_nothrow_constructible_v<index_type, const OtherIndexType&> is true.
+//
+// Preconditions:
+// - s[i] > 0 is true for all i in the range [0, rank_).
+// - REQUIRED-SPAN-SIZE(e, s) is representable as a value of type index_type ([basic.fundamental]).
+// - If rank_ is greater than 0, then there exists a permutation P of the integers in the range [0, rank_),
+// such that s[pi] >= s[pi_1] * e.extent(pi_1) is true for all i in the range [1, rank_), where
+// pi is the ith element of P.
+// [Note 1: For layout_stride, this condition is necessary and sufficient for is_unique() to be true. end note]
+//
+// Effects: Direct-non-list-initializes extents_ with e, and for all d in the range [0, rank_), direct-non-list-initializes strides_[d] with as_const(s[d]).
+
+#include <mdspan>
+#include <cassert>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+ constexpr size_t D = std::dynamic_extent;
+
+ // overlapping strides
+ {
+ TEST_LIBCPP_ASSERT_FAILURE(
+ ([=] {
+ std::array<unsigned, 3> strides{4, 1, 200};
+ std::layout_stride::template mapping<std::extents<unsigned, D, 5, 7>> m(
+ std::extents<unsigned, D, 5, 7>(20), std::span(strides));
+ }()),
+ "layout_stride::mapping ctor: the provided extents and strides lead to a non-unique mapping");
+ }
+
+ // equal strides
+ {
+ // should work because one of the equal strides is associated with an extent of 1
+ std::array<unsigned, 3> strides{5, 1, 5};
+ [[maybe_unused]] std::layout_stride::template mapping<std::extents<unsigned, D, 5, 1>> m1(
+ std::extents<unsigned, D, 5, 1>(2), std::span(strides));
+ [[maybe_unused]] std::layout_stride::template mapping<std::extents<unsigned, D, 5, 2>> m2(
+ std::extents<unsigned, D, 5, 2>(1), std::span(strides));
+
+ // will fail because neither of the equal strides is associated with an extent of 1
+ TEST_LIBCPP_ASSERT_FAILURE(
+ ([=] {
+ std::layout_stride::template mapping<std::extents<unsigned, D, 5, 2>> m3(
+ std::extents<unsigned, D, 5, 2>(2), std::span(strides));
+ }()),
+ "layout_stride::mapping ctor: the provided extents and strides lead to a non-unique mapping");
+ }
+ return 0;
+}
diff --git a/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_span.pass.cpp b/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_span.pass.cpp
new file mode 100644
index 000000000000000..cd06bbb545dd52d
--- /dev/null
+++ b/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.ctor.extents_span.pass.cpp
@@ -0,0 +1,80 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: libcpp-hardening-mode=unchecked
+// XFAIL: availability-verbose_abort-missing
+
+// <mdspan>
+
+// template<class OtherIndexType>
+// constexpr mapping(const extents_type& e, span<OtherIndexType, rank_> s) noexcept;
+//
+// Constraints:
+// - is_convertible_v<const OtherIndexType&, index_type> is true, and
+// - is_nothrow_constructible_v<index_type, const OtherIndexType&> is true.
+//
+// Preconditions:
+// - s[i] > 0 is true for all i in the range [0, rank_).
+// - REQUIRED-SPAN-SIZE(e, s) is representable as a value of type index_type ([basic.fundamental]).
+// - If rank_ is greater than 0, then there exists a permutation P of the integers in the range [0, rank_),
+// such that s[pi] >= s[pi_1] * e.extent(pi_1) is true for all i in the range [1, rank_), where
+// pi is the ith element of P.
+// [Note 1: For layout_stride, this condition is necessary and sufficient for is_unique() to be true. end note]
+//
+// Effects: Direct-non-list-initializes extents_ with e, and for all d in the range [0, rank_), direct-non-list-initializes strides_[d] with as_const(s[d]).
+
+#include <mdspan>
+#include <cassert>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+ constexpr size_t D = std::dynamic_extent;
+
+ // value out of range
+ {
+ // the extents are representable but the product with strides is not, so we can't use it for layout_stride
+ TEST_LIBCPP_ASSERT_FAILURE(
+ ([=] {
+ std::array<int, 2> strides{20, 1};
+ std::layout_stride::template mapping<std::extents<char, D, 5>> m(
+ std::extents<char, D, 5>(20), std::span(strides));
+ }()),
+ "layout_stride::mapping ctor: required span size is not representable as index_type.");
+
+ // check that if we first overflow in strides conversion we also fail
+ static_assert(static_cast<unsigned char>(257u) == 1);
+ TEST_LIBCPP_ASSERT_FAILURE(
+ ([=] {
+ std::array<unsigned, 2> strides{257, 1};
+ std::layout_stride::template mapping<std::extents<unsigned char, D, 5>> m(
+ std::extents<unsigned char, D, 5>(20), std::span(strides));
+ }()),
+ "layout_stride::mapping ctor: required span size is not representable as index_type.");
+
+ // negative strides are not allowed, check with unsigned index_type so we make sure we catch that
+ TEST_LIBCPP_ASSERT_FAILURE(
+ ([=] {
+ std::array<int, 2> strides{20, -1};
+ std::layout_stride::template mapping<std::extents<unsigned, D, 5>> m(
+ std::extents<unsigned, D, 5>(20), std::span(strides));
+ }()),
+ "layout_stride::mapping ctor: all strides must be greater than 0");
+ // zero strides are not allowed, check with unsigned index_type so we make sure we catch that
+ TEST_LIBCPP_ASSERT_FAILURE(
+ ([=] {
+ std::array<unsigned, 2> strides{20, 0};
+ std::layout_stride::template mapping<std::extents<unsigned, D, 5>> m(
+ std::extents<unsigned, D, 5>(20), std::span(strides));
+ }()),
+ "layout_stride::mapping ctor: all strides must be greater than 0");
+ }
+ return 0;
+}
diff --git a/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.index_operator.pass.cpp b/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.index_operator.pass.cpp
new file mode 100644
index 000000000000000..72e44bd97984c51
--- /dev/null
+++ b/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.index_operator.pass.cpp
@@ -0,0 +1,88 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: !libcpp-hardening-mode=debug
+// XFAIL: availability-verbose_abort-missing
+
+// <mdspan>
+
+// template<class... Indices>
+// constexpr index_type operator()(Indices... i) const noexcept;
+//
+// Constraints:
+// - sizeof...(Indices) == extents_type::rank() is true,
+// - (is_convertible_v<Indices, index_type> && ...) is true, and
+// - (is_nothrow_constructible_v<index_type, Indices> && ...) is true.
+//
+// Preconditions: extents_type::index-cast(i) is a multidimensional index in extents_ ([mdspan.overview]).
+
+#include <mdspan>
+#include <cassert>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+ // value out of range
+ {
+ std::layout_stride::template mapping<std::extents<unsigned char, 5>> m;
+ TEST_LIBCPP_ASSERT_FAILURE(m(-1), "layout_stride::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(-130), "layout_stride::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(5), "layout_stride::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(1000), "layout_stride::mapping: out of bounds indexing");
+ }
+ {
+ std::layout_stride::template mapping<std::extents<signed char, 5>> m;
+ TEST_LIBCPP_ASSERT_FAILURE(m(-1), "layout_stride::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(-130), "layout_stride::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(5), "layout_stride::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(1000), "layout_stride::mapping: out of bounds indexing");
+ }
+ {
+ std::layout_stride::template mapping<std::dextents<unsigned char, 1>> m(
+ std::dextents<unsigned char, 1>(5), std::array<int, 1>{1});
+ TEST_LIBCPP_ASSERT_FAILURE(m(-1), "layout_stride::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(-130), "layout_stride::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(5), "layout_stride::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(1000), "layout_stride::mapping: out of bounds indexing");
+ }
+ {
+ std::layout_stride::template mapping<std::dextents<signed char, 1>> m(
+ std::dextents<signed char, 1>(5), std::array<int, 1>{1});
+ TEST_LIBCPP_ASSERT_FAILURE(m(-1), "layout_stride::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(-130), "layout_stride::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(5), "layout_stride::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(1000), "layout_stride::mapping: out of bounds indexing");
+ }
+ {
+ std::layout_stride::template mapping<std::dextents<int, 3>> m(
+ std::dextents<int, 3>(5, 7, 9), std::array<int, 3>{1, 10, 100});
+ TEST_LIBCPP_ASSERT_FAILURE(m(-1, -1, -1), "layout_stride::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(-1, 0, 0), "layout_stride::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(0, -1, 0), "layout_stride::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(0, 0, -1), "layout_stride::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(5, 3, 3), "layout_stride::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(3, 7, 3), "layout_stride::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(3, 3, 9), "layout_stride::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(5, 7, 9), "layout_stride::mapping: out of bounds indexing");
+ }
+ {
+ std::layout_stride::template mapping<std::dextents<unsigned, 3>> m(
+ std::dextents<int, 3>(5, 7, 9), std::array<int, 3>{1, 10, 100});
+ TEST_LIBCPP_ASSERT_FAILURE(m(-1, -1, -1), "layout_stride::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(-1, 0, 0), "layout_stride::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(0, -1, 0), "layout_stride::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(0, 0, -1), "layout_stride::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(5, 3, 3), "layout_stride::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(3, 7, 3), "layout_stride::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(3, 3, 9), "layout_stride::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(5, 7, 9), "layout_stride::mapping: out of bounds indexing");
+ }
+ return 0;
+}
diff --git a/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.stride.pass.cpp b/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.stride.pass.cpp
new file mode 100644
index 000000000000000..8a42861a0be0799
--- /dev/null
+++ b/libcxx/test/libcxx/containers/views/mdspan/layout_stride/assert.stride.pass.cpp
@@ -0,0 +1,36 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// UNSUPPORTED: libcpp-hardening-mode=unchecked
+// XFAIL: availability-verbose_abort-missing
+
+// <mdspan>
+
+// constexpr index_type stride(rank_type i) const noexcept { return strides_[i]; }
+
+// We intercept this inside layout_stride to give a consistent error message with
+// layout_left and layout_right, technically the precondition is coming from the
+// array access.
+
+#include <mdspan>
+#include <cassert>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+ // value out of range
+ {
+ std::layout_stride::template mapping<std::dextents<int, 3>> m(
+ std::dextents<int, 3>(100, 100, 100), std::array<int, 3>{1, 100, 10000});
+
+ TEST_LIBCPP_ASSERT_FAILURE(m.stride(4), "invalid rank index");
+ }
+ return 0;
+}
diff --git a/libcxx/test/libcxx/containers/views/mdspan/mdspan/assert.conversion.pass.cpp b/libcxx/test/libcxx/containers/views/mdspan/mdspan/assert.conversion.pass.cpp
index cb0b2ed15fc1b48..8caaf2295c0a1b3 100644
--- a/libcxx/test/libcxx/containers/views/mdspan/mdspan/assert.conversion.pass.cpp
+++ b/libcxx/test/libcxx/containers/views/mdspan/mdspan/assert.conversion.pass.cpp
@@ -42,7 +42,7 @@
#include <mdspan>
#include "check_assertion.h"
-#include "../../../../../std/containers/views/mdspan/mdspan/CustomTestLayouts.h"
+#include "../../../../../std/containers/views/mdspan/CustomTestLayouts.h"
// We use a funky mapping in this test that doesn't check the dynamic/static extents mismatch itself
int main(int, char**) {
diff --git a/libcxx/test/libcxx/containers/views/mdspan/mdspan/assert.size.pass.cpp b/libcxx/test/libcxx/containers/views/mdspan/mdspan/assert.size.pass.cpp
index a10d89bc86d4b69..9b5b872bb3596f2 100644
--- a/libcxx/test/libcxx/containers/views/mdspan/mdspan/assert.size.pass.cpp
+++ b/libcxx/test/libcxx/containers/views/mdspan/mdspan/assert.size.pass.cpp
@@ -22,7 +22,7 @@
#include <mdspan>
#include "check_assertion.h"
-#include "../../../../../std/containers/views/mdspan/mdspan/CustomTestLayouts.h"
+#include "../../../../../std/containers/views/mdspan/CustomTestLayouts.h"
// We use a funky mapping in this test where required_span_size is much smaller than the size of the index space
int main(int, char**) {
diff --git a/libcxx/test/libcxx/transitive_includes/cxx03.csv b/libcxx/test/libcxx/transitive_includes/cxx03.csv
index f6aeb837a7292f6..153335c3519c473 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx03.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx03.csv
@@ -526,6 +526,7 @@ mdspan concepts
mdspan cstddef
mdspan limits
mdspan span
+mdspan version
memory atomic
memory compare
memory concepts
diff --git a/libcxx/test/libcxx/transitive_includes/cxx11.csv b/libcxx/test/libcxx/transitive_includes/cxx11.csv
index 08fd94393f47320..f3f8956df64ee78 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx11.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx11.csv
@@ -530,6 +530,7 @@ mdspan concepts
mdspan cstddef
mdspan limits
mdspan span
+mdspan version
memory atomic
memory compare
memory concepts
diff --git a/libcxx/test/libcxx/transitive_includes/cxx14.csv b/libcxx/test/libcxx/transitive_includes/cxx14.csv
index 33384a628e65c01..fb90523d5552ac3 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx14.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx14.csv
@@ -532,6 +532,7 @@ mdspan concepts
mdspan cstddef
mdspan limits
mdspan span
+mdspan version
memory atomic
memory compare
memory concepts
diff --git a/libcxx/test/libcxx/transitive_includes/cxx17.csv b/libcxx/test/libcxx/transitive_includes/cxx17.csv
index 33384a628e65c01..fb90523d5552ac3 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx17.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx17.csv
@@ -532,6 +532,7 @@ mdspan concepts
mdspan cstddef
mdspan limits
mdspan span
+mdspan version
memory atomic
memory compare
memory concepts
diff --git a/libcxx/test/libcxx/transitive_includes/cxx20.csv b/libcxx/test/libcxx/transitive_includes/cxx20.csv
index 8f6e8dd646df583..63ea6c09b1f5083 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx20.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx20.csv
@@ -537,6 +537,7 @@ mdspan concepts
mdspan cstddef
mdspan limits
mdspan span
+mdspan version
memory atomic
memory compare
memory concepts
diff --git a/libcxx/test/libcxx/transitive_includes/cxx23.csv b/libcxx/test/libcxx/transitive_includes/cxx23.csv
index d0d858056b1b00d..236dedd186c9268 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx23.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx23.csv
@@ -386,6 +386,7 @@ mdspan concepts
mdspan cstddef
mdspan limits
mdspan span
+mdspan version
memory compare
memory cstddef
memory cstdint
diff --git a/libcxx/test/libcxx/transitive_includes/cxx26.csv b/libcxx/test/libcxx/transitive_includes/cxx26.csv
index d0d858056b1b00d..236dedd186c9268 100644
--- a/libcxx/test/libcxx/transitive_includes/cxx26.csv
+++ b/libcxx/test/libcxx/transitive_includes/cxx26.csv
@@ -386,6 +386,7 @@ mdspan concepts
mdspan cstddef
mdspan limits
mdspan span
+mdspan version
memory compare
memory cstddef
memory cstdint
diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/CustomTestLayouts.h b/libcxx/test/std/containers/views/mdspan/CustomTestLayouts.h
similarity index 87%
rename from libcxx/test/std/containers/views/mdspan/mdspan/CustomTestLayouts.h
rename to libcxx/test/std/containers/views/mdspan/CustomTestLayouts.h
index e1d6e80afbd84ca..302eb2f5edd0193 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/CustomTestLayouts.h
+++ b/libcxx/test/std/containers/views/mdspan/CustomTestLayouts.h
@@ -14,8 +14,8 @@
//
//===---------------------------------------------------------------------===//
-#ifndef TEST_STD_CONTAINERS_VIEWS_MDSPAN_MDSPAN_CUSTOM_TEST_LAYOUTS_H
-#define TEST_STD_CONTAINERS_VIEWS_MDSPAN_MDSPAN_CUSTOM_TEST_LAYOUTS_H
+#ifndef TEST_STD_CONTAINERS_VIEWS_MDSPAN_CUSTOM_TEST_LAYOUTS_H
+#define TEST_STD_CONTAINERS_VIEWS_MDSPAN_CUSTOM_TEST_LAYOUTS_H
#include <algorithm>
#include <array>
@@ -205,9 +205,9 @@ constexpr auto construct_mapping(layout_wrapping_integral<Wraps>, Extents exts)
return typename layout_wrapping_integral<Wraps>::template mapping<Extents>(exts, not_extents_constructible_tag{});
}
-
// This layout does not check convertibility of extents for its conversion ctor
// Allows triggering mdspan's ctor static assertion on convertibility of extents
+// It also allows for negative strides and offsets via runtime arguments
class always_convertible_layout {
public:
template <class Extents>
@@ -239,8 +239,10 @@ class always_convertible_layout::mapping {
public:
constexpr mapping() noexcept = delete;
- constexpr mapping(const mapping& other) noexcept : extents_(other.extents()){};
- constexpr mapping(const extents_type& ext) noexcept : extents_(ext){};
+ constexpr mapping(const mapping& other) noexcept
+ : extents_(other.extents_), offset_(other.offset_), scaling_(other.scaling_){};
+ constexpr mapping(const extents_type& ext, index_type offset = 0, index_type scaling = 1) noexcept
+ : extents_(ext), offset_(offset), scaling_(scaling){};
template <class OtherExtents>
constexpr mapping(const mapping<OtherExtents>& other) noexcept {
@@ -256,10 +258,14 @@ class always_convertible_layout::mapping {
} else {
extents_ = extents_type();
}
+ offset_ = other.offset_;
+ scaling_ = other.scaling_;
}
constexpr mapping& operator=(const mapping& other) noexcept {
extents_ = other.extents_;
+ offset_ = other.offset_;
+ scaling_ = other.scaling_;
return *this;
};
@@ -269,7 +275,7 @@ class always_convertible_layout::mapping {
index_type size = 1;
for (size_t r = 0; r < extents_type::rank(); r++)
size *= extents_.extent(r);
- return size;
+ return std::max(size * scaling_ + offset_, offset_);
}
template <std::integral... Indices>
@@ -277,16 +283,18 @@ class always_convertible_layout::mapping {
(std::is_nothrow_constructible_v<index_type, Indices> && ...))
constexpr index_type operator()(Indices... idx) const noexcept {
std::array<index_type, extents_type::rank()> idx_a{static_cast<index_type>(static_cast<index_type>(idx))...};
- return [&]<size_t... Pos>(std::index_sequence<Pos...>) {
- index_type res = 0;
- ((res = idx_a[extents_type::rank() - 1 - Pos] + extents_.extent(extents_type::rank() - 1 - Pos) * res), ...);
- return res;
- }(std::make_index_sequence<sizeof...(Indices)>());
+ return offset_ +
+ scaling_ * ([&]<size_t... Pos>(std::index_sequence<Pos...>) {
+ index_type res = 0;
+ ((res = idx_a[extents_type::rank() - 1 - Pos] + extents_.extent(extents_type::rank() - 1 - Pos) * res),
+ ...);
+ return res;
+ }(std::make_index_sequence<sizeof...(Indices)>()));
}
- static constexpr bool is_always_unique() noexcept { return false; }
+ static constexpr bool is_always_unique() noexcept { return true; }
static constexpr bool is_always_exhaustive() noexcept { return true; }
- static constexpr bool is_always_strided() noexcept { return false; }
+ static constexpr bool is_always_strided() noexcept { return true; }
static constexpr bool is_unique() noexcept { return true; }
static constexpr bool is_exhaustive() noexcept { return true; }
@@ -296,15 +304,15 @@ class always_convertible_layout::mapping {
requires(extents_type::rank() > 0)
{
index_type s = 1;
- for (rank_type i = extents_type::rank() - 1; i > r; i--)
+ for (rank_type i = 0; i < r; i++)
s *= extents_.extent(i);
- return s;
+ return s * scaling_;
}
template <class OtherExtents>
requires(OtherExtents::rank() == extents_type::rank())
friend constexpr bool operator==(const mapping& lhs, const mapping<OtherExtents>& rhs) noexcept {
- return lhs.extents() == rhs.extents();
+ return lhs.extents() == rhs.extents() && lhs.offset_ == rhs.offset && lhs.scaling_ == rhs.scaling_;
}
friend constexpr void swap(mapping& x, mapping& y) noexcept {
@@ -320,6 +328,11 @@ class always_convertible_layout::mapping {
}
private:
+ template <class>
+ friend class mapping;
+
extents_type extents_{};
+ index_type offset_{};
+ index_type scaling_{};
};
-#endif // TEST_STD_CONTAINERS_VIEWS_MDSPAN_MDSPAN_CUSTOM_TEST_LAYOUTS_H
+#endif // TEST_STD_CONTAINERS_VIEWS_MDSPAN_CUSTOM_TEST_LAYOUTS_H
diff --git a/libcxx/test/std/containers/views/mdspan/layout_left/ctor.layout_stride.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_left/ctor.layout_stride.pass.cpp
new file mode 100644
index 000000000000000..e85270ad023db6e
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_left/ctor.layout_stride.pass.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <mdspan>
+
+// template<class OtherExtents>
+// constexpr explicit(extents_type::rank() > 0)
+// mapping(const layout_stride::mapping<OtherExtents>& other);
+//
+// Constraints: is_constructible_v<extents_type, OtherExtents> is true.
+//
+// Preconditions:
+// - If extents_type::rank() > 0 is true, then for all r in the range [0, extents_type::rank()),
+// other.stride(r) equals other.extents().fwd-prod-of-extents(r), and
+// - other.required_span_size() is representable as a value of type index_type ([basic.fundamental]).
+//
+// Effects: Direct-non-list-initializes extents_ with other.extents().
+
+#include <mdspan>
+#include <type_traits>
+#include <cassert>
+#include <limits>
+
+#include "test_macros.h"
+
+template <bool implicit, class ToE, class FromE>
+constexpr void test_conversion(FromE src_exts) {
+ using To = std::layout_left::mapping<ToE>;
+ using From = std::layout_stride::mapping<FromE>;
+ std::array<typename FromE::index_type, FromE::rank()> strides;
+ if constexpr (FromE::rank() > 0) {
+ strides[0] = 1;
+ for (size_t r = 1; r < FromE::rank(); r++)
+ strides[r] = src_exts.extent(r - 1) * strides[r - 1];
+ }
+ From src(src_exts, strides);
+
+ ASSERT_NOEXCEPT(To(src));
+ To dest(src);
+ assert(dest == src);
+
+ if constexpr (implicit) {
+ To dest_implicit = src;
+ assert(dest_implicit == src);
+ } else {
+ assert((!std::is_convertible_v<From, To>));
+ }
+}
+
+template <class T1, class T2>
+constexpr void test_conversion() {
+ constexpr size_t D = std::dynamic_extent;
+
+ // clang-format off
+ test_conversion<true, std::extents<T1>>(std::extents<T2>());
+ test_conversion<false, std::extents<T1, D>>(std::extents<T2, D>(5));
+ test_conversion<false, std::extents<T1, 5>>(std::extents<T2, D>(5));
+ test_conversion<false, std::extents<T1, 5>>(std::extents<T2, 5>());
+ test_conversion<false, std::extents<T1, 5, D>>(std::extents<T2, D, D>(5, 5));
+ test_conversion<false, std::extents<T1, D, D>>(std::extents<T2, D, D>(5, 5));
+ test_conversion<false, std::extents<T1, D, D>>(std::extents<T2, D, 7>(5));
+ test_conversion<false, std::extents<T1, 5, 7>>(std::extents<T2, 5, 7>());
+ test_conversion<false, std::extents<T1, 5, D, 8, D, D>>(std::extents<T2, D, D, 8, 9, 1>(5, 7));
+ test_conversion<false, std::extents<T1, D, D, D, D, D>>(
+ std::extents<T2, D, D, D, D, D>(5, 7, 8, 9, 1));
+ test_conversion<false, std::extents<T1, D, D, 8, 9, D>>(std::extents<T2, D, 7, 8, 9, 1>(5));
+ test_conversion<false, std::extents<T1, 5, 7, 8, 9, 1>>(std::extents<T2, 5, 7, 8, 9, 1>());
+ // clang-format on
+}
+
+template <class IdxT, size_t... Extents>
+using ll_mapping_t = typename std::layout_left::template mapping<std::extents<IdxT, Extents...>>;
+template <class IdxT, size_t... Extents>
+using ls_mapping_t = typename std::layout_stride::template mapping<std::extents<IdxT, Extents...>>;
+
+constexpr void test_rank_mismatch() {
+ constexpr size_t D = std::dynamic_extent;
+
+ static_assert(!std::is_constructible_v<ll_mapping_t<int, D>, ls_mapping_t<int>>);
+ static_assert(!std::is_constructible_v<ll_mapping_t<int>, ls_mapping_t<int, D, D>>);
+ static_assert(!std::is_constructible_v<ll_mapping_t<int, D>, ls_mapping_t<int, D, D>>);
+ static_assert(!std::is_constructible_v<ll_mapping_t<int, D, D, D>, ls_mapping_t<int, D, D>>);
+}
+
+constexpr void test_static_extent_mismatch() {
+ constexpr size_t D = std::dynamic_extent;
+
+ static_assert(!std::is_constructible_v<ll_mapping_t<int, D, 5>, ls_mapping_t<int, D, 4>>);
+ static_assert(!std::is_constructible_v<ll_mapping_t<int, 5>, ls_mapping_t<int, 4>>);
+ static_assert(!std::is_constructible_v<ll_mapping_t<int, 5, D>, ls_mapping_t<int, 4, D>>);
+}
+
+constexpr bool test() {
+ test_conversion<int, int>();
+ test_conversion<int, size_t>();
+ test_conversion<size_t, int>();
+ test_conversion<size_t, long>();
+ test_rank_mismatch();
+ test_static_extent_mismatch();
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/containers/views/mdspan/layout_right/ctor.layout_stride.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_right/ctor.layout_stride.pass.cpp
new file mode 100644
index 000000000000000..9d9677d54011ce4
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_right/ctor.layout_stride.pass.cpp
@@ -0,0 +1,114 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <mdspan>
+
+// template<class OtherExtents>
+// constexpr explicit(extents_type::rank() > 0)
+// mapping(const layout_stride::mapping<OtherExtents>& other);
+//
+// Constraints: is_constructible_v<extents_type, OtherExtents> is true.
+//
+// Preconditions:
+// - If extents_type::rank() > 0 is true, then for all r in the range [0, extents_type::rank()),
+// other.stride(r) equals other.extents().fwd-prod-of-extents(r), and
+// - other.required_span_size() is representable as a value of type index_type ([basic.fundamental]).
+//
+// Effects: Direct-non-list-initializes extents_ with other.extents().
+
+#include <mdspan>
+#include <type_traits>
+#include <cassert>
+#include <limits>
+
+#include "test_macros.h"
+
+template <bool implicit, class ToE, class FromE>
+constexpr void test_conversion(FromE src_exts) {
+ using To = std::layout_left::mapping<ToE>;
+ using From = std::layout_stride::mapping<FromE>;
+ std::array<typename FromE::index_type, FromE::rank()> strides;
+ if constexpr (FromE::rank() > 0) {
+ strides[0] = 1;
+ for (size_t r = 1; r < FromE::rank(); r++)
+ strides[r] = src_exts.extent(r - 1) * strides[r - 1];
+ }
+ From src(src_exts, strides);
+
+ ASSERT_NOEXCEPT(To(src));
+ To dest(src);
+ assert(dest == src);
+
+ if constexpr (implicit) {
+ To dest_implicit = src;
+ assert(dest_implicit == src);
+ } else {
+ assert((!std::is_convertible_v<From, To>));
+ }
+}
+
+template <class T1, class T2>
+constexpr void test_conversion() {
+ constexpr size_t D = std::dynamic_extent;
+
+ // clang-format off
+ test_conversion<true, std::extents<T1>>(std::extents<T2>());
+ test_conversion<false, std::extents<T1, D>>(std::extents<T2, D>(5));
+ test_conversion<false, std::extents<T1, 5>>(std::extents<T2, D>(5));
+ test_conversion<false, std::extents<T1, 5>>(std::extents<T2, 5>());
+ test_conversion<false, std::extents<T1, 5, D>>(std::extents<T2, D, D>(5, 5));
+ test_conversion<false, std::extents<T1, D, D>>(std::extents<T2, D, D>(5, 5));
+ test_conversion<false, std::extents<T1, D, D>>(std::extents<T2, D, 7>(5));
+ test_conversion<false, std::extents<T1, 5, 7>>(std::extents<T2, 5, 7>());
+ test_conversion<false, std::extents<T1, 5, D, 8, D, D>>(std::extents<T2, D, D, 8, 9, 1>(5, 7));
+ test_conversion<false, std::extents<T1, D, D, D, D, D>>(
+ std::extents<T2, D, D, D, D, D>(5, 7, 8, 9, 1));
+ test_conversion<false, std::extents<T1, D, D, 8, 9, D>>(std::extents<T2, D, 7, 8, 9, 1>(5));
+ test_conversion<false, std::extents<T1, 5, 7, 8, 9, 1>>(std::extents<T2, 5, 7, 8, 9, 1>());
+ // clang-format on
+}
+
+template <class IdxT, size_t... Extents>
+using lr_mapping_t = typename std::layout_right::template mapping<std::extents<IdxT, Extents...>>;
+template <class IdxT, size_t... Extents>
+using ls_mapping_t = typename std::layout_stride::template mapping<std::extents<IdxT, Extents...>>;
+
+constexpr void test_rank_mismatch() {
+ constexpr size_t D = std::dynamic_extent;
+
+ static_assert(!std::is_constructible_v<lr_mapping_t<int, D>, ls_mapping_t<int>>);
+ static_assert(!std::is_constructible_v<lr_mapping_t<int>, ls_mapping_t<int, D, D>>);
+ static_assert(!std::is_constructible_v<lr_mapping_t<int, D>, ls_mapping_t<int, D, D>>);
+ static_assert(!std::is_constructible_v<lr_mapping_t<int, D, D, D>, ls_mapping_t<int, D, D>>);
+}
+
+constexpr void test_static_extent_mismatch() {
+ constexpr size_t D = std::dynamic_extent;
+
+ static_assert(!std::is_constructible_v<lr_mapping_t<int, D, 5>, ls_mapping_t<int, D, 4>>);
+ static_assert(!std::is_constructible_v<lr_mapping_t<int, 5>, ls_mapping_t<int, 4>>);
+ static_assert(!std::is_constructible_v<lr_mapping_t<int, 5, D>, ls_mapping_t<int, 4, D>>);
+}
+
+constexpr bool test() {
+ test_conversion<int, int>();
+ test_conversion<int, size_t>();
+ test_conversion<size_t, int>();
+ test_conversion<size_t, long>();
+ test_rank_mismatch();
+ test_static_extent_mismatch();
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/containers/views/mdspan/layout_stride/comparison.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_stride/comparison.pass.cpp
new file mode 100644
index 000000000000000..e624e49df5a6c45
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_stride/comparison.pass.cpp
@@ -0,0 +1,198 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <mdspan>
+
+// template<class OtherMapping>
+// friend constexpr bool operator==(const mapping& x, const OtherMapping& y) noexcept;
+//
+// Constraints:
+// - layout-mapping-alike<OtherMapping> is satisfied.
+// - rank_ == OtherMapping::extents_type::rank() is true.
+// - OtherMapping::is_always_strided() is true.
+//
+// Preconditions: OtherMapping meets the layout mapping requirements ([mdspan.layout.policy.reqmts]).
+//
+// Returns: true if x.extents() == y.extents() is true, OFFSET(y) == 0 is true, and each of x.stride(r) == y.stride(r) is true for r in the range [0, x.extents().rank()). Otherwise, false.
+
+#include <mdspan>
+#include <type_traits>
+#include <concepts>
+#include <cassert>
+
+#include "test_macros.h"
+
+#include "../CustomTestLayouts.h"
+
+template <class E1, class E2>
+concept layout_mapping_comparable = requires(
+ E1 e1,
+ E2 e2,
+ std::array<typename E1::index_type, E1::rank()> s1,
+ std::array<typename E1::index_type, E1::rank()> s2) {
+ std::layout_stride::mapping<E1>(e1, s1) == std::layout_stride::mapping<E2>(e2, s2);
+};
+
+template <class T1, class T2>
+constexpr void test_comparison_different_rank() {
+ constexpr size_t D = std::dynamic_extent;
+
+ // sanity check same rank
+ static_assert(layout_mapping_comparable<std::extents<T1, D>, std::extents<T2, D>>);
+ static_assert(layout_mapping_comparable<std::extents<T1, 5>, std::extents<T2, D>>);
+ static_assert(layout_mapping_comparable<std::extents<T1, D>, std::extents<T2, 5>>);
+ static_assert(layout_mapping_comparable<std::extents<T1, 5>, std::extents<T2, 5>>);
+
+ // not equality comparable when rank is not the same
+ static_assert(!layout_mapping_comparable<std::extents<T1>, std::extents<T2, D>>);
+ static_assert(!layout_mapping_comparable<std::extents<T1>, std::extents<T2, 1>>);
+ static_assert(!layout_mapping_comparable<std::extents<T1, D>, std::extents<T2>>);
+ static_assert(!layout_mapping_comparable<std::extents<T1, 1>, std::extents<T2>>);
+ static_assert(!layout_mapping_comparable<std::extents<T1, D>, std::extents<T2, D, D>>);
+ static_assert(!layout_mapping_comparable<std::extents<T1, 5>, std::extents<T2, 5, D>>);
+ static_assert(!layout_mapping_comparable<std::extents<T1, 5>, std::extents<T2, 5, 1>>);
+ static_assert(!layout_mapping_comparable<std::extents<T1, D, D>, std::extents<T2, D>>);
+ static_assert(!layout_mapping_comparable<std::extents<T1, 5, D>, std::extents<T2, 5>>);
+ static_assert(!layout_mapping_comparable<std::extents<T1, 5, 1>, std::extents<T2, 5>>);
+}
+
+template <class To, class From>
+constexpr void test_comparison(
+ bool equal,
+ To dest_exts,
+ From src_exts,
+ std::array<int, To::rank()> dest_strides,
+ std::array<int, From::rank()> src_strides) {
+ std::layout_stride::mapping<To> dest(dest_exts, dest_strides);
+ std::layout_stride::mapping<From> src(src_exts, src_strides);
+ ASSERT_NOEXCEPT(dest == src);
+ assert((dest == src) == equal);
+ assert((dest != src) == !equal);
+}
+
+template <class T1, class T2>
+constexpr void test_comparison_same_rank() {
+ constexpr size_t D = std::dynamic_extent;
+
+ test_comparison(true, std::extents<T1>(), std::extents<T2>(), std::array<int, 0>{}, std::array<int, 0>{});
+
+ test_comparison(true, std::extents<T1, D>(5), std::extents<T2, D>(5), std::array<int, 1>{1}, std::array<int, 1>{1});
+ test_comparison(true, std::extents<T1, D>(0), std::extents<T2, D>(0), std::array<int, 1>{1}, std::array<int, 1>{1});
+ test_comparison(true, std::extents<T1, 5>(), std::extents<T2, D>(5), std::array<int, 1>{3}, std::array<int, 1>{3});
+ test_comparison(true, std::extents<T1, D>(5), std::extents<T2, 5>(), std::array<int, 1>{1}, std::array<int, 1>{1});
+ test_comparison(true, std::extents<T1, 5>(), std::extents< T2, 5>(), std::array<int, 1>{1}, std::array<int, 1>{1});
+ test_comparison(false, std::extents<T1, 5>(), std::extents<T2, D>(5), std::array<int, 1>{2}, std::array<int, 1>{1});
+ test_comparison(false, std::extents<T1, D>(5), std::extents<T2, D>(5), std::array<int, 1>{2}, std::array<int, 1>{1});
+ test_comparison(false, std::extents<T1, D>(5), std::extents<T2, D>(7), std::array<int, 1>{1}, std::array<int, 1>{1});
+ test_comparison(false, std::extents<T1, 5>(), std::extents<T2, D>(7), std::array<int, 1>{1}, std::array<int, 1>{1});
+ test_comparison(false, std::extents<T1, D>(5), std::extents<T2, 7>(), std::array<int, 1>{1}, std::array<int, 1>{1});
+ test_comparison(false, std::extents<T1, 5>(), std::extents<T2, 7>(), std::array<int, 1>{1}, std::array<int, 1>{1});
+
+ test_comparison(
+ true,
+ std::extents<T1, D, D, D, D, D>(5, 6, 7, 8, 9),
+ std::extents<T2, D, D, D, D, D>(5, 6, 7, 8, 9),
+ std::array<int, 5>{2, 20, 200, 2000, 20000},
+ std::array<int, 5>{2, 20, 200, 2000, 20000});
+ test_comparison(
+ true,
+ std::extents<T1, D, 6, D, 8, D>(5, 7, 9),
+ std::extents<T2, 5, D, D, 8, 9>(6, 7),
+ std::array<int, 5>{2, 20, 200, 2000, 20000},
+ std::array<int, 5>{2, 20, 200, 2000, 20000});
+ test_comparison(
+ true,
+ std::extents<T1, 5, 6, 7, 8, 9>(5, 6, 7, 8, 9),
+ std::extents<T2, 5, 6, 7, 8, 9>(),
+ std::array<int, 5>{2, 20, 200, 2000, 20000},
+ std::array<int, 5>{2, 20, 200, 2000, 20000});
+ test_comparison(
+ false,
+ std::extents<T1, 5, 6, 7, 8, 9>(5, 6, 7, 8, 9),
+ std::extents<T2, 5, 6, 7, 8, 9>(),
+ std::array<int, 5>{2, 20, 200, 20000, 2000},
+ std::array<int, 5>{2, 20, 200, 2000, 20000});
+ test_comparison(
+ false,
+ std::extents<T1, D, D, D, D, D>(5, 6, 7, 8, 9),
+ std::extents<T2, D, D, D, D, D>(5, 6, 3, 8, 9),
+ std::array<int, 5>{2, 20, 200, 2000, 20000},
+ std::array<int, 5>{2, 20, 200, 2000, 20000});
+ test_comparison(
+ false,
+ std::extents<T1, D, 6, D, 8, D>(5, 7, 9),
+ std::extents<T2, 5, D, D, 3, 9>(6, 7),
+ std::array<int, 5>{2, 20, 200, 2000, 20000},
+ std::array<int, 5>{2, 20, 200, 2000, 20000});
+ test_comparison(
+ false,
+ std::extents<T1, 5, 6, 7, 8, 9>(5, 6, 7, 8, 9),
+ std::extents<T2, 5, 6, 7, 3, 9>(),
+ std::array<int, 5>{2, 20, 200, 2000, 20000},
+ std::array<int, 5>{2, 20, 200, 2000, 20000});
+}
+
+template <class OtherLayout, class E1, class E2, class... OtherArgs>
+constexpr void test_comparison_with(
+ bool expect_equal, E1 e1, std::array<typename E1::index_type, E1::rank()> strides, E2 e2, OtherArgs... other_args) {
+ typename std::layout_stride::template mapping<E1> map(e1, strides);
+ typename OtherLayout::template mapping<E2> other_map(e2, other_args...);
+
+ assert((map == other_map) == expect_equal);
+}
+
+template <class OtherLayout>
+constexpr void test_comparison_with() {
+ constexpr size_t D = std::dynamic_extent;
+ bool is_left_based =
+ std::is_same_v<OtherLayout, std::layout_left> || std::is_same_v<OtherLayout, always_convertible_layout>;
+ test_comparison_with<OtherLayout>(true, std::extents<int>(), std::array<int, 0>{}, std::extents<unsigned>());
+ test_comparison_with<OtherLayout>(true, std::extents<int, 5>(), std::array<int, 1>{1}, std::extents<unsigned, 5>());
+ test_comparison_with<OtherLayout>(true, std::extents<int, D>(5), std::array<int, 1>{1}, std::extents<unsigned, 5>());
+ test_comparison_with<OtherLayout>(false, std::extents<int, D>(5), std::array<int, 1>{2}, std::extents<unsigned, 5>());
+ test_comparison_with<OtherLayout>(
+ is_left_based, std::extents<int, D, D>(5, 7), std::array<int, 2>{1, 5}, std::extents<unsigned, D, D>(5, 7));
+ test_comparison_with<OtherLayout>(
+ !is_left_based, std::extents<int, D, D>(5, 7), std::array<int, 2>{7, 1}, std::extents<unsigned, D, D>(5, 7));
+ test_comparison_with<OtherLayout>(
+ false, std::extents<int, D, D>(5, 7), std::array<int, 2>{8, 1}, std::extents<unsigned, D, D>(5, 7));
+
+ if constexpr (std::is_same_v<OtherLayout, always_convertible_layout>) {
+ // test layout with strides not equal to product of extents
+ test_comparison_with<OtherLayout>(
+ true, std::extents<int, D, D>(5, 7), std::array<int, 2>{2, 10}, std::extents<unsigned, D, D>(5, 7), 0, 2);
+ // make sure that offset != 0 results in false
+ test_comparison_with<OtherLayout>(
+ false, std::extents<int, D, D>(5, 7), std::array<int, 2>{2, 10}, std::extents<unsigned, D, D>(5, 7), 1, 2);
+ }
+}
+
+template <class T1, class T2>
+constexpr void test_comparison_index_type() {
+ test_comparison_same_rank<T1, T2>();
+ test_comparison_different_rank<T1, T2>();
+ test_comparison_with<std::layout_right>();
+ test_comparison_with<std::layout_left>();
+ test_comparison_with<always_convertible_layout>();
+}
+
+constexpr bool test() {
+ test_comparison_index_type<int, int>();
+ test_comparison_index_type<int, size_t>();
+ test_comparison_index_type<size_t, int>();
+ test_comparison_index_type<size_t, long>();
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/containers/views/mdspan/layout_stride/ctor.default.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_stride/ctor.default.pass.cpp
new file mode 100644
index 000000000000000..2fa8866058720d4
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_stride/ctor.default.pass.cpp
@@ -0,0 +1,73 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <mdspan>
+
+// Test default construction:
+//
+// constexpr mapping() noexcept;
+//
+//
+// Preconditions: layout_right::mapping<extents_type>().required_span_size() is representable as a value of type index_type ([basic.fundamental]).
+//
+// Effects: Direct-non-list-initializes extents_ with extents_type(), and for all d in the range [0, rank_),
+// direct-non-list-initializes strides_[d] with layout_right::mapping<extents_type>().stride(d).
+
+#include <mdspan>
+#include <cassert>
+#include <cstdint>
+
+#include "test_macros.h"
+
+template <class E>
+constexpr void test_construction() {
+ using M = std::layout_stride::mapping<E>;
+ ASSERT_NOEXCEPT(M{});
+ M m;
+ E e;
+
+ // check correct extents are returned
+ ASSERT_NOEXCEPT(m.extents());
+ assert(m.extents() == e);
+
+ // check required_span_size()
+ typename E::index_type expected_size = 1;
+ for (typename E::rank_type r = 0; r < E::rank(); r++)
+ expected_size *= e.extent(r);
+ assert(m.required_span_size() == expected_size);
+
+ // check strides: node stride function is constrained on rank>0, e.extent(r) is not
+ auto strides = m.strides();
+ ASSERT_NOEXCEPT(m.strides());
+ if constexpr (E::rank() > 0) {
+ std::layout_right::mapping<E> m_right;
+ for (typename E::rank_type r = 0; r < E::rank(); r++) {
+ assert(m.stride(r) == m_right.stride(r));
+ assert(strides[r] == m.stride(r));
+ }
+ }
+}
+
+constexpr bool test() {
+ constexpr size_t D = std::dynamic_extent;
+ test_construction<std::extents<int>>();
+ test_construction<std::extents<unsigned, D>>();
+ test_construction<std::extents<unsigned, 7>>();
+ test_construction<std::extents<unsigned, 0>>();
+ test_construction<std::extents<unsigned, 7, 8>>();
+ test_construction<std::extents<int64_t, D, 8, D, D>>();
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/containers/views/mdspan/layout_stride/ctor.extents_array.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_stride/ctor.extents_array.pass.cpp
new file mode 100644
index 000000000000000..f9157f8c6eeddec
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_stride/ctor.extents_array.pass.cpp
@@ -0,0 +1,138 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <mdspan>
+
+// template<class OtherIndexType>
+// constexpr mapping(const extents_type& e, array<OtherIndexType, rank_> s) noexcept;
+//
+// Constraints:
+// - is_convertible_v<const OtherIndexType&, index_type> is true, and
+// - is_nothrow_constructible_v<index_type, const OtherIndexType&> is true.
+//
+// Preconditions:
+// - s[i] > 0 is true for all i in the range [0, rank_).
+// - REQUIRED-SPAN-SIZE(e, s) is representable as a value of type index_type ([basic.fundamental]).
+// - If rank_ is greater than 0, then there exists a permutation P of the integers in the range [0, rank_),
+// such that s[pi] >= s[pi_1] * e.extent(pi_1) is true for all i in the range [1, rank_), where pi is the ith element of P.
+// Note 1: For layout_stride, this condition is necessary and sufficient for is_unique() to be true.
+//
+// Effects: Direct-non-list-initializes extents_ with e, and for all d in the range [0, rank_),
+// direct-non-list-initializes strides_[d] with as_const(s[d]).
+
+#include <mdspan>
+#include <cassert>
+#include <cstdint>
+
+#include "test_macros.h"
+#include "../ConvertibleToIntegral.h"
+
+template <class E, class S>
+constexpr void test_construction(E e, S s) {
+ using M = std::layout_stride::mapping<E>;
+ ASSERT_NOEXCEPT(M{e, s});
+ M m(e, s);
+
+ // check correct extents are returned
+ ASSERT_NOEXCEPT(m.extents());
+ assert(m.extents() == e);
+
+ // check required_span_size()
+ typename E::index_type expected_size = 1;
+ for (typename E::rank_type r = 0; r < E::rank(); r++) {
+ if (e.extent(r) == 0) {
+ expected_size = 0;
+ break;
+ }
+ expected_size += (e.extent(r) - 1) * static_cast<typename E::index_type>(s[r]);
+ }
+ assert(m.required_span_size() == expected_size);
+
+ // check strides: node stride function is constrained on rank>0, e.extent(r) is not
+ auto strides = m.strides();
+ ASSERT_NOEXCEPT(m.strides());
+ if constexpr (E::rank() > 0) {
+ for (typename E::rank_type r = 0; r < E::rank(); r++) {
+ assert(m.stride(r) == static_cast<typename E::index_type>(s[r]));
+ assert(strides[r] == m.stride(r));
+ }
+ }
+}
+
+constexpr bool test() {
+ constexpr size_t D = std::dynamic_extent;
+ {
+ std::array<int, 0> s{};
+ test_construction(std::extents<int>(), s);
+ }
+ {
+ std::array<int, 1> s{1};
+ test_construction(std::extents<unsigned, D>(7), s);
+ }
+ {
+ std::array<int, 1> s{1};
+ test_construction(std::extents<unsigned, D>(0), s);
+ }
+ {
+ std::array<int, 1> s{2};
+ test_construction(std::extents<unsigned, 7>(), s);
+ }
+ {
+ std::array<IntType, 1> s{1};
+ test_construction(std::extents<int, D>(7), s);
+ }
+ {
+ std::array<int, 2> s{3, 30};
+ test_construction(std::extents<unsigned, 7, 8>(), s);
+ }
+ {
+ std::array<int, 4> s{20, 2, 200, 2000};
+ test_construction(std::extents<int64_t, D, 8, D, D>(7, 9, 10), s);
+ test_construction(std::extents<int64_t, D, 8, D, D>(0, 9, 10), s);
+ test_construction(std::extents<int64_t, D, 8, D, D>(0, 8, 0), s);
+ }
+ {
+ std::array<int, 4> s{200, 20, 20, 2000};
+ test_construction(std::extents<int64_t, D, D, D, D>(7, 0, 8, 9), s);
+ test_construction(std::extents<int64_t, D, D, D, D>(7, 8, 0, 9), s);
+ test_construction(std::extents<int64_t, D, D, D, D>(7, 1, 8, 9), s);
+ test_construction(std::extents<int64_t, D, D, D, D>(7, 8, 1, 9), s);
+ test_construction(std::extents<int64_t, D, D, D, D>(7, 1, 1, 9), s);
+ test_construction(std::extents<int64_t, D, D, D, D>(7, 0, 0, 9), s);
+ test_construction(std::extents<int64_t, D, D, D, D>(7, 1, 1, 9), s);
+ test_construction(std::extents<int64_t, D, D, D, D>(7, 1, 0, 9), s);
+ test_construction(std::extents<int64_t, D, D, D, D>(7, 0, 1, 9), s);
+ }
+
+ {
+ using mapping_t = std::layout_stride::mapping<std::dextents<unsigned, 2>>;
+ // wrong strides size
+ static_assert(!std::is_constructible_v<mapping_t, std::dextents<int, 2>, std::array<int, 3>>);
+ static_assert(!std::is_constructible_v<mapping_t, std::dextents<int, 2>, std::array<int, 1>>);
+ // wrong extents rank
+ static_assert(!std::is_constructible_v<mapping_t, std::dextents<int, 3>, std::array<int, 2>>);
+ // none-convertible strides
+ static_assert(!std::is_constructible_v<mapping_t, std::dextents<int, 2>, std::array<IntType, 2>>);
+ }
+ {
+ // not no-throw constructible index_type from stride
+ using mapping_t = std::layout_stride::mapping<std::dextents<unsigned char, 2>>;
+ static_assert(std::is_convertible_v<IntType, unsigned char>);
+ static_assert(!std::is_constructible_v<mapping_t, std::dextents<int, 2>, std::array<IntType, 2>>);
+ }
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/containers/views/mdspan/layout_stride/ctor.extents_span.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_stride/ctor.extents_span.pass.cpp
new file mode 100644
index 000000000000000..36a87ae7a9e843b
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_stride/ctor.extents_span.pass.cpp
@@ -0,0 +1,141 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <mdspan>
+
+// template<class OtherIndexType>
+// constexpr mapping(const extents_type& e, span<OtherIndexType, rank_> s) noexcept;
+//
+// Constraints:
+// - is_convertible_v<const OtherIndexType&, index_type> is true, and
+// - is_nothrow_constructible_v<index_type, const OtherIndexType&> is true.
+//
+// Preconditions:
+// - s[i] > 0 is true for all i in the range [0, rank_).
+// - REQUIRED-SPAN-SIZE(e, s) is representable as a value of type index_type ([basic.fundamental]).
+// - If rank_ is greater than 0, then there exists a permutation P of the integers in the range [0, rank_),
+// such that s[pi] >= s[pi_1] * e.extent(pi_1) is true for all i in the range [1, rank_), where pi is the ith element of P.
+// Note 1: For layout_stride, this condition is necessary and sufficient for is_unique() to be true.
+//
+// Effects: Direct-non-list-initializes extents_ with e, and for all d in the range [0, rank_),
+// direct-non-list-initializes strides_[d] with as_const(s[d]).
+
+#include <mdspan>
+#include <cassert>
+#include <cstdint>
+
+#include "test_macros.h"
+#include "../ConvertibleToIntegral.h"
+
+template <class E, class S>
+constexpr void test_construction(E e, S s) {
+ using M = std::layout_stride::mapping<E>;
+ ASSERT_NOEXCEPT(M{e, s});
+ M m(e, s);
+
+ // check correct extents are returned
+ ASSERT_NOEXCEPT(m.extents());
+ assert(m.extents() == e);
+
+ // check required_span_size()
+ typename E::index_type expected_size = 1;
+ for (typename E::rank_type r = 0; r < E::rank(); r++) {
+ if (e.extent(r) == 0) {
+ expected_size = 0;
+ break;
+ }
+ expected_size += (e.extent(r) - 1) * static_cast<typename E::index_type>(s[r]);
+ }
+ assert(m.required_span_size() == expected_size);
+
+ // check strides: node stride function is constrained on rank>0, e.extent(r) is not
+ auto strides = m.strides();
+ ASSERT_NOEXCEPT(m.strides());
+ if constexpr (E::rank() > 0) {
+ for (typename E::rank_type r = 0; r < E::rank(); r++) {
+ assert(m.stride(r) == static_cast<typename E::index_type>(s[r]));
+ assert(strides[r] == m.stride(r));
+ }
+ }
+}
+
+constexpr bool test() {
+ constexpr size_t D = std::dynamic_extent;
+ {
+ std::array<int, 0> s{};
+ test_construction(std::extents<int>(), std::span(s));
+ }
+ {
+ std::array<int, 1> s{1};
+ test_construction(std::extents<unsigned, D>(7), std::span(s));
+ }
+ {
+ std::array<int, 1> s{1};
+ test_construction(std::extents<unsigned, D>(0), std::span(s));
+ }
+ {
+ std::array<int, 1> s{2};
+ test_construction(std::extents<unsigned, 7>(), std::span(s));
+ }
+ {
+ std::array<IntType, 1> s{1};
+ test_construction(std::extents<int, D>(7), std::span(s));
+ }
+ {
+ std::array<int, 2> s{3, 30};
+ test_construction(std::extents<unsigned, 7, 8>(), std::span(s));
+ }
+ {
+ std::array<int, 4> s{20, 2, 200, 2000};
+ test_construction(std::extents<int64_t, D, 8, D, D>(7, 9, 10), std::span(s));
+ }
+ {
+ std::array<int, 4> s{20, 2, 200, 2000};
+ test_construction(std::extents<int64_t, D, 8, D, D>(7, 0, 10), std::span(s));
+ test_construction(std::extents<int64_t, D, 8, D, D>(0, 9, 10), std::span(s));
+ test_construction(std::extents<int64_t, D, 8, D, D>(0, 8, 0), std::span(s));
+ }
+ {
+ std::array<int, 4> s{200, 20, 20, 2000};
+ test_construction(std::extents<int64_t, D, D, D, D>(7, 0, 8, 9), std::span(s));
+ test_construction(std::extents<int64_t, D, D, D, D>(7, 8, 0, 9), std::span(s));
+ test_construction(std::extents<int64_t, D, D, D, D>(7, 1, 8, 9), std::span(s));
+ test_construction(std::extents<int64_t, D, D, D, D>(7, 8, 1, 9), std::span(s));
+ test_construction(std::extents<int64_t, D, D, D, D>(7, 1, 1, 9), std::span(s));
+ test_construction(std::extents<int64_t, D, D, D, D>(7, 0, 0, 9), std::span(s));
+ test_construction(std::extents<int64_t, D, D, D, D>(7, 1, 1, 9), std::span(s));
+ test_construction(std::extents<int64_t, D, D, D, D>(7, 1, 0, 9), std::span(s));
+ test_construction(std::extents<int64_t, D, D, D, D>(7, 0, 1, 9), std::span(s));
+ }
+
+ {
+ using mapping_t = std::layout_stride::mapping<std::dextents<unsigned, 2>>;
+ // wrong strides size
+ static_assert(!std::is_constructible_v<mapping_t, std::dextents<int, 2>, std::span<int, 3>>);
+ static_assert(!std::is_constructible_v<mapping_t, std::dextents<int, 2>, std::span<int, 1>>);
+ // wrong extents rank
+ static_assert(!std::is_constructible_v<mapping_t, std::dextents<int, 3>, std::span<int, 2>>);
+ // none-convertible strides
+ static_assert(!std::is_constructible_v<mapping_t, std::dextents<int, 2>, std::span<IntType, 2>>);
+ }
+ {
+ // not no-throw constructible index_type from stride
+ using mapping_t = std::layout_stride::mapping<std::dextents<unsigned char, 2>>;
+ static_assert(std::is_convertible_v<IntType, unsigned char>);
+ static_assert(!std::is_constructible_v<mapping_t, std::dextents<int, 2>, std::span<IntType, 2>>);
+ }
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/containers/views/mdspan/layout_stride/ctor.strided_mapping.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_stride/ctor.strided_mapping.pass.cpp
new file mode 100644
index 000000000000000..7c9b3f34a41f0ec
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_stride/ctor.strided_mapping.pass.cpp
@@ -0,0 +1,187 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <mdspan>
+
+// template<class StridedLayoutMapping>
+// constexpr explicit(see below)
+// mapping(const StridedLayoutMapping& other) noexcept;
+//
+// Constraints:
+// - layout-mapping-alike<StridedLayoutMapping> is satisfied.
+// - is_constructible_v<extents_type, typename StridedLayoutMapping::extents_type> is true.
+// - StridedLayoutMapping::is_always_unique() is true.
+// - StridedLayoutMapping::is_always_strided() is true.
+//
+// Preconditions:
+// - StridedLayoutMapping meets the layout mapping requirements ([mdspan.layout.policy.reqmts]),
+// - other.stride(r) > 0 is true for every rank index r of extents(),
+// - other.required_span_size() is representable as a value of type index_type ([basic.fundamental]), and
+// - OFFSET(other) == 0 is true.
+//
+// Effects: Direct-non-list-initializes extents_ with other.extents(), and for all d in the range [0, rank_),
+// direct-non-list-initializes strides_[d] with other.stride(d).
+//
+// Remarks: The expression inside explicit is equivalent to:
+// - !(is_convertible_v<typename StridedLayoutMapping::extents_type, extents_type> &&
+// (is-mapping-of<layout_left, LayoutStrideMapping> ||
+// is-mapping-of<layout_right, LayoutStrideMapping> ||
+// is-mapping-of<layout_stride, LayoutStrideMapping>))
+
+#include <mdspan>
+#include <type_traits>
+#include <cassert>
+#include <limits>
+
+#include "test_macros.h"
+
+#include "../CustomTestLayouts.h"
+
+template <bool implicit, class FromL, class ToE, class FromE>
+constexpr void test_conversion(FromE src_exts) {
+ using To = std::layout_stride::mapping<ToE>;
+ using From = typename FromL::template mapping<FromE>;
+
+ From src([&]() {
+ if constexpr (std::is_same_v<FromL, std::layout_stride>) {
+ // just construct some strides which aren't layout_left/layout_right
+ std::array<size_t, FromE::rank()> strides;
+ size_t stride = 2;
+ for (size_t r = 0; r < FromE::rank(); r++) {
+ strides[r] = stride;
+ stride *= src_exts.extent(r);
+ }
+ return From(src_exts, strides);
+ } else {
+ return From(src_exts);
+ }
+ }());
+
+ ASSERT_NOEXCEPT(To(src));
+ To dest(src);
+ assert(dest == src);
+
+ if constexpr (implicit) {
+ To dest_implicit = src;
+ assert(dest_implicit == src);
+ } else {
+ assert((!std::is_convertible_v<From, To>));
+ }
+}
+
+template <class FromL, class T1, class T2>
+constexpr void test_conversion() {
+ constexpr size_t D = std::dynamic_extent;
+ constexpr bool idx_convertible =
+ static_cast<size_t>(std::numeric_limits<T1>::max()) >= static_cast<size_t>(std::numeric_limits<T2>::max());
+ constexpr bool l_convertible =
+ std::is_same_v<FromL, std::layout_right> || std::is_same_v<FromL, std::layout_left> ||
+ std::is_same_v<FromL, std::layout_stride>;
+ constexpr bool idx_l_convertible = idx_convertible && l_convertible;
+
+ // clang-format off
+ // adding extents convertibility expectation
+ test_conversion<idx_l_convertible && true, FromL, std::extents<T1>>(std::extents<T2>());
+ test_conversion<idx_l_convertible && true, FromL, std::extents<T1, D>>(std::extents<T2, D>(0));
+ test_conversion<idx_l_convertible && true, FromL, std::extents<T1, D>>(std::extents<T2, D>(5));
+ test_conversion<idx_l_convertible && false, FromL, std::extents<T1, 5>>(std::extents<T2, D>(5));
+ test_conversion<idx_l_convertible && true, FromL, std::extents<T1, 5>>(std::extents<T2, 5>());
+ test_conversion<idx_l_convertible && false, FromL, std::extents<T1, 5, D>>(std::extents<T2, D, D>(5, 5));
+ test_conversion<idx_l_convertible && true, FromL, std::extents<T1, D, D>>(std::extents<T2, D, D>(5, 5));
+ test_conversion<idx_l_convertible && true, FromL, std::extents<T1, D, D>>(std::extents<T2, D, 7>(5));
+ test_conversion<idx_l_convertible && true, FromL, std::extents<T1, 5, 7>>(std::extents<T2, 5, 7>());
+ test_conversion<idx_l_convertible && false, FromL, std::extents<T1, 5, D, 8, D, D>>(std::extents<T2, D, D, 8, 9, 1>(5, 7));
+ test_conversion<idx_l_convertible && true, FromL, std::extents<T1, D, D, D, D, D>>(
+ std::extents<T2, D, D, D, D, D>(5, 7, 8, 9, 1));
+ test_conversion<idx_l_convertible && true, FromL, std::extents<T1, D, D, 8, 9, D>>(std::extents<T2, D, 7, 8, 9, 1>(5));
+ test_conversion<idx_l_convertible && true, FromL, std::extents<T1, 5, 7, 8, 9, 1>>(std::extents<T2, 5, 7, 8, 9, 1>());
+ // clang-format on
+}
+
+template <class IdxT, size_t... Extents>
+using ToM = typename std::layout_stride::template mapping<std::extents<IdxT, Extents...>>;
+
+template <class FromL, class IdxT, size_t... Extents>
+using FromM = typename FromL::template mapping<std::extents<IdxT, Extents...>>;
+
+template <class FromL>
+constexpr void test_no_implicit_conversion() {
+ constexpr size_t D = std::dynamic_extent;
+
+ // Sanity check that one static to dynamic conversion works
+ static_assert(std::is_constructible_v<ToM<int, D>, FromM<FromL, int, 5>>);
+ static_assert(std::is_convertible_v<FromM<FromL, int, 5>, ToM<int, D>>);
+
+ // Check that dynamic to static conversion only works explicitly
+ static_assert(std::is_constructible_v<ToM<int, 5>, FromM<FromL, int, D>>);
+ static_assert(!std::is_convertible_v<FromM<FromL, int, D>, ToM<int, 5>>);
+
+ // Sanity check that one static to dynamic conversion works
+ static_assert(std::is_constructible_v<ToM<int, D, 7>, FromM<FromL, int, 5, 7>>);
+ static_assert(std::is_convertible_v<FromM<FromL, int, 5, 7>, ToM<int, D, 7>>);
+
+ // Check that dynamic to static conversion only works explicitly
+ static_assert(std::is_constructible_v<ToM<int, 5, 7>, FromM<FromL, int, D, 7>>);
+ static_assert(!std::is_convertible_v<FromM<FromL, int, D, 7>, ToM<int, 5, 7>>);
+
+ // Sanity check that smaller index_type to larger index_type conversion works
+ static_assert(std::is_constructible_v<ToM<size_t, 5>, FromM<FromL, int, 5>>);
+ static_assert(std::is_convertible_v<FromM<FromL, int, 5>, ToM<size_t, 5>>);
+
+ // Check that larger index_type to smaller index_type conversion works explicitly only
+ static_assert(std::is_constructible_v<ToM<int, 5>, FromM<FromL, size_t, 5>>);
+ static_assert(!std::is_convertible_v<FromM<FromL, size_t, 5>, ToM<int, 5>>);
+}
+
+template <class FromL>
+constexpr void test_rank_mismatch() {
+ constexpr size_t D = std::dynamic_extent;
+
+ static_assert(!std::is_constructible_v<ToM<int, D>, FromM<FromL, int>>);
+ static_assert(!std::is_constructible_v<ToM<int>, FromM<FromL, int, D, D>>);
+ static_assert(!std::is_constructible_v<ToM<int, D>, FromM<FromL, int, D, D>>);
+ static_assert(!std::is_constructible_v<ToM<int, D, D, D>, FromM<FromL, int, D, D>>);
+}
+
+template <class FromL>
+constexpr void test_static_extent_mismatch() {
+ constexpr size_t D = std::dynamic_extent;
+
+ static_assert(!std::is_constructible_v<ToM<int, D, 5>, FromM<FromL, int, D, 4>>);
+ static_assert(!std::is_constructible_v<ToM<int, 5>, FromM<FromL, int, 4>>);
+ static_assert(!std::is_constructible_v<ToM<int, 5, D>, FromM<FromL, int, 4, D>>);
+}
+
+template <class FromL>
+constexpr void test_layout() {
+ test_conversion<FromL, int, int>();
+ test_conversion<FromL, int, size_t>();
+ test_conversion<FromL, size_t, int>();
+ test_conversion<FromL, size_t, long>();
+ // the implicit convertibility test doesn't apply to non std::layouts
+ if constexpr (!std::is_same_v<FromL, always_convertible_layout>)
+ test_no_implicit_conversion<FromL>();
+ test_rank_mismatch<FromL>();
+ test_static_extent_mismatch<FromL>();
+}
+
+constexpr bool test() {
+ test_layout<std::layout_right>();
+ test_layout<std::layout_left>();
+ test_layout<std::layout_stride>();
+ test_layout<always_convertible_layout>();
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/containers/views/mdspan/layout_stride/deduction.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_stride/deduction.pass.cpp
new file mode 100644
index 000000000000000..ebe31d9d0ece34e
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_stride/deduction.pass.cpp
@@ -0,0 +1,55 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// ADDITIONAL_COMPILE_FLAGS: -Wno-ctad-maybe-unsupported
+
+// <mdspan>
+
+#include <mdspan>
+#include <type_traits>
+#include <concepts>
+#include <cassert>
+
+#include "test_macros.h"
+
+// mdspan
+
+// layout_stride::mapping does not have explicit deduction guides,
+// but implicit deduction guides for constructor taking extents and strides
+// should work
+
+constexpr bool test() {
+ constexpr size_t D = std::dynamic_extent;
+
+ ASSERT_SAME_TYPE(decltype(std::layout_stride::mapping(std::extents<int>(), std::array<unsigned, 0>())),
+ std::layout_stride::template mapping<std::extents<int>>);
+ ASSERT_SAME_TYPE(decltype(std::layout_stride::mapping(std::extents<int, 4>(), std::array<char, 1>{1})),
+ std::layout_stride::template mapping<std::extents<int, 4>>);
+ ASSERT_SAME_TYPE(decltype(std::layout_stride::mapping(std::extents<int, D>(), std::array<char, 1>{1})),
+ std::layout_stride::template mapping<std::extents<int, D>>);
+ ASSERT_SAME_TYPE(
+ decltype(std::layout_stride::mapping(std::extents<unsigned, D, 3>(), std::array<int64_t, 2>{3, 100})),
+ std::layout_stride::template mapping<std::extents<unsigned, D, 3>>);
+
+ ASSERT_SAME_TYPE(decltype(std::layout_stride::mapping(std::extents<int>(), std::span<unsigned, 0>())),
+ std::layout_stride::template mapping<std::extents<int>>);
+ ASSERT_SAME_TYPE(decltype(std::layout_stride::mapping(std::extents<int, 4>(), std::declval<std::span<char, 1>>())),
+ std::layout_stride::template mapping<std::extents<int, 4>>);
+ ASSERT_SAME_TYPE(decltype(std::layout_stride::mapping(std::extents<int, D>(), std::declval<std::span<char, 1>>())),
+ std::layout_stride::template mapping<std::extents<int, D>>);
+ ASSERT_SAME_TYPE(
+ decltype(std::layout_stride::mapping(std::extents<unsigned, D, 3>(), std::declval<std::span<int64_t, 2>>())),
+ std::layout_stride::template mapping<std::extents<unsigned, D, 3>>);
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/containers/views/mdspan/layout_stride/extents.verify.cpp b/libcxx/test/std/containers/views/mdspan/layout_stride/extents.verify.cpp
new file mode 100644
index 000000000000000..4742527f7af11e3
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_stride/extents.verify.cpp
@@ -0,0 +1,33 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <mdspan>
+
+// template<class Extents>
+// class layout_stride::mapping;
+
+// If Extents is not a specialization of extents, then the program is
+// ill-formed.
+
+// Mandates: If Extents::rank_dynamic() == 0 is true, then the size of the
+// multidimensional index space Extents() is representable as a value of type
+// typename Extents::index_type.
+
+#include <mdspan>
+
+void not_extents() {
+ // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}layout_stride::mapping template argument must be a specialization of extents}}
+ [[maybe_unused]] std::layout_stride::mapping<void> mapping;
+}
+
+void representable() {
+ // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}layout_stride::mapping product of static extents must be representable as index_type.}}
+ [[maybe_unused]] std::layout_stride::mapping<std::extents<char, 20, 20>> mapping;
+}
diff --git a/libcxx/test/std/containers/views/mdspan/layout_stride/index_operator.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_stride/index_operator.pass.cpp
new file mode 100644
index 000000000000000..dbd56a54758c97c
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_stride/index_operator.pass.cpp
@@ -0,0 +1,121 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <mdspan>
+
+// Test default iteration:
+//
+// template<class... Indices>
+// constexpr index_type operator()(Indices...) const noexcept;
+//
+// Constraints:
+// * sizeof...(Indices) == extents_type::rank() is true,
+// * (is_convertible_v<Indices, index_type> && ...) is true, and
+// * (is_nothrow_constructible_v<index_type, Indices> && ...) is true.
+//
+// Preconditions:
+// * extents_type::index-cast(i) is a multidimensional index in extents_.
+
+#include <mdspan>
+#include <cassert>
+#include <cstdint>
+
+#include "test_macros.h"
+
+#include "../ConvertibleToIntegral.h"
+
+template <class Mapping, class... Indices>
+concept operator_constraints = requires(Mapping m, Indices... idxs) {
+ { std::is_same_v<decltype(m(idxs...)), typename Mapping::index_type> };
+};
+
+template <class Mapping, class... Indices>
+ requires(operator_constraints<Mapping, Indices...>)
+constexpr bool check_operator_constraints(Mapping m, Indices... idxs) {
+ (void)m(idxs...);
+ return true;
+}
+
+template <class Mapping, class... Indices>
+constexpr bool check_operator_constraints(Mapping, Indices...) {
+ return false;
+}
+
+template <class M, class... Args>
+constexpr void iterate_stride(M m, const std::array<int, M::extents_type::rank()>& strides, Args... args) {
+ constexpr int r = static_cast<int>(M::extents_type::rank()) - 1 - static_cast<int>(sizeof...(Args));
+ if constexpr (-1 == r) {
+ ASSERT_NOEXCEPT(m(args...));
+ size_t expected_val = [&]<size_t... Pos>(std::index_sequence<Pos...>) {
+ return ((args * strides[Pos]) + ... + 0);
+ }(std::make_index_sequence<M::extents_type::rank()>());
+ assert(expected_val == static_cast<size_t>(m(args...)));
+ } else {
+ for (typename M::index_type i = 0; i < m.extents().extent(r); i++) {
+ iterate_stride(m, strides, i, args...);
+ }
+ }
+}
+
+template <class E, class... Args>
+constexpr void test_iteration(std::array<int, E::rank()> strides, Args... args) {
+ using M = std::layout_stride::mapping<E>;
+ M m(E(args...), strides);
+
+ iterate_stride(m, strides);
+}
+
+constexpr bool test() {
+ constexpr size_t D = std::dynamic_extent;
+ test_iteration<std::extents<int>>(std::array<int, 0>{});
+ test_iteration<std::extents<unsigned, D>>(std::array<int, 1>{2}, 1);
+ test_iteration<std::extents<unsigned, D>>(std::array<int, 1>{3}, 7);
+ test_iteration<std::extents<unsigned, 7>>(std::array<int, 1>{4});
+ test_iteration<std::extents<unsigned, 7, 8>>(std::array<int, 2>{25, 3});
+ test_iteration<std::extents<char, D, D, D, D>>(std::array<int, 4>{1, 1, 1, 1}, 1, 1, 1, 1);
+
+ // Check operator constraint for number of arguments
+ static_assert(check_operator_constraints(
+ std::layout_stride::mapping<std::extents<int, D>>(std::extents<int, D>(1), std::array{1}), 0));
+ static_assert(!check_operator_constraints(
+ std::layout_stride::mapping<std::extents<int, D>>(std::extents<int, D>(1), std::array{1}), 0, 0));
+
+ // Check operator constraint for convertibility of arguments to index_type
+ static_assert(check_operator_constraints(
+ std::layout_stride::mapping<std::extents<int, D>>(std::extents<int, D>(1), std::array{1}), IntType(0)));
+ static_assert(!check_operator_constraints(
+ std::layout_stride::mapping<std::extents<unsigned, D>>(std::extents<unsigned, D>(1), std::array{1}), IntType(0)));
+
+ // Check operator constraint for no-throw-constructibility of index_type from arguments
+ static_assert(!check_operator_constraints(
+ std::layout_stride::mapping<std::extents<unsigned char, D>>(std::extents<unsigned char, D>(1), std::array{1}),
+ IntType(0)));
+
+ return true;
+}
+
+constexpr bool test_large() {
+ constexpr size_t D = std::dynamic_extent;
+ test_iteration<std::extents<int64_t, D, 8, D, D>>(std::array<int, 4>{2000, 2, 20, 200}, 7, 9, 10);
+ test_iteration<std::extents<int64_t, D, 8, 1, D>>(std::array<int, 4>{2000, 20, 20, 200}, 7, 10);
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+
+ // The large test iterates over ~10k loop indices.
+ // With assertions enabled this triggered the maximum default limit
+ // for steps in consteval expressions. Assertions roughly double the
+ // total number of instructions, so this was already close to the maximum.
+ test_large();
+ return 0;
+}
diff --git a/libcxx/test/std/containers/views/mdspan/layout_stride/is_exhaustive_corner_case.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_stride/is_exhaustive_corner_case.pass.cpp
new file mode 100644
index 000000000000000..d13db91ac5a175d
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_stride/is_exhaustive_corner_case.pass.cpp
@@ -0,0 +1,54 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <mdspan>
+
+// constexpr bool is_exhaustive() const noexcept;
+//
+// Returns:
+// - true if rank_ is 0.
+// - Otherwise, true if there is a permutation P of the integers in the range [0, rank_) such that
+// stride(p0) equals 1, and stride(pi) equals stride(pi_1) * extents().extent(pi_1) for i in the
+// range [1, rank_), where pi is the ith element of P.
+// - Otherwise, false.
+
+#include <mdspan>
+#include <type_traits>
+#include <concepts>
+#include <cassert>
+
+#include "test_macros.h"
+
+template <class E>
+constexpr void
+test_layout_mapping_stride(E ext, std::array<typename E::index_type, E::rank()> strides, bool exhaustive) {
+ using M = std::layout_stride::template mapping<E>;
+ M m(ext, strides);
+ assert(m.is_exhaustive() == exhaustive);
+}
+
+constexpr bool test() {
+ constexpr size_t D = std::dynamic_extent;
+ test_layout_mapping_stride(std::extents<int, 0>(), std::array<int, 1>{1}, true);
+ test_layout_mapping_stride(std::extents<unsigned, D>(0), std::array<unsigned, 1>{3}, false);
+ test_layout_mapping_stride(std::extents<int, 0, 3>(), std::array<int, 2>{6, 2}, true);
+ test_layout_mapping_stride(std::extents<int, D, D>(3, 0), std::array<int, 2>{6, 2}, false);
+ test_layout_mapping_stride(std::extents<int, D, D>(0, 0), std::array<int, 2>{6, 2}, false);
+ test_layout_mapping_stride(
+ std::extents<unsigned, D, D, D, D>(3, 3, 0, 3), std::array<unsigned, 4>{3, 1, 27, 9}, true);
+ test_layout_mapping_stride(std::extents<int, D, D, D, D>(0, 3, 3, 3), std::array<int, 4>{3, 1, 27, 9}, false);
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/containers/views/mdspan/layout_stride/properties.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_stride/properties.pass.cpp
new file mode 100644
index 000000000000000..d3d0aa6b3c3a538
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_stride/properties.pass.cpp
@@ -0,0 +1,116 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <mdspan>
+
+// namespace std {
+// template<class Extents>
+// class layout_stride::mapping {
+//
+// ...
+// static constexpr bool is_always_unique() noexcept { return true; }
+// static constexpr bool is_always_exhaustive() noexcept { return false; }
+// static constexpr bool is_always_strided() noexcept { return true; }
+//
+// static constexpr bool is_unique() noexcept { return true; }
+// static constexpr bool is_exhaustive() noexcept;
+// static constexpr bool is_strided() noexcept { return true; }
+// ...
+// };
+// }
+//
+//
+// layout_stride::mapping<E> is a trivially copyable type that models regular for each E.
+//
+// constexpr bool is_exhaustive() const noexcept;
+//
+// Returns:
+// - true if rank_ is 0.
+// - Otherwise, true if there is a permutation P of the integers in the range [0, rank_) such that
+// stride(p0) equals 1, and stride(pi) equals stride(pi_1) * extents().extent(pi_1) for i in the
+// range [1, rank_), where pi is the ith element of P.
+// - Otherwise, false.
+
+#include <mdspan>
+#include <type_traits>
+#include <concepts>
+#include <cassert>
+
+#include "test_macros.h"
+
+template <class E>
+constexpr void
+test_layout_mapping_stride(E ext, std::array<typename E::index_type, E::rank()> strides, bool exhaustive) {
+ using M = std::layout_stride::template mapping<E>;
+ M m(ext, strides);
+ const M c_m = m;
+ assert(m.strides() == strides);
+ assert(c_m.strides() == strides);
+ assert(m.extents() == ext);
+ assert(c_m.extents() == ext);
+ assert(M::is_unique() == true);
+ assert(m.is_exhaustive() == exhaustive);
+ assert(c_m.is_exhaustive() == exhaustive);
+ assert(M::is_strided() == true);
+ assert(M::is_always_unique() == true);
+ assert(M::is_always_exhaustive() == false);
+ assert(M::is_always_strided() == true);
+
+ ASSERT_NOEXCEPT(m.strides());
+ ASSERT_NOEXCEPT(c_m.strides());
+ ASSERT_NOEXCEPT(m.extents());
+ ASSERT_NOEXCEPT(c_m.extents());
+ ASSERT_NOEXCEPT(M::is_unique());
+ ASSERT_NOEXCEPT(m.is_exhaustive());
+ ASSERT_NOEXCEPT(c_m.is_exhaustive());
+ ASSERT_NOEXCEPT(M::is_strided());
+ ASSERT_NOEXCEPT(M::is_always_unique());
+ ASSERT_NOEXCEPT(M::is_always_exhaustive());
+ ASSERT_NOEXCEPT(M::is_always_strided());
+
+ for (typename E::rank_type r = 0; r < E::rank(); r++) {
+ assert(m.stride(r) == strides[r]);
+ assert(c_m.stride(r) == strides[r]);
+ ASSERT_NOEXCEPT(m.stride(r));
+ ASSERT_NOEXCEPT(c_m.stride(r));
+ }
+
+ typename E::index_type expected_size = 1;
+ for (typename E::rank_type r = 0; r < E::rank(); r++) {
+ if (ext.extent(r) == 0) {
+ expected_size = 0;
+ break;
+ }
+ expected_size += (ext.extent(r) - 1) * static_cast<typename E::index_type>(strides[r]);
+ }
+ assert(m.required_span_size() == expected_size);
+ assert(c_m.required_span_size() == expected_size);
+ ASSERT_NOEXCEPT(m.required_span_size());
+ ASSERT_NOEXCEPT(c_m.required_span_size());
+
+ static_assert(std::is_trivially_copyable_v<M>);
+ static_assert(std::regular<M>);
+}
+
+constexpr bool test() {
+ constexpr size_t D = std::dynamic_extent;
+ test_layout_mapping_stride(std::extents<int>(), std::array<int, 0>{}, true);
+ test_layout_mapping_stride(std::extents<char, 4, 5>(), std::array<char, 2>{1, 4}, true);
+ test_layout_mapping_stride(std::extents<char, 4, 5>(), std::array<char, 2>{1, 5}, false);
+ test_layout_mapping_stride(std::extents<unsigned, D, 4>(7), std::array<unsigned, 2>{20, 2}, false);
+ test_layout_mapping_stride(std::extents<size_t, D, D, D, D>(3, 3, 3, 3), std::array<size_t, 4>{3, 1, 9, 27}, true);
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/containers/views/mdspan/layout_stride/required_span_size.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_stride/required_span_size.pass.cpp
new file mode 100644
index 000000000000000..3b6af9f6c1ff00f
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_stride/required_span_size.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <mdspan>
+
+// Let REQUIRED-SPAN-SIZE(e, strides) be:
+// - 1, if e.rank() == 0 is true,
+// - otherwise 0, if the size of the multidimensional index space e is 0,
+// - otherwise 1 plus the sum of products of (e.extent(r) - 1) and strides[r] for all r in the range [0, e.rank()).
+
+// constexpr index_type required_span_size() const noexcept;
+//
+// Returns: REQUIRED-SPAN-SIZE(extents(), strides_).
+
+#include <mdspan>
+#include <cassert>
+#include <cstdint>
+
+#include "test_macros.h"
+
+template <class E>
+constexpr void test_required_span_size(E e, std::array<int, E::rank()> strides, typename E::index_type expected_size) {
+ using M = std::layout_stride::mapping<E>;
+ const M m(e, strides);
+
+ ASSERT_NOEXCEPT(m.required_span_size());
+ assert(m.required_span_size() == expected_size);
+}
+
+constexpr bool test() {
+ constexpr size_t D = std::dynamic_extent;
+ test_required_span_size(std::extents<int>(), std::array<int, 0>{}, 1);
+ test_required_span_size(std::extents<unsigned, D>(0), std::array<int, 1>{5}, 0);
+ test_required_span_size(std::extents<unsigned, D>(1), std::array<int, 1>{5}, 1);
+ test_required_span_size(std::extents<unsigned, D>(7), std::array<int, 1>{5}, 31);
+ test_required_span_size(std::extents<unsigned, 7>(), std::array<int, 1>{5}, 31);
+ test_required_span_size(std::extents<unsigned, 7, 8>(), std::array<int, 2>{20, 2}, 135);
+ test_required_span_size(
+ std::extents<int64_t, D, 8, D, D>(7, 9, 10), std::array<int, 4>{1, 7, 7 * 8, 7 * 8 * 9}, 5040);
+ test_required_span_size(std::extents<int64_t, 1, 8, D, D>(9, 10), std::array<int, 4>{1, 7, 7 * 8, 7 * 8 * 9}, 5034);
+ test_required_span_size(std::extents<int64_t, 1, 0, D, D>(9, 10), std::array<int, 4>{1, 7, 7 * 8, 7 * 8 * 9}, 0);
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/containers/views/mdspan/layout_stride/static_requirements.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_stride/static_requirements.pass.cpp
new file mode 100644
index 000000000000000..112f4b9e5a04033
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_stride/static_requirements.pass.cpp
@@ -0,0 +1,131 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <mdspan>
+
+// A type M meets the layout mapping requirements if
+// - M models copyable and equality_comparable,
+// - is_nothrow_move_constructible_v<M> is true,
+// - is_nothrow_move_assignable_v<M> is true,
+// - is_nothrow_swappable_v<M> is true, and
+//
+// the following types and expressions are well-formed and have the specified semantics.
+//
+// typename M::extents_type
+// Result: A type that is a specialization of extents.
+//
+// typename M::index_type
+// Result: typename M::extents_type::index_type.
+//
+// typename M::rank_type
+// Result: typename M::extents_type::rank_type.
+//
+// typename M::layout_type
+// Result: A type MP that meets the layout mapping policy requirements ([mdspan.layout.policy.reqmts]) and for which is-mapping-of<MP, M> is true.
+//
+// m.extents()
+// Result: const typename M::extents_type&
+//
+// m(i...)
+// Result: typename M::index_type
+// Returns: A nonnegative integer less than numeric_limits<typename M::index_type>::max() and less than or equal to numeric_limits<size_t>::max().
+//
+// m(i...) == m(static_cast<typename M::index_type>(i)...)
+// Result: bool
+// Returns: true
+//
+// m.required_span_size()
+// Result: typename M::index_type
+// Returns: If the size of the multidimensional index space m.extents() is 0, then 0, else 1 plus the maximum value of m(i...) for all i.
+//
+// m.is_unique()
+// Result: bool
+// Returns: true only if for every i and j where (i != j || ...) is true, m(i...) != m(j...) is true.
+//
+// m.is_exhaustive()
+// Result: bool
+// Returns: true only if for all k in the range [0, m.required_span_size()) there exists an i such that m(i...) equals k.
+//
+// m.is_strided()
+// Result: bool
+// Returns: true only if for every rank index r of m.extents() there exists an integer
+// sr such that, for all i where (i+dr) is a multidimensional index in m.extents() ([mdspan.overview]),
+// m((i + dr)...) - m(i...) equals sr
+//
+// m.stride(r)
+// Preconditions: m.is_strided() is true.
+// Result: typename M::index_type
+// Returns: sr as defined in m.is_strided() above.
+//
+// M::is_always_unique()
+// Result: A constant expression ([expr.const]) of type bool.
+// Returns: true only if m.is_unique() is true for all possible objects m of type M.
+//
+// M::is_always_exhaustive()
+// Result: A constant expression ([expr.const]) of type bool.
+// Returns: true only if m.is_exhaustive() is true for all possible objects m of type M.
+//
+// M::is_always_strided()
+// Result: A constant expression ([expr.const]) of type bool.
+// Returns: true only if m.is_strided() is true for all possible objects m of type M.
+
+#include <mdspan>
+#include <type_traits>
+#include <concepts>
+#include <cassert>
+
+#include "test_macros.h"
+
+// Common requirements of all layout mappings
+template <class M, size_t... Idxs>
+void test_mapping_requirements(std::index_sequence<Idxs...>) {
+ using E = typename M::extents_type;
+ static_assert(std::__mdspan_detail::__is_extents_v<E>);
+ static_assert(std::is_copy_constructible_v<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>);
+ ASSERT_SAME_TYPE(typename M::index_type, typename E::index_type);
+ ASSERT_SAME_TYPE(typename M::size_type, typename E::size_type);
+ ASSERT_SAME_TYPE(typename M::rank_type, typename E::rank_type);
+ ASSERT_SAME_TYPE(typename M::layout_type, std::layout_stride);
+ ASSERT_SAME_TYPE(typename M::layout_type::template mapping<E>, M);
+ static_assert(std::is_same_v<decltype(std::declval<M>().extents()), const E&>);
+ static_assert(std::is_same_v<decltype(std::declval<M>().strides()), std::array<typename M::index_type, E::rank()>>);
+ static_assert(std::is_same_v<decltype(std::declval<M>()(Idxs...)), typename M::index_type>);
+ static_assert(std::is_same_v<decltype(std::declval<M>().required_span_size()), typename M::index_type>);
+ static_assert(std::is_same_v<decltype(std::declval<M>().is_unique()), bool>);
+ static_assert(std::is_same_v<decltype(std::declval<M>().is_exhaustive()), bool>);
+ static_assert(std::is_same_v<decltype(std::declval<M>().is_strided()), bool>);
+ static_assert(std::is_same_v<decltype(std::declval<M>().stride(0)), typename M::index_type>);
+ static_assert(std::is_same_v<decltype(M::is_always_unique()), bool>);
+ static_assert(std::is_same_v<decltype(M::is_always_exhaustive()), bool>);
+ static_assert(std::is_same_v<decltype(M::is_always_strided()), bool>);
+}
+
+template <class L, class E>
+void test_layout_mapping_requirements() {
+ using M = typename L::template mapping<E>;
+ test_mapping_requirements<M>(std::make_index_sequence<E::rank()>());
+}
+
+template <class E>
+void test_layout_mapping_stride() {
+ test_layout_mapping_requirements<std::layout_stride, E>();
+}
+
+int main(int, char**) {
+ constexpr size_t D = std::dynamic_extent;
+ test_layout_mapping_stride<std::extents<int>>();
+ test_layout_mapping_stride<std::extents<char, 4, 5>>();
+ test_layout_mapping_stride<std::extents<unsigned, D, 4>>();
+ test_layout_mapping_stride<std::extents<size_t, D, D, D, D>>();
+ return 0;
+}
diff --git a/libcxx/test/std/containers/views/mdspan/layout_stride/stride.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_stride/stride.pass.cpp
new file mode 100644
index 000000000000000..07a5199e5bbb18c
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_stride/stride.pass.cpp
@@ -0,0 +1,57 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+
+// <mdspan>
+
+// constexpr index_type stride(rank_type i) const noexcept;
+//
+// Constraints: extents_type::rank() > 0 is true.
+//
+// Preconditions: i < extents_type::rank() is true.
+//
+// Returns: extents().rev-prod-of-extents(i).
+
+#include <mdspan>
+#include <cassert>
+#include <array>
+#include <cstdint>
+#include <cstdio>
+#include "test_macros.h"
+
+template <class E, class... Args>
+constexpr void test_stride(std::array<typename E::index_type, E::rank()> strides, Args... args) {
+ using M = std::layout_stride::mapping<E>;
+ M m(E(args...), strides);
+
+ ASSERT_NOEXCEPT(m.stride(0));
+ for (size_t r = 0; r < E::rank(); r++)
+ assert(strides[r] == m.stride(r));
+
+ ASSERT_NOEXCEPT(m.strides());
+ auto strides_out = m.strides();
+ static_assert(std::is_same_v<decltype(strides_out), std::array<typename E::index_type, E::rank()>>);
+ for (size_t r = 0; r < E::rank(); r++)
+ assert(strides[r] == strides_out[r]);
+}
+
+constexpr bool test() {
+ constexpr size_t D = std::dynamic_extent;
+ test_stride<std::extents<unsigned, D>>(std::array<unsigned, 1>{1}, 7);
+ test_stride<std::extents<unsigned, 7>>(std::array<unsigned, 1>{1});
+ test_stride<std::extents<unsigned, 7, 8>>(std::array<unsigned, 2>{8, 1});
+ test_stride<std::extents<int64_t, D, 8, D, D>>(std::array<int64_t, 4>{720, 90, 10, 1}, 7, 9, 10);
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/assign.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/assign.pass.cpp
index a6f436fd44880c9..8741c79315f1e87 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/assign.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/assign.pass.cpp
@@ -19,7 +19,7 @@
#include "test_macros.h"
#include "../MinimalElementType.h"
-#include "CustomTestLayouts.h"
+#include "../CustomTestLayouts.h"
#include "CustomTestAccessors.h"
template <class H, class M, class A>
diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/conversion.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/conversion.pass.cpp
index bb5927633350328..abb647e960c34e8 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/conversion.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/conversion.pass.cpp
@@ -43,7 +43,7 @@
#include "test_macros.h"
#include "../MinimalElementType.h"
-#include "CustomTestLayouts.h"
+#include "../CustomTestLayouts.h"
#include "CustomTestAccessors.h"
template <class ToMDS, class FromMDS>
diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/conversion.verify.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/conversion.verify.cpp
index c9fcdb11f8db41c..e3f03ebd07e5fa0 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/conversion.verify.cpp
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/conversion.verify.cpp
@@ -37,7 +37,7 @@
#include <mdspan>
#include "CustomTestAccessors.h"
-#include "CustomTestLayouts.h"
+#include "../CustomTestLayouts.h"
void cant_construct_data_handle_type() {
int data;
diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/ctor.copy.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.copy.pass.cpp
index 813250d8cefff5d..7fb85ed845d6cbc 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/ctor.copy.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.copy.pass.cpp
@@ -21,7 +21,7 @@
#include "test_macros.h"
#include "../MinimalElementType.h"
-#include "CustomTestLayouts.h"
+#include "../CustomTestLayouts.h"
#include "CustomTestAccessors.h"
template <class H, class M, class A>
diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/ctor.default.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.default.pass.cpp
index f42a4dbf1bf2c08..8ad12944df84d4b 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/ctor.default.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.default.pass.cpp
@@ -29,7 +29,7 @@
#include "test_macros.h"
#include "../MinimalElementType.h"
-#include "CustomTestLayouts.h"
+#include "../CustomTestLayouts.h"
#include "CustomTestAccessors.h"
template <bool hc, bool mc, bool ac, class H, class M, class A>
diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_array.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_array.pass.cpp
index e7fd81b3ab4e933..e4f88d6035049f6 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_array.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_array.pass.cpp
@@ -38,7 +38,7 @@
#include "../ConvertibleToIntegral.h"
#include "../MinimalElementType.h"
-#include "CustomTestLayouts.h"
+#include "../CustomTestLayouts.h"
#include "CustomTestAccessors.h"
template <class Extents, size_t... Idxs>
diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_extents.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_extents.pass.cpp
index c194fdaeffd1386..c15d1dc47ad9703 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_extents.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_extents.pass.cpp
@@ -31,7 +31,7 @@
#include "test_macros.h"
#include "../MinimalElementType.h"
-#include "CustomTestLayouts.h"
+#include "../CustomTestLayouts.h"
#include "CustomTestAccessors.h"
template <bool mec, bool ac, class H, class M, class A>
diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_integers.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_integers.pass.cpp
index 1bd9e488ad0d41b..0288e63106522df 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_integers.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_integers.pass.cpp
@@ -38,7 +38,7 @@
#include "test_macros.h"
#include "../MinimalElementType.h"
-#include "CustomTestLayouts.h"
+#include "../CustomTestLayouts.h"
#include "CustomTestAccessors.h"
template <class MDS, class... Args>
diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_map.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_map.pass.cpp
index 9b1c6bd0668af9d..9fe79ad460ccb05 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_map.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_map.pass.cpp
@@ -29,7 +29,7 @@
#include "test_macros.h"
#include "../MinimalElementType.h"
-#include "CustomTestLayouts.h"
+#include "../CustomTestLayouts.h"
#include "CustomTestAccessors.h"
template <bool ac, class H, class M, class A>
diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_map_acc.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_map_acc.pass.cpp
index 7a94e8b04032693..3be47ae92072395 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_map_acc.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_map_acc.pass.cpp
@@ -26,7 +26,7 @@
#include "test_macros.h"
#include "../MinimalElementType.h"
-#include "CustomTestLayouts.h"
+#include "../CustomTestLayouts.h"
#include "CustomTestAccessors.h"
template <class H, class M, class A>
diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_span.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_span.pass.cpp
index f76c909783344f4..30876abea5005bb 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_span.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.dh_span.pass.cpp
@@ -38,7 +38,7 @@
#include "../ConvertibleToIntegral.h"
#include "../MinimalElementType.h"
-#include "CustomTestLayouts.h"
+#include "../CustomTestLayouts.h"
#include "CustomTestAccessors.h"
template <class Extents, size_t... Idxs>
diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/ctor.move.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.move.pass.cpp
index aaaa4bc85709c29..10bd5114bd4d89d 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/ctor.move.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/ctor.move.pass.cpp
@@ -21,7 +21,7 @@
#include "test_macros.h"
#include "../MinimalElementType.h"
-#include "CustomTestLayouts.h"
+#include "../CustomTestLayouts.h"
#include "CustomTestAccessors.h"
template <class H, class M, class A>
diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp
index 69b2f7e39831afa..d83299c2c53c993 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/deduction.pass.cpp
@@ -55,7 +55,7 @@
#include "test_macros.h"
#include "../MinimalElementType.h"
-#include "CustomTestLayouts.h"
+#include "../CustomTestLayouts.h"
#include "CustomTestAccessors.h"
template <class H, class M, class A>
diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/index_operator.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/index_operator.pass.cpp
index 9e622d1c01fc007..5af2b3a45874b5c 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/index_operator.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/index_operator.pass.cpp
@@ -36,7 +36,7 @@
#include "test_macros.h"
#include "../ConvertibleToIntegral.h"
-#include "CustomTestLayouts.h"
+#include "../CustomTestLayouts.h"
// Clang 16 does not support argument packs as input to operator []
#if defined(__clang_major__) && __clang_major__ < 17
diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/move.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/move.pass.cpp
index 544a5b8aa1ede4a..4729bd171da86a0 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/move.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/move.pass.cpp
@@ -21,7 +21,7 @@
#include "test_macros.h"
#include "../MinimalElementType.h"
-#include "CustomTestLayouts.h"
+#include "../CustomTestLayouts.h"
#include "CustomTestAccessors.h"
template <class H, class M, class A>
diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/properties.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/properties.pass.cpp
index c822c3ad9c24f06..8f2bc42c8c01ec1 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/properties.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/properties.pass.cpp
@@ -58,7 +58,7 @@
#include "test_macros.h"
#include "../MinimalElementType.h"
-#include "CustomTestLayouts.h"
+#include "../CustomTestLayouts.h"
template <class H, class M, class A>
constexpr void test_mdspan_types(const H& handle, const M& map, const A& acc) {
diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/swap.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/swap.pass.cpp
index dd1336c7af07e80..9b54da3252d47ea 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/swap.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/swap.pass.cpp
@@ -24,7 +24,7 @@
#include "test_macros.h"
#include "../MinimalElementType.h"
-#include "CustomTestLayouts.h"
+#include "../CustomTestLayouts.h"
template <class MDS>
constexpr void test_swap(MDS a, MDS b) {
diff --git a/libcxx/test/std/containers/views/mdspan/mdspan/types.pass.cpp b/libcxx/test/std/containers/views/mdspan/mdspan/types.pass.cpp
index 5effea554c0d22d..e99f8a3c524a8bb 100644
--- a/libcxx/test/std/containers/views/mdspan/mdspan/types.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/mdspan/types.pass.cpp
@@ -36,7 +36,7 @@
#include "../MinimalElementType.h"
#include "CustomTestAccessors.h"
-#include "CustomTestLayouts.h"
+#include "../CustomTestLayouts.h"
// Calculated expected size of an mdspan
// Note this expectes that only default_accessor is empty
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/mdspan.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/mdspan.version.compile.pass.cpp
index e47d678b254ef8d..a44f2b6a91e5cc3 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/mdspan.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/mdspan.version.compile.pass.cpp
@@ -65,17 +65,11 @@
#elif TEST_STD_VER == 23
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_mdspan
-# error "__cpp_lib_mdspan should be defined in c++23"
-# endif
-# if __cpp_lib_mdspan != 202207L
-# error "__cpp_lib_mdspan should have the value 202207L in c++23"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_mdspan
-# error "__cpp_lib_mdspan should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_mdspan
+# error "__cpp_lib_mdspan should be defined in c++23"
+# endif
+# if __cpp_lib_mdspan != 202207L
+# error "__cpp_lib_mdspan should have the value 202207L in c++23"
# endif
# ifdef __cpp_lib_submdspan
@@ -84,17 +78,11 @@
#elif TEST_STD_VER > 23
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_mdspan
-# error "__cpp_lib_mdspan should be defined in c++26"
-# endif
-# if __cpp_lib_mdspan != 202207L
-# error "__cpp_lib_mdspan should have the value 202207L in c++26"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_mdspan
-# error "__cpp_lib_mdspan should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_mdspan
+# error "__cpp_lib_mdspan should be defined in c++26"
+# endif
+# if __cpp_lib_mdspan != 202207L
+# error "__cpp_lib_mdspan should have the value 202207L in c++26"
# endif
# if !defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index 3804a8b2bd3459d..ed86c555221c542 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -4804,17 +4804,11 @@
# endif
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_mdspan
-# error "__cpp_lib_mdspan should be defined in c++23"
-# endif
-# if __cpp_lib_mdspan != 202207L
-# error "__cpp_lib_mdspan should have the value 202207L in c++23"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_mdspan
-# error "__cpp_lib_mdspan should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_mdspan
+# error "__cpp_lib_mdspan should be defined in c++23"
+# endif
+# if __cpp_lib_mdspan != 202207L
+# error "__cpp_lib_mdspan should have the value 202207L in c++23"
# endif
# if !defined(_LIBCPP_AVAILABILITY_HAS_NO_PMR)
@@ -6345,17 +6339,11 @@
# endif
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_mdspan
-# error "__cpp_lib_mdspan should be defined in c++26"
-# endif
-# if __cpp_lib_mdspan != 202207L
-# error "__cpp_lib_mdspan should have the value 202207L in c++26"
-# endif
-# else // _LIBCPP_VERSION
-# ifdef __cpp_lib_mdspan
-# error "__cpp_lib_mdspan should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_mdspan
+# error "__cpp_lib_mdspan should be defined in c++26"
+# endif
+# if __cpp_lib_mdspan != 202207L
+# error "__cpp_lib_mdspan should have the value 202207L in c++26"
# endif
# if !defined(_LIBCPP_AVAILABILITY_HAS_NO_PMR)
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 4b114d3824d7bb8..76faa275d2d7e2f 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -689,7 +689,6 @@ def add_version_header(tc):
"name": "__cpp_lib_mdspan",
"values": {"c++23": 202207},
"headers": ["mdspan"],
- "unimplemented": True,
},
{
"name": "__cpp_lib_memory_resource",
More information about the libcxx-commits
mailing list