[libcxx-commits] [libcxx] b4ff893 - [libc++][mdspan] Implement layout_left
Christian Trott via libcxx-commits
libcxx-commits at lists.llvm.org
Thu Jun 29 13:04:38 PDT 2023
Author: Christian Trott
Date: 2023-06-29T14:01:08-06:00
New Revision: b4ff893877ffe6f80fefd705a9e16a6dcd624851
URL: https://github.com/llvm/llvm-project/commit/b4ff893877ffe6f80fefd705a9e16a6dcd624851
DIFF: https://github.com/llvm/llvm-project/commit/b4ff893877ffe6f80fefd705a9e16a6dcd624851.diff
LOG: [libc++][mdspan] Implement layout_left
This commit implements layout_left in support of C++23 mdspan
(https://wg21.link/p0009). layout_left is a layout mapping policy
whose index mapping corresponds to the memory layout of Fortran arrays.
Thus the left most index has stride-1 access, and the right most index
is associated with the largest stride.
Co-authored-by: Damien L-G <dalg24 at gmail.com>
Differential Revision: https://reviews.llvm.org/D153783
Added:
libcxx/include/__mdspan/layout_left.h
libcxx/test/std/containers/views/mdspan/layout_left/assert.conversion.pass.cpp
libcxx/test/std/containers/views/mdspan/layout_left/assert.ctor.extents.pass.cpp
libcxx/test/std/containers/views/mdspan/layout_left/assert.ctor.layout_right.pass.cpp
libcxx/test/std/containers/views/mdspan/layout_left/assert.index_operator.pass.cpp
libcxx/test/std/containers/views/mdspan/layout_left/assert.stride.pass.cpp
libcxx/test/std/containers/views/mdspan/layout_left/comparison.pass.cpp
libcxx/test/std/containers/views/mdspan/layout_left/ctor.default.pass.cpp
libcxx/test/std/containers/views/mdspan/layout_left/ctor.extents.pass.cpp
libcxx/test/std/containers/views/mdspan/layout_left/ctor.layout_right.pass.cpp
libcxx/test/std/containers/views/mdspan/layout_left/ctor.mapping.pass.cpp
libcxx/test/std/containers/views/mdspan/layout_left/extents.verify.cpp
libcxx/test/std/containers/views/mdspan/layout_left/index_operator.pass.cpp
libcxx/test/std/containers/views/mdspan/layout_left/properties.pass.cpp
libcxx/test/std/containers/views/mdspan/layout_left/required_span_size.pass.cpp
libcxx/test/std/containers/views/mdspan/layout_left/static_requirements.pass.cpp
libcxx/test/std/containers/views/mdspan/layout_left/stride.pass.cpp
libcxx/test/std/containers/views/mdspan/layout_right/assert.ctor.layout_left.pass.cpp
libcxx/test/std/containers/views/mdspan/layout_right/ctor.layout_left.pass.cpp
Modified:
libcxx/docs/Status/Cxx23.rst
libcxx/include/CMakeLists.txt
libcxx/include/__fwd/mdspan.h
libcxx/include/__mdspan/layout_right.h
libcxx/include/mdspan
libcxx/include/module.modulemap.in
libcxx/modules/std/mdspan.cppm
libcxx/test/std/containers/views/mdspan/ConvertibleToIntegral.h
libcxx/test/std/containers/views/mdspan/layout_right/assert.conversion.pass.cpp
Removed:
################################################################################
diff --git a/libcxx/docs/Status/Cxx23.rst b/libcxx/docs/Status/Cxx23.rst
index d99728b1bec84a..c1c8170c117e9b 100644
--- a/libcxx/docs/Status/Cxx23.rst
+++ b/libcxx/docs/Status/Cxx23.rst
@@ -40,7 +40,7 @@ Paper Status
.. note::
- .. [#note-P0009R18] P0009R18: ``extents``, ``dextents``, and ``layout_right`` are implemented.
+ .. [#note-P0009R18] P0009R18: ``extents``, ``dextents``, ``layout_left``, and ``layout_right`` 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/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index b77772b56d7dac..12fec8d3d9ad00 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -478,6 +478,7 @@ set(files
__locale_dir/locale_base_api/locale_guard.h
__mbstate_t.h
__mdspan/extents.h
+ __mdspan/layout_left.h
__mdspan/layout_right.h
__memory/addressof.h
__memory/align.h
diff --git a/libcxx/include/__fwd/mdspan.h b/libcxx/include/__fwd/mdspan.h
index 591e5f6e9511be..9a5cfceb5f69c0 100644
--- a/libcxx/include/__fwd/mdspan.h
+++ b/libcxx/include/__fwd/mdspan.h
@@ -30,14 +30,11 @@ _LIBCPP_BEGIN_NAMESPACE_STD
#if _LIBCPP_STD_VER >= 23
-/*
-// Will be implemented with follow on revision
// Layout policy with a mapping which corresponds to FORTRAN-style array layouts
struct layout_left {
- template<class Extents>
- class mapping;
+ template <class Extents>
+ class mapping;
};
-*/
// Layout policy with a mapping which corresponds to C-style array layouts
struct layout_right {
diff --git a/libcxx/include/__mdspan/layout_left.h b/libcxx/include/__mdspan/layout_left.h
new file mode 100644
index 00000000000000..e81e0d10b595a7
--- /dev/null
+++ b/libcxx/include/__mdspan/layout_left.h
@@ -0,0 +1,171 @@
+// -*- 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_LEFT_H
+#define _LIBCPP___MDSPAN_LAYOUT_LEFT_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/integer_sequence.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
+
+template <class _Extents>
+class layout_left::mapping {
+public:
+ static_assert(__mdspan_detail::__is_extents<_Extents>::value,
+ "layout_left::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_left;
+
+private:
+ _LIBCPP_HIDE_FROM_ABI static constexpr bool __required_span_size_is_representable(const extents_type& __ext) {
+ if constexpr (extents_type::rank() == 0)
+ return true;
+
+ index_type __prod = __ext.extent(0);
+ for (rank_type __r = 1; __r < extents_type::rank(); __r++) {
+ bool __overflowed = __builtin_mul_overflow(__prod, __ext.extent(__r), &__prod);
+ if (__overflowed)
+ return false;
+ }
+ return true;
+ }
+
+ static_assert((extents_type::rank_dynamic() > 0) || __required_span_size_is_representable(extents_type()),
+ "layout_left::mapping product of static extents must be representable as index_type.");
+
+public:
+ // [mdspan.layout.left.cons], constructors
+ _LIBCPP_HIDE_FROM_ABI constexpr mapping() noexcept = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr mapping(const mapping&) noexcept = default;
+ _LIBCPP_HIDE_FROM_ABI constexpr mapping(const extents_type& __ext) noexcept : __extents_(__ext) {
+ _LIBCPP_ASSERT(__required_span_size_is_representable(__ext),
+ "layout_left::mapping extents ctor: product of extents must be representable as index_type.");
+ }
+
+ template <class _OtherExtents>
+ requires(is_constructible_v<extents_type, _OtherExtents>)
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>)
+ mapping(const mapping<_OtherExtents>& __other) noexcept
+ : __extents_(__other.extents()) {
+ _LIBCPP_ASSERT(
+ __mdspan_detail::__is_representable_as<index_type>(__other.required_span_size()),
+ "layout_left::mapping converting ctor: other.required_span_size() must be representable as index_type.");
+ }
+
+ template <class _OtherExtents>
+ requires(is_constructible_v<extents_type, _OtherExtents> && _OtherExtents::rank() <= 1)
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>)
+ mapping(const layout_right::mapping<_OtherExtents>& __other) noexcept
+ : __extents_(__other.extents()) {
+ _LIBCPP_ASSERT(
+ __mdspan_detail::__is_representable_as<index_type>(__other.required_span_size()),
+ "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
+
+ _LIBCPP_HIDE_FROM_ABI constexpr mapping& operator=(const mapping&) noexcept = default;
+
+ // [mdspan.layout.left.obs], observers
+ _LIBCPP_HIDE_FROM_ABI constexpr const extents_type& extents() const noexcept { return __extents_; }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr index_type required_span_size() const noexcept {
+ index_type __size = 1;
+ for (size_t __r = 0; __r < extents_type::rank(); __r++)
+ __size *= __extents_.extent(__r);
+ return __size;
+ }
+
+ template <class... _Indices>
+ requires((sizeof...(_Indices) == extents_type::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 {
+ _LIBCPP_ASSERT(__mdspan_detail::__is_multidimensional_index_in(__extents_, __idx...),
+ "layout_left::mapping: out of bounds indexing");
+ array<index_type, extents_type::rank()> __idx_a{static_cast<index_type>(__idx)...};
+ return [&]<size_t... _Pos>(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;
+ }(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 true; }
+ _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; }
+ _LIBCPP_HIDE_FROM_ABI static constexpr bool is_exhaustive() noexcept { return true; }
+ _LIBCPP_HIDE_FROM_ABI static constexpr bool is_strided() noexcept { return true; }
+
+ _LIBCPP_HIDE_FROM_ABI constexpr index_type stride(rank_type __r) const noexcept
+ requires(extents_type::rank() > 0)
+ {
+ _LIBCPP_ASSERT(__r < extents_type::rank(), "layout_left::mapping::stride(): invalid rank index");
+ index_type __s = 1;
+ for (rank_type __i = extents_type::rank() - 1; __i > __r; __i--)
+ __s *= __extents_.extent(__i);
+ return __s;
+ }
+
+ template <class _OtherExtents>
+ requires(_OtherExtents::rank() == extents_type::rank())
+ _LIBCPP_HIDE_FROM_ABI friend constexpr bool
+ operator==(const mapping& __lhs, const mapping<_OtherExtents>& __rhs) noexcept {
+ return __lhs.extents() == __rhs.extents();
+ }
+
+private:
+ extents_type __extents_{}; // exposition only
+};
+
+#endif // _LIBCPP_STD_VER >= 23
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___MDSPAN_LAYOUT_LEFT_H
diff --git a/libcxx/include/__mdspan/layout_right.h b/libcxx/include/__mdspan/layout_right.h
index 18ecb2008fe93d..a8a91b86c71421 100644
--- a/libcxx/include/__mdspan/layout_right.h
+++ b/libcxx/include/__mdspan/layout_right.h
@@ -88,12 +88,18 @@ class layout_right::mapping {
"layout_right::mapping converting ctor: other.required_span_size() must be representable as index_type.");
}
+ template <class _OtherExtents>
+ requires(is_constructible_v<extents_type, _OtherExtents> && _OtherExtents::rank() <= 1)
+ _LIBCPP_HIDE_FROM_ABI constexpr explicit(!is_convertible_v<_OtherExtents, extents_type>)
+ mapping(const layout_left::mapping<_OtherExtents>& __other) noexcept
+ : __extents_(__other.extents()) {
+ _LIBCPP_ASSERT(
+ __mdspan_detail::__is_representable_as<index_type>(__other.required_span_size()),
+ "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(!is_convertible_v<_OtherExtents, extents_type>)
- mapping(const layout_left::mapping<_OtherExtents>&) noexcept {}
-
template<class _OtherExtents>
constexpr explicit(extents_type::rank() > 0)
mapping(const layout_stride::mapping_<OtherExtents>&) noexcept;
diff --git a/libcxx/include/mdspan b/libcxx/include/mdspan
index 39405712ec012d..9116c859a8809f 100644
--- a/libcxx/include/mdspan
+++ b/libcxx/include/mdspan
@@ -21,7 +21,7 @@ namespace std {
using dextents = see below;
// [mdspan.layout], layout mapping
- struct layout_left; // not implemented yet
+ struct layout_left;
struct layout_right;
struct layout_stride; // not implemented yet
@@ -82,6 +82,60 @@ namespace std {
-> see below;
}
+// layout_left synopsis
+
+namespace std {
+ template<class Extents>
+ class layout_left::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_left;
+
+ // [mdspan.layout.right.cons], constructors
+ constexpr mapping() noexcept = default;
+ constexpr mapping(const mapping&) noexcept = default;
+ constexpr mapping(const extents_type&) noexcept;
+ template<class OtherExtents>
+ constexpr explicit(!is_convertible_v<OtherExtents, extents_type>)
+ mapping(const mapping<OtherExtents>&) noexcept;
+ template<class OtherExtents>
+ constexpr explicit(!is_convertible_v<OtherExtents, extents_type>)
+ mapping(const layout_right::mapping<OtherExtents>&) noexcept;
+ template<class OtherExtents>
+ constexpr explicit(extents_type::rank() > 0)
+ mapping(const layout_stride::mapping<OtherExtents>&) noexcept;
+
+ constexpr mapping& operator=(const mapping&) noexcept = default;
+
+ // [mdspan.layout.right.obs], observers
+ constexpr const extents_type& extents() const noexcept { return extents_; }
+
+ 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 true; }
+ 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; }
+ static constexpr bool is_strided() noexcept { return true; }
+
+ constexpr index_type stride(rank_type) const noexcept;
+
+ template<class OtherExtents>
+ friend constexpr bool operator==(const mapping&, const mapping<OtherExtents>&) noexcept;
+
+ private:
+ extents_type extents_{}; // exposition only
+ };
+}
+
// layout_right synopsis
namespace std {
@@ -144,6 +198,7 @@ namespace std {
#include <__config>
#include <__fwd/mdspan.h>
#include <__mdspan/extents.h>
+#include <__mdspan/layout_left.h>
#include <__mdspan/layout_right.h>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 06c5f5d8750c7a..011c5560eabea5 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -1174,6 +1174,7 @@ module std [system] {
module __mdspan {
module extents { private header "__mdspan/extents.h" }
+ module layout_left { private header "__mdspan/layout_left.h" }
module layout_right { private header "__mdspan/layout_right.h" }
module mdspan_fwd { private header "__fwd/mdspan.h" }
}
diff --git a/libcxx/modules/std/mdspan.cppm b/libcxx/modules/std/mdspan.cppm
index af2c16f736adb1..eef6097e88ef4c 100644
--- a/libcxx/modules/std/mdspan.cppm
+++ b/libcxx/modules/std/mdspan.cppm
@@ -19,7 +19,7 @@ export namespace std {
using std::dextents;
// [mdspan.layout], layout mapping
- // using std::layout_left;
+ using std::layout_left;
using std::layout_right;
// using std::layout_stride;
diff --git a/libcxx/test/std/containers/views/mdspan/ConvertibleToIntegral.h b/libcxx/test/std/containers/views/mdspan/ConvertibleToIntegral.h
index 92ff35d15ed4ad..df67c2050d4bea 100644
--- a/libcxx/test/std/containers/views/mdspan/ConvertibleToIntegral.h
+++ b/libcxx/test/std/containers/views/mdspan/ConvertibleToIntegral.h
@@ -6,8 +6,8 @@
//
//===----------------------------------------------------------------------===//
-#ifndef TEST_STD_CONTAINERS_VIEWS_MDSPAN_LAYOUT_RIGHT_CONVERTIBLE_TO_INTEGRAL_H
-#define TEST_STD_CONTAINERS_VIEWS_MDSPAN_LAYOUT_RIGHT_CONVERTIBLE_TO_INTEGRAL_H
+#ifndef TEST_STD_CONTAINERS_VIEWS_MDSPAN_CONVERTIBLE_TO_INTEGRAL_H
+#define TEST_STD_CONTAINERS_VIEWS_MDSPAN_CONVERTIBLE_TO_INTEGRAL_H
struct IntType {
int val;
@@ -20,4 +20,4 @@ struct IntType {
constexpr operator char() const noexcept { return val; }
};
-#endif // TEST_STD_CONTAINERS_VIEWS_MDSPAN_LAYOUT_RIGHT_CONVERTIBLE_TO_INTEGRAL_H
+#endif // TEST_STD_CONTAINERS_VIEWS_MDSPAN_CONVERTIBLE_TO_INTEGRAL_H
diff --git a/libcxx/test/std/containers/views/mdspan/layout_left/assert.conversion.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_left/assert.conversion.pass.cpp
new file mode 100644
index 00000000000000..288855708d6af2
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_left/assert.conversion.pass.cpp
@@ -0,0 +1,59 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: availability-verbose_abort-missing
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1
+
+// <mdspan>
+
+// template<class OtherExtents>
+// constexpr explicit(!is_convertible_v<OtherExtents, extents_type>)
+// mapping(const mapping<OtherExtents>&) noexcept;
+
+// Constraints: is_constructible_v<extents_type, OtherExtents> is true.
+//
+// Preconditions: other.required_span_size() is representable as a value of type index_type.
+
+#include <mdspan>
+#include <cassert>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+ constexpr size_t D = std::dynamic_extent;
+ std::extents<int, D, D> arg_exts{100, 5};
+ std::layout_left::mapping<std::extents<int, D, D>> arg(arg_exts);
+
+ // working case
+ {
+ [[maybe_unused]] std::layout_left::mapping<std::extents<size_t, D, 5>> m(arg); // should work
+ }
+ // mismatch of static extent
+ {
+ TEST_LIBCPP_ASSERT_FAILURE(([=] { std::layout_left::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_left::mapping<std::extents<char, D>> m(
+ std::layout_left::mapping<std::extents<int, D>>(std::extents<int, D>(500))); }()),
+ "extents ctor: arguments must be representable as index_type and nonnegative");
+ }
+ // required_span_size not representable, while individual extents are
+ {
+ // 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_left
+ TEST_LIBCPP_ASSERT_FAILURE(
+ ([=] { std::layout_left::template mapping<std::extents<char, D, 5>> m(arg); }()),
+ "layout_left::mapping converting ctor: other.required_span_size() must be representable as index_type.");
+ }
+ return 0;
+}
diff --git a/libcxx/test/std/containers/views/mdspan/layout_left/assert.ctor.extents.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_left/assert.ctor.extents.pass.cpp
new file mode 100644
index 00000000000000..d970c1ee997cc8
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_left/assert.ctor.extents.pass.cpp
@@ -0,0 +1,39 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: availability-verbose_abort-missing
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1
+
+// <mdspan>
+
+
+// constexpr mapping(const extents_type& e) noexcept;
+//
+// Preconditions: The size of the multidimensional index space e is representable as a value of type index_type ([basic.fundamental]).
+//
+// Effects: Direct-non-list-initializes extents_ with e.
+
+#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 is not, so we can't use it for layout_left
+ TEST_LIBCPP_ASSERT_FAILURE(
+ ([=] { std::layout_left::template mapping<std::extents<char, D, 5>> m(std::extents<char, D, 5>(100)); }()),
+ "layout_left::mapping extents ctor: product of extents must be representable as index_type.");
+ }
+ return 0;
+}
diff --git a/libcxx/test/std/containers/views/mdspan/layout_left/assert.ctor.layout_right.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_left/assert.ctor.layout_right.pass.cpp
new file mode 100644
index 00000000000000..0d4e829e12cd98
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_left/assert.ctor.layout_right.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// XFAIL: availability-verbose_abort-missing
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1
+
+// <mdspan>
+
+// template<class OtherExtents>
+// constexpr explicit(!is_convertible_v<OtherExtents, extents_type>)
+// mapping(const layout_right::mapping<OtherExtents>&) noexcept;
+
+// Constraints:
+// - extents_type::rank() <= 1 is true, and
+// - is_constructible_v<extents_type, OtherExtents> is true.
+//
+// Preconditions: other.required_span_size() is representable as a value of type index_type
+
+#include <mdspan>
+#include <cassert>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+ constexpr size_t D = std::dynamic_extent;
+ std::extents<int, D> arg_exts{5};
+ std::layout_right::mapping<std::extents<int, D>> arg(arg_exts);
+
+ // working case
+ {
+ [[maybe_unused]] std::layout_left::mapping<std::extents<size_t, 5>> m(arg); // should work
+ }
+ // mismatch of static extent
+ {
+ 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
+ {
+ TEST_LIBCPP_ASSERT_FAILURE(([=] { std::layout_left::mapping<std::extents<char, D>> m(
+ std::layout_right::mapping<std::extents<int, D>>(std::extents<int, D>(500))); }()),
+ "extents ctor: arguments must be representable as index_type and nonnegative");
+ }
+
+ // Can't trigger required_span_size() representability assertion, since for rank-1 the above check will trigger first,
+ // and this conversion constructor is constrained on rank <= 1.
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/views/mdspan/layout_left/assert.index_operator.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_left/assert.index_operator.pass.cpp
new file mode 100644
index 00000000000000..779e38cbb0f507
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_left/assert.index_operator.pass.cpp
@@ -0,0 +1,84 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: availability-verbose_abort-missing
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1
+
+// <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_left::template mapping<std::extents<unsigned char, 5>> m;
+ TEST_LIBCPP_ASSERT_FAILURE(m(-1), "layout_left::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(-130), "layout_left::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(5), "layout_left::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(1000), "layout_left::mapping: out of bounds indexing");
+ }
+ {
+ std::layout_left::template mapping<std::extents<signed char, 5>> m;
+ TEST_LIBCPP_ASSERT_FAILURE(m(-1), "layout_left::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(-130), "layout_left::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(5), "layout_left::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(1000), "layout_left::mapping: out of bounds indexing");
+ }
+ {
+ std::layout_left::template mapping<std::dextents<unsigned char, 1>> m(std::dextents<unsigned char, 1>(5));
+ TEST_LIBCPP_ASSERT_FAILURE(m(-1), "layout_left::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(-130), "layout_left::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(5), "layout_left::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(1000), "layout_left::mapping: out of bounds indexing");
+ }
+ {
+ std::layout_left::template mapping<std::dextents<signed char, 1>> m(std::dextents<signed char, 1>(5));
+ TEST_LIBCPP_ASSERT_FAILURE(m(-1), "layout_left::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(-130), "layout_left::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(5), "layout_left::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(1000), "layout_left::mapping: out of bounds indexing");
+ }
+ {
+ std::layout_left::template mapping<std::dextents<int, 3>> m(std::dextents<int, 3>(5, 7, 9));
+ TEST_LIBCPP_ASSERT_FAILURE(m(-1, -1, -1), "layout_left::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(-1, 0, 0), "layout_left::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(0, -1, 0), "layout_left::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(0, 0, -1), "layout_left::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(5, 3, 3), "layout_left::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(3, 7, 3), "layout_left::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(3, 3, 9), "layout_left::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(5, 7, 9), "layout_left::mapping: out of bounds indexing");
+ }
+ {
+ std::layout_left::template mapping<std::dextents<unsigned, 3>> m(std::dextents<int, 3>(5, 7, 9));
+ TEST_LIBCPP_ASSERT_FAILURE(m(-1, -1, -1), "layout_left::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(-1, 0, 0), "layout_left::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(0, -1, 0), "layout_left::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(0, 0, -1), "layout_left::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(5, 3, 3), "layout_left::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(3, 7, 3), "layout_left::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(3, 3, 9), "layout_left::mapping: out of bounds indexing");
+ TEST_LIBCPP_ASSERT_FAILURE(m(5, 7, 9), "layout_left::mapping: out of bounds indexing");
+ }
+ return 0;
+}
diff --git a/libcxx/test/std/containers/views/mdspan/layout_left/assert.stride.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_left/assert.stride.pass.cpp
new file mode 100644
index 00000000000000..ed558c598f4892
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_left/assert.stride.pass.cpp
@@ -0,0 +1,41 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+// XFAIL: availability-verbose_abort-missing
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1
+
+// <mdspan>
+
+// layout_left::mapping
+//
+// 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 "check_assertion.h"
+
+int main(int, char**) {
+ // value out of range
+ {
+ std::layout_left::template mapping<std::dextents<int, 3>> m{std::dextents<int, 3>{100, 100, 100}};
+
+ TEST_LIBCPP_ASSERT_FAILURE(m.stride(4), "invalid rank index");
+ }
+ return 0;
+}
diff --git a/libcxx/test/std/containers/views/mdspan/layout_left/comparison.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_left/comparison.pass.cpp
new file mode 100644
index 00000000000000..ab85ccf863963c
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_left/comparison.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>
+// friend constexpr bool operator==(const mapping& x, const mapping<OtherExtents>& y) noexcept;
+// `
+// Constraints: extents_type::rank() == OtherExtents::rank() is true.
+
+#include <mdspan>
+#include <type_traits>
+#include <concepts>
+#include <cassert>
+
+#include "test_macros.h"
+
+template <class To, class From>
+constexpr void test_comparison(bool equal, To dest_exts, From src_exts) {
+ std::layout_left::mapping<To> dest(dest_exts);
+ std::layout_left::mapping<From> src(src_exts);
+ ASSERT_NOEXCEPT(dest == src);
+ assert((dest == src) == equal);
+ assert((dest != src) == !equal);
+}
+
+struct X {
+ constexpr bool does_not_match() { return true; }
+};
+
+constexpr X compare_layout_mappings(...) { return {}; }
+
+template <class E1, class E2>
+constexpr auto compare_layout_mappings(E1 e1, E2 e2)
+ -> decltype(std::layout_left::mapping<E1>(e1) == std::layout_left::mapping<E2>(e2)) {
+ return true;
+}
+
+template <class T1, class T2>
+constexpr void test_comparison_
diff erent_rank() {
+ constexpr size_t D = std::dynamic_extent;
+
+ // sanity check same rank
+ static_assert(compare_layout_mappings(std::extents<T1, D>(5), std::extents<T2, D>(5)));
+ static_assert(compare_layout_mappings(std::extents<T1, 5>(), std::extents<T2, D>(5)));
+ static_assert(compare_layout_mappings(std::extents<T1, D>(5), std::extents<T2, 5>()));
+ static_assert(compare_layout_mappings(std::extents<T1, 5>(), std::extents<T2, 5>()));
+
+ // not equality comparable when rank is not the same
+ static_assert(compare_layout_mappings(std::extents<T1>(), std::extents<T2, D>(1)).does_not_match());
+ static_assert(compare_layout_mappings(std::extents<T1>(), std::extents<T2, 1>()).does_not_match());
+
+ static_assert(compare_layout_mappings(std::extents<T1, D>(1), std::extents<T2>()).does_not_match());
+ static_assert(compare_layout_mappings(std::extents<T1, 1>(), std::extents<T2>()).does_not_match());
+
+ static_assert(compare_layout_mappings(std::extents<T1, D>(5), std::extents<T2, D, D>(5, 5)).does_not_match());
+ static_assert(compare_layout_mappings(std::extents<T1, 5>(), std::extents<T2, 5, D>(5)).does_not_match());
+ static_assert(compare_layout_mappings(std::extents<T1, 5>(), std::extents<T2, 5, 1>()).does_not_match());
+
+ static_assert(compare_layout_mappings(std::extents<T1, D, D>(5, 5), std::extents<T2, D>(5)).does_not_match());
+ static_assert(compare_layout_mappings(std::extents<T1, 5, D>(5), std::extents<T2, D>(5)).does_not_match());
+ static_assert(compare_layout_mappings(std::extents<T1, 5, 5>(), std::extents<T2, 5>()).does_not_match());
+}
+
+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>());
+
+ test_comparison(true, std::extents<T1, D>(5), std::extents<T2, D>(5));
+ test_comparison(true, std::extents<T1, 5>(), std::extents<T2, D>(5));
+ test_comparison(true, std::extents<T1, D>(5), std::extents<T2, 5>());
+ test_comparison(true, std::extents<T1, 5>(), std::extents< T2, 5>());
+ test_comparison(false, std::extents<T1, D>(5), std::extents<T2, D>(7));
+ test_comparison(false, std::extents<T1, 5>(), std::extents<T2, D>(7));
+ test_comparison(false, std::extents<T1, D>(5), std::extents<T2, 7>());
+ test_comparison(false, std::extents<T1, 5>(), std::extents<T2, 7>());
+
+ 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));
+ test_comparison(true, std::extents<T1, D, 6, D, 8, D>(5, 7, 9), std::extents<T2, 5, D, D, 8, 9>(6, 7));
+ test_comparison(true, std::extents<T1, 5, 6, 7, 8, 9>(5, 6, 7, 8, 9), std::extents<T2, 5, 6, 7, 8, 9>());
+ 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));
+ test_comparison(false, std::extents<T1, D, 6, D, 8, D>(5, 7, 9), std::extents<T2, 5, D, D, 3, 9>(6, 7));
+ test_comparison(false, std::extents<T1, 5, 6, 7, 8, 9>(5, 6, 7, 8, 9), std::extents<T2, 5, 6, 7, 3, 9>());
+}
+
+template <class T1, class T2>
+constexpr void test_comparison() {
+ test_comparison_same_rank<T1, T2>();
+ test_comparison_
diff erent_rank<T1, T2>();
+}
+
+constexpr bool test() {
+ test_comparison<int, int>();
+ test_comparison<int, size_t>();
+ test_comparison<size_t, int>();
+ test_comparison<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_left/ctor.default.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_left/ctor.default.pass.cpp
new file mode 100644
index 00000000000000..ca478d047549ee
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_left/ctor.default.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
+
+// <mdspan>
+
+// Test default construction:
+//
+// constexpr mapping() noexcept = default;
+
+#include <mdspan>
+#include <cassert>
+#include <cstdint>
+
+#include "test_macros.h"
+
+template <class E>
+constexpr void test_construction() {
+ using M = std::layout_left::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);
+}
+
+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, 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_left/ctor.extents.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_left/ctor.extents.pass.cpp
new file mode 100644
index 00000000000000..5147539deed26b
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_left/ctor.extents.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 mapping(const extents_type&) noexcept;
+//
+// Preconditions: The size of the multidimensional index space e is representable
+// as a value of type index_type ([basic.fundamental]).
+//
+// Effects: Direct-non-list-initializes extents_ with e.
+
+#include <mdspan>
+#include <cassert>
+#include <cstdint>
+
+#include "test_macros.h"
+
+template <class E>
+constexpr void test_construction(E e) {
+ using M = std::layout_left::mapping<E>;
+ ASSERT_NOEXCEPT(M{e});
+ M m(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);
+}
+
+constexpr bool test() {
+ constexpr size_t D = std::dynamic_extent;
+ test_construction(std::extents<int>());
+ test_construction(std::extents<unsigned, D>(7));
+ test_construction(std::extents<unsigned, 7>());
+ test_construction(std::extents<unsigned, 7, 8>());
+ test_construction(std::extents<int64_t, D, 8, D, D>(7, 9, 10));
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/containers/views/mdspan/layout_left/ctor.layout_right.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_left/ctor.layout_right.pass.cpp
new file mode 100644
index 00000000000000..7e686cd9688f07
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_left/ctor.layout_right.pass.cpp
@@ -0,0 +1,128 @@
+//===----------------------------------------------------------------------===//
+//
+// 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(!is_convertible_v<OtherExtents, extents_type>)
+// mapping(const layout_right::mapping<OtherExtents>&) noexcept;
+
+// Constraints:
+// - extents_type::rank() <= 1 is true, and
+// - is_constructible_v<extents_type, OtherExtents> is true.
+//
+// Preconditions: other.required_span_size() is representable as a value of type index_type
+
+#include <mdspan>
+#include <type_traits>
+#include <cassert>
+#include <limits>
+
+#include "test_macros.h"
+
+template <class To, class From>
+constexpr void test_implicit_conversion(To dest, From src) {
+ assert(dest.extents() == src.extents());
+}
+
+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_right::mapping<FromE>;
+ From src(src_exts);
+
+ ASSERT_NOEXCEPT(To(src));
+ To dest(src);
+
+ assert(dest.extents() == src.extents());
+ if constexpr (implicit) {
+ dest = src;
+ assert(dest.extents() == src.extents());
+ test_implicit_conversion<To, From>(src, src);
+ }
+}
+
+template <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());
+
+ // clang-format off
+ test_conversion<idx_convertible && true, std::extents<T1>>(std::extents<T2>());
+ test_conversion<idx_convertible && true, std::extents<T1, D>>(std::extents<T2, D>(5));
+ test_conversion<idx_convertible && false, std::extents<T1, 5>>(std::extents<T2, D>(5));
+ test_conversion<idx_convertible && true, std::extents<T1, 5>>(std::extents<T2, 5>());
+ // 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 ll_mapping_t = typename std::layout_left::template mapping<std::extents<IdxT, Extents...>>;
+
+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<ll_mapping_t<int, D>, lr_mapping_t<int, 5>>);
+ static_assert(std::is_convertible_v<lr_mapping_t<int, 5>, ll_mapping_t<int, D>>);
+
+ // Check that dynamic to static conversion only works explicitly
+ static_assert(std::is_constructible_v<ll_mapping_t<int, 5>, lr_mapping_t<int, D>>);
+ static_assert(!std::is_convertible_v<lr_mapping_t<int, D>, ll_mapping_t<int, 5>>);
+
+ // Sanity check that smaller index_type to larger index_type conversion works
+ static_assert(std::is_constructible_v<ll_mapping_t<size_t, 5>, lr_mapping_t<int, 5>>);
+ static_assert(std::is_convertible_v<lr_mapping_t<int, 5>, ll_mapping_t<size_t, 5>>);
+
+ // Check that larger index_type to smaller index_type conversion works explicitly only
+ static_assert(std::is_constructible_v<ll_mapping_t<int, 5>, lr_mapping_t<size_t, 5>>);
+ static_assert(!std::is_convertible_v<lr_mapping_t<size_t, 5>, ll_mapping_t<int, 5>>);
+}
+
+constexpr void test_rank_mismatch() {
+ constexpr size_t D = std::dynamic_extent;
+
+ static_assert(!std::is_constructible_v<lr_mapping_t<int, D>, lr_mapping_t<int>>);
+ static_assert(!std::is_constructible_v<lr_mapping_t<int>, lr_mapping_t<int, D, D>>);
+ static_assert(!std::is_constructible_v<lr_mapping_t<int, D>, lr_mapping_t<int, D, D>>);
+ static_assert(!std::is_constructible_v<lr_mapping_t<int, D, D, D>, lr_mapping_t<int, D, D>>);
+}
+
+constexpr void test_static_extent_mismatch() {
+ static_assert(!std::is_constructible_v<ll_mapping_t<int, 5>, lr_mapping_t<int, 4>>);
+}
+
+constexpr void test_rank_greater_one() {
+ constexpr size_t D = std::dynamic_extent;
+
+ static_assert(!std::is_constructible_v<ll_mapping_t<int, D, D>, lr_mapping_t<int, D, D>>);
+ static_assert(!std::is_constructible_v<ll_mapping_t<int, 1, 1>, lr_mapping_t<int, 1, 1>>);
+ static_assert(!std::is_constructible_v<ll_mapping_t<int, D, D, D>, lr_mapping_t<int, D, D, D>>);
+}
+
+constexpr bool test() {
+ test_conversion<int, int>();
+ test_conversion<int, size_t>();
+ test_conversion<size_t, int>();
+ test_conversion<size_t, long>();
+ test_no_implicit_conversion();
+ test_rank_mismatch();
+ test_static_extent_mismatch();
+ test_rank_greater_one();
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/containers/views/mdspan/layout_left/ctor.mapping.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_left/ctor.mapping.pass.cpp
new file mode 100644
index 00000000000000..40666ae960f462
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_left/ctor.mapping.pass.cpp
@@ -0,0 +1,136 @@
+//===----------------------------------------------------------------------===//
+//
+// 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(!is_convertible_v<OtherExtents, extents_type>)
+// mapping(const mapping<OtherExtents>&) noexcept;
+
+// Constraints: is_constructible_v<extents_type, OtherExtents> is true.
+//
+// Preconditions: other.required_span_size() is representable as a value of type index_type
+
+#include <mdspan>
+#include <type_traits>
+#include <cassert>
+#include <limits>
+
+#include "test_macros.h"
+
+template <class To, class From>
+constexpr void test_implicit_conversion(To dest, From src) {
+ assert(dest == src);
+}
+
+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_left::mapping<FromE>;
+ From src(src_exts);
+
+ ASSERT_NOEXCEPT(To(src));
+ To dest(src);
+
+ assert(dest == src);
+ if constexpr (implicit) {
+ dest = src;
+ assert(dest == src);
+ test_implicit_conversion<To, From>(src, src);
+ }
+}
+
+template <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());
+
+ // clang-format off
+ test_conversion<idx_convertible && true, std::extents<T1>>(std::extents<T2>());
+ test_conversion<idx_convertible && true, std::extents<T1, D>>(std::extents<T2, D>(5));
+ test_conversion<idx_convertible && false, std::extents<T1, 5>>(std::extents<T2, D>(5));
+ test_conversion<idx_convertible && true, std::extents<T1, 5>>(std::extents<T2, 5>());
+ test_conversion<idx_convertible && false, std::extents<T1, 5, D>>(std::extents<T2, D, D>(5, 5));
+ test_conversion<idx_convertible && true, std::extents<T1, D, D>>(std::extents<T2, D, D>(5, 5));
+ test_conversion<idx_convertible && true, std::extents<T1, D, D>>(std::extents<T2, D, 7>(5));
+ test_conversion<idx_convertible && true, std::extents<T1, 5, 7>>(std::extents<T2, 5, 7>());
+ test_conversion<idx_convertible && false, std::extents<T1, 5, D, 8, D, D>>(std::extents<T2, D, D, 8, 9, 1>(5, 7));
+ test_conversion<idx_convertible && true, std::extents<T1, D, D, D, D, D>>(
+ std::extents<T2, D, D, D, D, D>(5, 7, 8, 9, 1));
+ test_conversion<idx_convertible && true, std::extents<T1, D, D, 8, 9, D>>(std::extents<T2, D, 7, 8, 9, 1>(5));
+ test_conversion<idx_convertible && true, 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 mapping_t = typename std::layout_left::template mapping<std::extents<IdxT, Extents...>>;
+
+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<mapping_t<int, D>, mapping_t<int, 5>>);
+ static_assert(std::is_convertible_v<mapping_t<int, 5>, mapping_t<int, D>>);
+
+ // Check that dynamic to static conversion only works explicitly
+ static_assert(std::is_constructible_v<mapping_t<int, 5>, mapping_t<int, D>>);
+ static_assert(!std::is_convertible_v<mapping_t<int, D>, mapping_t<int, 5>>);
+
+ // Sanity check that one static to dynamic conversion works
+ static_assert(std::is_constructible_v<mapping_t<int, D, 7>, mapping_t<int, 5, 7>>);
+ static_assert(std::is_convertible_v<mapping_t<int, 5, 7>, mapping_t<int, D, 7>>);
+
+ // Check that dynamic to static conversion only works explicitly
+ static_assert(std::is_constructible_v<mapping_t<int, 5, 7>, mapping_t<int, D, 7>>);
+ static_assert(!std::is_convertible_v<mapping_t<int, D, 7>, mapping_t<int, 5, 7>>);
+
+ // Sanity check that smaller index_type to larger index_type conversion works
+ static_assert(std::is_constructible_v<mapping_t<size_t, 5>, mapping_t<int, 5>>);
+ static_assert(std::is_convertible_v<mapping_t<int, 5>, mapping_t<size_t, 5>>);
+
+ // Check that larger index_type to smaller index_type conversion works explicitly only
+ static_assert(std::is_constructible_v<mapping_t<int, 5>, mapping_t<size_t, 5>>);
+ static_assert(!std::is_convertible_v<mapping_t<size_t, 5>, mapping_t<int, 5>>);
+}
+
+constexpr void test_rank_mismatch() {
+ constexpr size_t D = std::dynamic_extent;
+
+ static_assert(!std::is_constructible_v<mapping_t<int, D>, mapping_t<int>>);
+ static_assert(!std::is_constructible_v<mapping_t<int>, mapping_t<int, D, D>>);
+ static_assert(!std::is_constructible_v<mapping_t<int, D>, mapping_t<int, D, D>>);
+ static_assert(!std::is_constructible_v<mapping_t<int, D, D, D>, mapping_t<int, D, D>>);
+}
+
+constexpr void test_static_extent_mismatch() {
+ constexpr size_t D = std::dynamic_extent;
+
+ static_assert(!std::is_constructible_v<mapping_t<int, D, 5>, mapping_t<int, D, 4>>);
+ static_assert(!std::is_constructible_v<mapping_t<int, 5>, mapping_t<int, 4>>);
+ static_assert(!std::is_constructible_v<mapping_t<int, 5, D>, 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_no_implicit_conversion();
+ 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_left/extents.verify.cpp b/libcxx/test/std/containers/views/mdspan/layout_left/extents.verify.cpp
new file mode 100644
index 00000000000000..01e5042b64e04d
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_left/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_left::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_left::mapping template argument must be a specialization of extents}}
+ [[maybe_unused]] std::layout_left::mapping<void> mapping;
+}
+
+void representable() {
+ // expected-error-re@*:* {{{{(static_assert|static assertion)}} failed {{.*}}layout_left::mapping product of static extents must be representable as index_type.}}
+ [[maybe_unused]] std::layout_left::mapping<std::extents<char, 20, 20>> mapping;
+}
diff --git a/libcxx/test/std/containers/views/mdspan/layout_left/index_operator.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_left/index_operator.pass.cpp
new file mode 100644
index 00000000000000..5ca6f65e510d30
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_left/index_operator.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>
+
+// 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 T, class... Args>
+constexpr void iterate_left(M m, T& count, 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...));
+ assert(count == m(args...));
+ count++;
+ } else {
+ for (typename M::index_type i = 0; i < m.extents().extent(r); i++) {
+ iterate_left(m, count, i, args...);
+ }
+ }
+}
+
+template <class E, class... Args>
+constexpr void test_iteration(Args... args) {
+ using M = std::layout_left::mapping<E>;
+ M m(E(args...));
+
+ typename E::index_type count = 0;
+ iterate_left(m, count);
+}
+
+constexpr bool test() {
+ constexpr size_t D = std::dynamic_extent;
+ test_iteration<std::extents<int>>();
+ test_iteration<std::extents<unsigned, D>>(1);
+ test_iteration<std::extents<unsigned, D>>(7);
+ test_iteration<std::extents<unsigned, 7>>();
+ test_iteration<std::extents<unsigned, 7, 8>>();
+ test_iteration<std::extents<char, D, D, D, D>>(1, 1, 1, 1);
+
+ // Check operator constraint for number of arguments
+ static_assert(check_operator_constraints(std::layout_left::mapping<std::extents<int, D>>(std::extents<int, D>(1)), 0));
+ static_assert(!check_operator_constraints(std::layout_left::mapping<std::extents<int, D>>(std::extents<int, D>(1)), 0, 0));
+
+ // Check operator constraint for convertibility of arguments to index_type
+ static_assert(check_operator_constraints(std::layout_left::mapping<std::extents<int, D>>(std::extents<int, D>(1)), IntType(0)));
+ static_assert(!check_operator_constraints(std::layout_left::mapping<std::extents<unsigned, D>>(std::extents<unsigned, D>(1)), IntType(0)));
+
+ // Check operator constraint for no-throw-constructibility of index_type from arguments
+ static_assert(!check_operator_constraints(std::layout_left::mapping<std::extents<unsigned char, D>>(std::extents<unsigned char, D>(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>>(7, 9, 10);
+ test_iteration<std::extents<int64_t, D, 8, 1, D>>(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_left/properties.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_left/properties.pass.cpp
new file mode 100644
index 00000000000000..85a362fc41d258
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_left/properties.pass.cpp
@@ -0,0 +1,66 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_left::mapping {
+//
+// ...
+// 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 true; }
+//
+// static constexpr bool is_unique() noexcept { return true; }
+// static constexpr bool is_exhaustive() noexcept { return true; }
+// static constexpr bool is_strided() noexcept { return true; }
+// ...
+// };
+// }
+
+#include <mdspan>
+#include <type_traits>
+#include <concepts>
+#include <cassert>
+
+#include "test_macros.h"
+
+template <class E>
+constexpr void test_layout_mapping_left() {
+ using M = std::layout_left::template mapping<E>;
+ assert(M::is_unique() == true);
+ assert(M::is_exhaustive() == true);
+ assert(M::is_strided() == true);
+ assert(M::is_always_unique() == true);
+ assert(M::is_always_exhaustive() == true);
+ assert(M::is_always_strided() == true);
+ ASSERT_NOEXCEPT(std::declval<M>().is_unique());
+ ASSERT_NOEXCEPT(std::declval<M>().is_exhaustive());
+ ASSERT_NOEXCEPT(std::declval<M>().is_strided());
+ ASSERT_NOEXCEPT(M::is_always_unique());
+ ASSERT_NOEXCEPT(M::is_always_exhaustive());
+ ASSERT_NOEXCEPT(M::is_always_strided());
+}
+
+constexpr bool test() {
+ constexpr size_t D = std::dynamic_extent;
+ test_layout_mapping_left<std::extents<int>>();
+ test_layout_mapping_left<std::extents<char, 4, 5>>();
+ test_layout_mapping_left<std::extents<unsigned, D, 4>>();
+ test_layout_mapping_left<std::extents<size_t, D, D, D, D>>();
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/containers/views/mdspan/layout_left/required_span_size.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_left/required_span_size.pass.cpp
new file mode 100644
index 00000000000000..d0ee090099cf26
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_left/required_span_size.pass.cpp
@@ -0,0 +1,51 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 required_span_size() const noexcept;
+//
+// Returns: extents().fwd-prod-of-extents(extents_type::rank()).
+
+
+#include <mdspan>
+#include <cassert>
+#include <cstdint>
+
+#include "test_macros.h"
+
+template <class E>
+constexpr void test_required_span_size(E e, typename E::index_type expected_size) {
+ using M = std::layout_left::mapping<E>;
+ const M m(e);
+
+ 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>(), 1);
+ test_required_span_size(std::extents<unsigned, D>(0), 0);
+ test_required_span_size(std::extents<unsigned, D>(1), 1);
+ test_required_span_size(std::extents<unsigned, D>(7), 7);
+ test_required_span_size(std::extents<unsigned, 7>(), 7);
+ test_required_span_size(std::extents<unsigned, 7, 8>(), 56);
+ test_required_span_size(std::extents<int64_t, D, 8, D, D>(7, 9, 10), 5040);
+ test_required_span_size(std::extents<int64_t, 1, 8, D, D>(9, 10), 720);
+ test_required_span_size(std::extents<int64_t, 1, 0, D, D>(9, 10), 0);
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
diff --git a/libcxx/test/std/containers/views/mdspan/layout_left/static_requirements.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_left/static_requirements.pass.cpp
new file mode 100644
index 00000000000000..b74d129946d40d
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_left/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_left);
+ 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>()(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>);
+ if constexpr (E::rank() > 0)
+ 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_left() {
+ test_layout_mapping_requirements<std::layout_left, E>();
+}
+
+int main(int, char**) {
+ constexpr size_t D = std::dynamic_extent;
+ test_layout_mapping_left<std::extents<int>>();
+ test_layout_mapping_left<std::extents<char, 4, 5>>();
+ test_layout_mapping_left<std::extents<unsigned, D, 4>>();
+ test_layout_mapping_left<std::extents<size_t, D, D, D, D>>();
+ return 0;
+}
diff --git a/libcxx/test/std/containers/views/mdspan/layout_left/stride.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_left/stride.pass.cpp
new file mode 100644
index 00000000000000..c10da3a0002afc
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_left/stride.pass.cpp
@@ -0,0 +1,51 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_left::mapping<E>;
+ M m(E(args...));
+
+ ASSERT_NOEXCEPT(m.stride(0));
+ for (size_t r = 0; r < E::rank(); r++)
+ assert(strides[r] == m.stride(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/layout_right/assert.conversion.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_right/assert.conversion.pass.cpp
index 9fa3e57671cbfb..2aa4f139374845 100644
--- a/libcxx/test/std/containers/views/mdspan/layout_right/assert.conversion.pass.cpp
+++ b/libcxx/test/std/containers/views/mdspan/layout_right/assert.conversion.pass.cpp
@@ -40,7 +40,13 @@ int main(int, char**) {
TEST_LIBCPP_ASSERT_FAILURE(([=] { std::layout_right::mapping<std::extents<int, D, 3>> m(arg); }()),
"extents construction: mismatch of provided arguments with static extents.");
}
- // value out of range
+ // non-representability of extents itself
+ {
+ TEST_LIBCPP_ASSERT_FAILURE(([=] { std::layout_right::mapping<std::extents<char, D>> m(
+ std::layout_right::mapping<std::extents<int, D>>(std::extents<int, D>(500))); }()),
+ "extents ctor: arguments must be representable as index_type and nonnegative");
+ }
+ // required_span_size not representable, while individual extents are
{
// check extents would be constructible
[[maybe_unused]] std::extents<char, D, 5> e(arg_exts);
diff --git a/libcxx/test/std/containers/views/mdspan/layout_right/assert.ctor.layout_left.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_right/assert.ctor.layout_left.pass.cpp
new file mode 100644
index 00000000000000..941ae6fc21a4e0
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_right/assert.ctor.layout_left.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: has-unix-headers
+// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
+// XFAIL: availability-verbose_abort-missing
+// ADDITIONAL_COMPILE_FLAGS: -D_LIBCPP_ENABLE_ASSERTIONS=1
+
+// <mdspan>
+
+// template<class OtherExtents>
+// constexpr explicit(!is_convertible_v<OtherExtents, extents_type>)
+// mapping(const layout_left::mapping<OtherExtents>&) noexcept;
+
+// Constraints:
+// - extents_type::rank() <= 1 is true, and
+// - is_constructible_v<extents_type, OtherExtents> is true.
+//
+// Preconditions: other.required_span_size() is representable as a value of type index_type
+
+#include <mdspan>
+#include <cassert>
+
+#include "check_assertion.h"
+
+int main(int, char**) {
+ constexpr size_t D = std::dynamic_extent;
+ std::extents<int, D> arg_exts{5};
+ std::layout_left::mapping<std::extents<int, D>> arg(arg_exts);
+
+ // working case
+ {
+ [[maybe_unused]] std::layout_right::mapping<std::extents<size_t, 5>> m(arg); // should work
+ }
+ // mismatch of static extent
+ {
+ 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
+ {
+ TEST_LIBCPP_ASSERT_FAILURE(([=] { std::layout_right::mapping<std::extents<char, D>> m(
+ std::layout_left::mapping<std::extents<int, D>>(std::extents<int, D>(500))); }()),
+ "extents ctor: arguments must be representable as index_type and nonnegative");
+ }
+
+ // Can't trigger required_span_size() representability assertion, since for rank-1 the above check will trigger first,
+ // and this conversion constructor is constrained on rank <= 1.
+ return 0;
+}
diff --git a/libcxx/test/std/containers/views/mdspan/layout_right/ctor.layout_left.pass.cpp b/libcxx/test/std/containers/views/mdspan/layout_right/ctor.layout_left.pass.cpp
new file mode 100644
index 00000000000000..5ab8d05926c6c2
--- /dev/null
+++ b/libcxx/test/std/containers/views/mdspan/layout_right/ctor.layout_left.pass.cpp
@@ -0,0 +1,128 @@
+//===----------------------------------------------------------------------===//
+//
+// 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(!is_convertible_v<OtherExtents, extents_type>)
+// mapping(const layout_left::mapping<OtherExtents>&) noexcept;
+
+// Constraints:
+// - extents_type::rank() <= 1 is true, and
+// - is_constructible_v<extents_type, OtherExtents> is true.
+//
+// Preconditions: other.required_span_size() is representable as a value of type index_type
+
+#include <mdspan>
+#include <type_traits>
+#include <cassert>
+#include <limits>
+
+#include "test_macros.h"
+
+template <class To, class From>
+constexpr void test_implicit_conversion(To dest, From src) {
+ assert(dest.extents() == src.extents());
+}
+
+template <bool implicit, class ToE, class FromE>
+constexpr void test_conversion(FromE src_exts) {
+ using To = std::layout_right::mapping<ToE>;
+ using From = std::layout_left::mapping<FromE>;
+ From src(src_exts);
+
+ ASSERT_NOEXCEPT(To(src));
+ To dest(src);
+
+ assert(dest.extents() == src.extents());
+ if constexpr (implicit) {
+ dest = src;
+ assert(dest.extents() == src.extents());
+ test_implicit_conversion<To, From>(src, src);
+ }
+}
+
+template <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());
+
+ // clang-format off
+ test_conversion<idx_convertible && true, std::extents<T1>>(std::extents<T2>());
+ test_conversion<idx_convertible && true, std::extents<T1, D>>(std::extents<T2, D>(5));
+ test_conversion<idx_convertible && false, std::extents<T1, 5>>(std::extents<T2, D>(5));
+ test_conversion<idx_convertible && true, std::extents<T1, 5>>(std::extents<T2, 5>());
+ // 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 ll_mapping_t = typename std::layout_left::template mapping<std::extents<IdxT, Extents...>>;
+
+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<lr_mapping_t<int, D>, ll_mapping_t<int, 5>>);
+ static_assert(std::is_convertible_v<ll_mapping_t<int, 5>, lr_mapping_t<int, D>>);
+
+ // Check that dynamic to static conversion only works explicitly
+ static_assert(std::is_constructible_v<lr_mapping_t<int, 5>, ll_mapping_t<int, D>>);
+ static_assert(!std::is_convertible_v<ll_mapping_t<int, D>, lr_mapping_t<int, 5>>);
+
+ // Sanity check that smaller index_type to larger index_type conversion works
+ static_assert(std::is_constructible_v<lr_mapping_t<size_t, 5>, ll_mapping_t<int, 5>>);
+ static_assert(std::is_convertible_v<ll_mapping_t<int, 5>, lr_mapping_t<size_t, 5>>);
+
+ // Check that larger index_type to smaller index_type conversion works explicitly only
+ static_assert(std::is_constructible_v<lr_mapping_t<int, 5>, ll_mapping_t<size_t, 5>>);
+ static_assert(!std::is_convertible_v<ll_mapping_t<size_t, 5>, lr_mapping_t<int, 5>>);
+}
+
+constexpr void test_rank_mismatch() {
+ constexpr size_t D = std::dynamic_extent;
+
+ static_assert(!std::is_constructible_v<lr_mapping_t<int, D>, ll_mapping_t<int>>);
+ static_assert(!std::is_constructible_v<lr_mapping_t<int>, ll_mapping_t<int, D, D>>);
+ static_assert(!std::is_constructible_v<lr_mapping_t<int, D>, ll_mapping_t<int, D, D>>);
+ static_assert(!std::is_constructible_v<lr_mapping_t<int, D, D, D>, ll_mapping_t<int, D, D>>);
+}
+
+constexpr void test_static_extent_mismatch() {
+ static_assert(!std::is_constructible_v<lr_mapping_t<int, 5>, ll_mapping_t<int, 4>>);
+}
+
+constexpr void test_rank_greater_one() {
+ constexpr size_t D = std::dynamic_extent;
+
+ static_assert(!std::is_constructible_v<lr_mapping_t<int, D, D>, ll_mapping_t<int, D, D>>);
+ static_assert(!std::is_constructible_v<lr_mapping_t<int, 1, 1>, ll_mapping_t<int, 1, 1>>);
+ static_assert(!std::is_constructible_v<lr_mapping_t<int, D, D, D>, ll_mapping_t<int, D, D, D>>);
+}
+
+constexpr bool test() {
+ test_conversion<int, int>();
+ test_conversion<int, size_t>();
+ test_conversion<size_t, int>();
+ test_conversion<size_t, long>();
+ test_no_implicit_conversion();
+ test_rank_mismatch();
+ test_static_extent_mismatch();
+ test_rank_greater_one();
+ return true;
+}
+
+int main(int, char**) {
+ test();
+ static_assert(test());
+ return 0;
+}
More information about the libcxx-commits
mailing list